diff --git a/CHANGELOG.md b/CHANGELOG.md index 408805e5..00cc224d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,151 @@ +## [0.29.4](https://github.com/blinko-space/blinko/compare/v0.29.3...v0.29.4) (2024-12-21) + + +### Bug Fixes + +* update LoadingPage and SendButton components for improved UI ([f6c3c54](https://github.com/blinko-space/blinko/commit/f6c3c54f4b9a4120cfc143f787a7cdc4b91beb7c)) + +## [0.29.3](https://github.com/blinko-space/blinko/compare/v0.29.2...v0.29.3) (2024-12-21) + + +### Bug Fixes + +* enhance ScrollArea component and update attachment router ([2b34bfa](https://github.com/blinko-space/blinko/commit/2b34bfaa9c5f9813198670b7355392e2c02f1f4e)) + +## [0.29.2](https://github.com/blinko-space/blinko/compare/v0.29.1...v0.29.2) (2024-12-21) + + +### Bug Fixes + +* enhance resource management and UI updates ([5248a03](https://github.com/blinko-space/blinko/commit/5248a03efbd39ea9636a5327a1b16da8289d3c91)) + +## [0.29.1](https://github.com/blinko-space/blinko/compare/v0.29.0...v0.29.1) (2024-12-21) + + +### Bug Fixes + +* add error handling for missing account information in DBJob plugin ([fd9bf88](https://github.com/blinko-space/blinko/commit/fd9bf8850574c5f998050d31a9956bda1ef713a1)) + +# [0.29.0](https://github.com/blinko-space/blinko/compare/v0.28.1...v0.29.0) (2024-12-21) + + +### Bug Fixes + +* update Chinese translations and enhance BlinkoMusicPlayer responsiveness ([838f26e](https://github.com/blinko-space/blinko/commit/838f26e6bba1b3c121ddc24093422d886b84c18c)) + + +### Features + +* add music metadata functionality and Spotify integration ([800cc73](https://github.com/blinko-space/blinko/commit/800cc73cee9f5c03b603975e2804964405409eef)) + +## [0.28.1](https://github.com/blinko-space/blinko/compare/v0.28.0...v0.28.1) (2024-12-21) + + +### Bug Fixes + +* build issue ([169e3b3](https://github.com/blinko-space/blinko/commit/169e3b301fec927b505ad8bb46d33c6f83ec906d)) + +# [0.28.0](https://github.com/blinko-space/blinko/compare/v0.27.7...v0.28.0) (2024-12-20) + + +### Bug Fixes + +* improve layout and accessibility in resources and layout components ([2bfb29c](https://github.com/blinko-space/blinko/commit/2bfb29c841c0c0b181f15c013b44dfb78c380c44)) + + +### Features + +* enhance configuration and improve attachment handling ([6572ebb](https://github.com/blinko-space/blinko/commit/6572ebb0e7c5255e51943eebc36bee96a16623d8)) +* enhance localization and improve resource context menu ([99003be](https://github.com/blinko-space/blinko/commit/99003be617eb533f4ac8d769018d329d6755e0e2)) +* refactor resource page ([cda450a](https://github.com/blinko-space/blinko/commit/cda450a89a96aca417ac2a98ebec69639189c233)) + +## [0.27.7](https://github.com/blinko-space/blinko/compare/v0.27.6...v0.27.7) (2024-12-20) + + +### Bug Fixes + +* german translations ([d498892](https://github.com/blinko-space/blinko/commit/d4988929592998df4bd79d9e60eacd0e21093cde)) + +## [0.27.6](https://github.com/blinko-space/blinko/compare/v0.27.5...v0.27.6) (2024-12-19) + + +### Bug Fixes + +* remove unused components and utility functions ([69b475b](https://github.com/blinko-space/blinko/commit/69b475b4c9d2754b5d4e1bc409dc85b761f5c307)) + +## [0.27.5](https://github.com/blinko-space/blinko/compare/v0.27.4...v0.27.5) (2024-12-19) + + +### Bug Fixes + +* add AI writing feature and enhance localization ([dbaa73e](https://github.com/blinko-space/blinko/commit/dbaa73ef615f8f387db573417cf9c04465eeaeb6)) + +## [0.27.4](https://github.com/blinko-space/blinko/compare/v0.27.3...v0.27.4) (2024-12-19) + + +### Bug Fixes + +* add edit time functionality and enhance note management [#242](https://github.com/blinko-space/blinko/issues/242) ([c112df6](https://github.com/blinko-space/blinko/commit/c112df6a149a2b31f3e9ff8273136585db763981)) +* add edit time option to BlinkoRightClickMenu ([57620e2](https://github.com/blinko-space/blinko/commit/57620e2b1fa5a21e4245ea4c2636822752351089)) + +## [0.27.3](https://github.com/blinko-space/blinko/compare/v0.27.2...v0.27.3) (2024-12-19) + + +### Bug Fixes + +* enhance user settings initialization and layout interaction ([251f2ff](https://github.com/blinko-space/blinko/commit/251f2ffd2a9e4b6c13235539932f1a77281374b9)) + +## [0.27.2](https://github.com/blinko-space/blinko/compare/v0.27.1...v0.27.2) (2024-12-18) + + +### Bug Fixes + +* disable dragging for image thumbnails in AttachmentRender component [#331](https://github.com/blinko-space/blinko/issues/331) ([b2edb76](https://github.com/blinko-space/blinko/commit/b2edb7620770c4a2315f272df16ed83a4ceba85a)) +* enhance attachment management and sorting functionality ([5b69f2b](https://github.com/blinko-space/blinko/commit/5b69f2b99f45c69165034c5d8132b9cea5656839)) +* enhance layout components and improve user interaction ([9de5e8e](https://github.com/blinko-space/blinko/commit/9de5e8e8786d1fec3e6781467af9bb4a565333e4)) +* improve BlinkoAiChat responsiveness and enhance file name handling ([91229c6](https://github.com/blinko-space/blinko/commit/91229c6addd1b4ca25547fe831a675f5b188642c)) +* pwa refresh language lose ([dc41a44](https://github.com/blinko-space/blinko/commit/dc41a44883e5f4a0913c7b956fc911baa7351415)) +* refactor sidebar state management and enhance resizing functionality ([6194067](https://github.com/blinko-space/blinko/commit/6194067d5de9f8ed95a2b850b747448d188ea539)) +* update dialog store reference in DeleteIcon component [#332](https://github.com/blinko-space/blinko/issues/332) ([4183c9f](https://github.com/blinko-space/blinko/commit/4183c9f964c6e851a0c2331c4e78d79eea4421e8)) + +## [0.27.1](https://github.com/blinko-space/blinko/compare/v0.27.0...v0.27.1) (2024-12-17) + + +### Bug Fixes + +* adjust button alignment in TipsDialog component ([cf7b057](https://github.com/blinko-space/blinko/commit/cf7b05752a9221291d4f96bf37afffcd02b38015)) +* enhance file upload functionality and improve user feedback ([c86cd4e](https://github.com/blinko-space/blinko/commit/c86cd4e61c66cc5e419510c92ba7fac3ec40df5e)) +* enhance user sign-out process and improve component structure 326 ([e38501a](https://github.com/blinko-space/blinko/commit/e38501a071ea9c5b952e6cfe6354cf7e170effea)) +* remove bodyParser configuration from file upload route ([4047c64](https://github.com/blinko-space/blinko/commit/4047c64f68c41bb95b057e7a5a0ff1d8072c7808)) +* update icon paths in manifest.json for consistency ([66f1cbe](https://github.com/blinko-space/blinko/commit/66f1cbe38146e2d21de7d828841f3e310cc20dec)) + +# [0.27.0](https://github.com/blinko-space/blinko/compare/v0.26.12...v0.27.0) (2024-12-17) + + +### Bug Fixes + +* enhance sign-in component with theme-based logo and clean up imports ([f9d500c](https://github.com/blinko-space/blinko/commit/f9d500ce40ebd8a6d2608945e3dbfe8d123c89b6)) + + +### Features + +* add self-deletion prevention in user deletion logic ([60c4a41](https://github.com/blinko-space/blinko/commit/60c4a418164f0cc8e2eae32edad6a667f740a6df)) +* add user data deletion confirmation dialog and enhance translation files ([ef09306](https://github.com/blinko-space/blinko/commit/ef0930618aefbefa461fd98f9d23095d39e3ae34)) + +## [0.26.12](https://github.com/blinko-space/blinko/compare/v0.26.11...v0.26.12) (2024-12-17) + + +### Bug Fixes + +* add error logging in FileService writeFileSafe method ([17604c2](https://github.com/blinko-space/blinko/commit/17604c28f08e0e3f4ab426e074bbd8c812d6375f)) + +## [0.26.11](https://github.com/blinko-space/blinko/compare/v0.26.10...v0.26.11) (2024-12-17) + + +### Bug Fixes + +* update logo assets and caching headers ([da931de](https://github.com/blinko-space/blinko/commit/da931debcc34f0bf50ed86934c6fa1c5ab0b5f7d)) + ## [0.26.10](https://github.com/blinko-space/blinko/compare/v0.26.9...v0.26.10) (2024-12-16) diff --git a/README.md b/README.md index ea14bcd7..870dfa0d 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ -Blinko +Blinko # Blinko - Open Source, Self-hosted diff --git a/app/api/file/[...filename]/route.ts b/app/api/file/[...filename]/route.ts index 1c98b14b..ece1552a 100644 --- a/app/api/file/[...filename]/route.ts +++ b/app/api/file/[...filename]/route.ts @@ -47,7 +47,7 @@ export const GET = async (req: NextRequest, { params }: any) => { const sanitizedPath = fullPath.replace(/^[./\\]+/, ''); - const filePath = path.join(process.cwd(), UPLOAD_FILE_PATH, sanitizedPath); + const filePath = path.join(UPLOAD_FILE_PATH, sanitizedPath); try { if (isImage(fullPath) && needThumbnail) { diff --git a/app/api/file/upload-by-url/route.ts b/app/api/file/upload-by-url/route.ts index 6f76e2c4..c7a8e7e3 100644 --- a/app/api/file/upload-by-url/route.ts +++ b/app/api/file/upload-by-url/route.ts @@ -32,7 +32,7 @@ export const POST = async (req: NextRequest, res: NextResponse) => { const urlPath = new URL(url).pathname; const originalName = path.basename(urlPath).replaceAll(" ", "_"); const extension = path.extname(originalName); - + console.log({ originalName, extension }) const filePath = await FileService.uploadFile(buffer, originalName); return NextResponse.json({ diff --git a/app/api/file/upload/route.ts b/app/api/file/upload/route.ts index 509910d0..7c2240c1 100644 --- a/app/api/file/upload/route.ts +++ b/app/api/file/upload/route.ts @@ -8,20 +8,35 @@ export const POST = async (req: NextRequest, res: NextResponse) => { if (!token) { return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); } - const formData = await req.formData(); - const file = formData.getAll('file')[0] - if (!file) { - return NextResponse.json({ error: "No files received." }, { status: 400 }); - } - if (process.env.IS_DEMO) { - return NextResponse.json({ error: "In Demo App" }, { status: 401 }); + + // if (process.env.IS_DEMO) { + // return NextResponse.json({ error: "In Demo App" }, { status: 401 }); + // } + + try { + const formData = await req.formData(); + const file = formData.getAll('file')[0] as File; + + if (!file) { + return NextResponse.json({ error: "No files received." }, { status: 400 }); + } + + const originalName = file.name.replaceAll(" ", "_"); + const stream = file.stream(); + + const filePath = await FileService.uploadFileStream(stream, originalName, file.size); + + return NextResponse.json({ + Message: "Success", + status: 200, + ...filePath, + type: file.type, + size: file.size + }); + + } catch (error) { + console.error('Upload error:', error); + return NextResponse.json({ error: "Upload failed" }, { status: 500 }); } - //@ts-ignore - const buffer = Buffer.from(await file.arrayBuffer()); - //@ts-ignore - const originalName = (file.name).replaceAll(" ", "_"); - const extension = path.extname(originalName); - const filePath = await FileService.uploadFile(buffer, originalName) - //@ts-ignore - return NextResponse.json({ Message: "Success", status: 200, ...filePath, type: file?.type ?? '', size: file?.size ?? 0 }); }; + diff --git a/next.config.js b/next.config.js index 424cf983..617ece8e 100644 --- a/next.config.js +++ b/next.config.js @@ -7,7 +7,56 @@ const withPWA = require('next-pwa')({ module.exports = withPWA({ output: 'standalone', transpilePackages: ['react-diff-view','highlight.js','remark-gfm','rehype-raw'], - webpack: (config, { isServer }) => { + async headers() { + return [ + { + headers: [ + { + key: 'Cache-Control', + value: 'public, max-age=31536000, immutable', + }, + ], + source: '/icons/(.*).(png|jpe?g|gif|svg|ico|webp)', + }, + { + headers: [ + { + key: 'Cache-Control', + value: 'public, max-age=31536000, immutable', + }, + ], + source: '/favicon.ico', + }, + { + headers: [ + { + key: 'Cache-Control', + value: 'public, max-age=31536000, immutable', + }, + ], + source: '/loading.mp4', + }, + { + headers: [ + { + key: 'Cache-Control', + value: 'public, max-age=31536000, immutable', + }, + ], + source: '/logo-light.png', + }, + { + headers: [ + { + key: 'Cache-Control', + value: 'public, max-age=31536000, immutable', + }, + ], + source: '/logo-dark.png', + }, + ]; + }, + webpack: (config, { dev,isServer }) => { config.experiments = { ...config.experiments, topLevelAwait: true }; if (!isServer) { config.resolve.fallback = { diff --git a/package.json b/package.json index 76d05552..f38fa003 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "blinko", - "version": "0.26.10", + "version": "0.29.4", "repository": "https://github.com/blinko-space/blinko.git", "private": true, "browser": { @@ -24,6 +24,7 @@ "dev:https": "ngrok http 1111", "build": "set NODE_ENV=production & next build", "build:debug": "set DEBUG=* & next build", + "build:max": "set NODE_OPTIONS=--max-old-space-size=16384 & set DEBUG=* & next build", "vercel-build": "prisma generate && prisma migrate deploy && prisma db seed && next build", "start": "prisma migrate & prisma db seed & next start -p 1111", "lint": "next lint", @@ -53,6 +54,7 @@ "@nextui-org/theme": "^2.4.1", "@prisma/client": "^5.21.1", "@radix-ui/colors": "^3.0.0", + "@radix-ui/react-dialog": "^1.1.4", "@tailwindcss/line-clamp": "^0.4.4", "@tailwindcss/typography": "^0.5.15", "@trpc/client": "11.0.0-rc.553", @@ -62,6 +64,7 @@ "adm-zip": "^0.5.16", "axios": "^1.7.7", "canvas-confetti": "^1.9.3", + "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "copy-to-clipboard": "^3.3.3", "cron": "^3.1.7", @@ -85,6 +88,7 @@ "lexical": "0.17.1", "lodash": "^4.17.21", "lottie-react": "^2.4.0", + "lucide-react": "^0.468.0", "mammoth": "^1.8.0", "mantine-datatable": "^7.12.4", "markdown-it": "^14.1.0", @@ -96,6 +100,7 @@ "mobx": "^6.13.3", "mobx-react-lite": "^4.0.7", "motion": "^11.13.1", + "music-metadata": "^10.6.4", "ncp": "^2.0.0", "next": "14.2.10", "next-auth": "^4.24.7", @@ -116,6 +121,7 @@ "react-accessible-treeview": "^2.9.1", "react-audio-visualize": "^1.2.0", "react-audio-voice-recorder": "^2.2.0", + "react-beautiful-dnd-next": "^11.0.5", "react-burger-menu": "^3.0.9", "react-collapsed": "^4.1.2", "react-dev-inspector": "^2.0.1", @@ -143,7 +149,7 @@ "superjson": "^2.2.1", "swagger-ui-react": "^5.17.14", "swiper": "^11.1.14", - "tailwind-merge": "^1.13.0", + "tailwind-merge": "^1.14.0", "trpc-to-openapi": "^2.0.2", "typed-emitter": "^2.1.0", "typeorm": "^0.3.20", @@ -176,7 +182,7 @@ "prisma": "^5.21.1", "semantic-release": "^24.2.0", "tailwindcss": "^3.3.2", - "tailwindcss-animate": "^1.0.6", + "tailwindcss-animate": "^1.0.7", "ts-node": "^10.9.2", "typescript": "^5.1.6" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d70aa3bb..c72dbdeb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -77,6 +77,9 @@ importers: '@radix-ui/colors': specifier: ^3.0.0 version: 3.0.0 + '@radix-ui/react-dialog': + specifier: ^1.1.4 + version: 1.1.4(@types/react-dom@18.3.1)(@types/react@18.2.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@tailwindcss/line-clamp': specifier: ^0.4.4 version: 0.4.4(tailwindcss@3.4.14(ts-node@10.9.2(@types/node@20.2.5)(typescript@5.6.3))) @@ -104,6 +107,9 @@ importers: canvas-confetti: specifier: ^1.9.3 version: 1.9.3 + class-variance-authority: + specifier: ^0.7.1 + version: 0.7.1 clsx: specifier: ^2.1.1 version: 2.1.1 @@ -173,6 +179,9 @@ importers: lottie-react: specifier: ^2.4.0 version: 2.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + lucide-react: + specifier: ^0.468.0 + version: 0.468.0(react@18.3.1) mammoth: specifier: ^1.8.0 version: 1.8.0 @@ -206,6 +215,9 @@ importers: motion: specifier: ^11.13.1 version: 11.13.1(@emotion/is-prop-valid@1.2.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + music-metadata: + specifier: ^10.6.4 + version: 10.6.4 ncp: specifier: ^2.0.0 version: 2.0.0 @@ -266,6 +278,9 @@ importers: react-audio-voice-recorder: specifier: ^2.2.0 version: 2.2.0(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-beautiful-dnd-next: + specifier: ^11.0.5 + version: 11.0.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react-burger-menu: specifier: ^3.0.9 version: 3.0.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -348,7 +363,7 @@ importers: specifier: ^11.1.14 version: 11.1.14 tailwind-merge: - specifier: ^1.13.0 + specifier: ^1.14.0 version: 1.14.0 trpc-to-openapi: specifier: ^2.0.2 @@ -442,7 +457,7 @@ importers: specifier: ^3.3.2 version: 3.4.14(ts-node@10.9.2(@types/node@20.2.5)(typescript@5.6.3)) tailwindcss-animate: - specifier: ^1.0.6 + specifier: ^1.0.7 version: 1.0.7(tailwindcss@3.4.14(ts-node@10.9.2(@types/node@20.2.5)(typescript@5.6.3))) ts-node: specifier: ^10.9.2 @@ -1219,6 +1234,10 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 || ^8.0.0-0 <8.0.0 + '@babel/runtime-corejs2@7.26.0': + resolution: {integrity: sha512-AQKSxUdaM7uTEGFmLZj1LOgX3LaLdt4udjqywaVdN6R5P2KAgqtBkDW4TS2ySRYNqcKmEe8Xv96jegHJNNb7Gg==} + engines: {node: '>=6.9.0'} + '@babel/runtime-corejs3@7.25.7': resolution: {integrity: sha512-gMmIEhg35sXk9Te5qbGp3W9YKrvLt3HV658/d3odWrHSqT0JeG5OzsJWFHRLiOohRyjRsJc/x03DhJm3i8VJxg==} engines: {node: '>=6.9.0'} @@ -2807,6 +2826,168 @@ packages: '@radix-ui/colors@3.0.0': resolution: {integrity: sha512-FUOsGBkHrYJwCSEtWRCIfQbZG7q1e6DgxCIOe1SUQzDe/7rXXeA47s8yCn6fuTNQAj1Zq4oTFi9Yjp3wzElcxg==} + '@radix-ui/primitive@1.1.1': + resolution: {integrity: sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA==} + + '@radix-ui/react-compose-refs@1.1.1': + resolution: {integrity: sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-context@1.1.1': + resolution: {integrity: sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-dialog@1.1.4': + resolution: {integrity: sha512-Ur7EV1IwQGCyaAuyDRiOLA5JIUZxELJljF+MbM/2NC0BYwfuRrbpS30BiQBJrVruscgUkieKkqXYDOoByaxIoA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-dismissable-layer@1.1.3': + resolution: {integrity: sha512-onrWn/72lQoEucDmJnr8uczSNTujT0vJnA/X5+3AkChVPowr8n1yvIKIabhWyMQeMvvmdpsvcyDqx3X1LEXCPg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-focus-guards@1.1.1': + resolution: {integrity: sha512-pSIwfrT1a6sIoDASCSpFwOasEwKTZWDw/iBdtnqKO7v6FeOzYJ7U53cPzYFVR3geGGXgVHaH+CdngrrAzqUGxg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-focus-scope@1.1.1': + resolution: {integrity: sha512-01omzJAYRxXdG2/he/+xy+c8a8gCydoQ1yOxnWNcRhrrBW5W+RQJ22EK1SaO8tb3WoUsuEw7mJjBozPzihDFjA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-id@1.1.0': + resolution: {integrity: sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-portal@1.1.3': + resolution: {integrity: sha512-NciRqhXnGojhT93RPyDaMPfLH3ZSl4jjIFbZQ1b/vxvZEdHsBZ49wP9w8L3HzUQwep01LcWtkUvm0OVB5JAHTw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-presence@1.1.2': + resolution: {integrity: sha512-18TFr80t5EVgL9x1SwF/YGtfG+l0BS0PRAlCWBDoBEiDQjeKgnNZRVJp/oVBl24sr3Gbfwc/Qpj4OcWTQMsAEg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-primitive@2.0.1': + resolution: {integrity: sha512-sHCWTtxwNn3L3fH8qAfnF3WbUZycW93SM1j3NFDzXBiz8D6F5UTTy8G1+WFEaiCdvCVRJWj6N2R4Xq6HdiHmDg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-slot@1.1.1': + resolution: {integrity: sha512-RApLLOcINYJA+dMVbOju7MYv1Mb2EBp2nH4HdDzXTSyaR5optlm6Otrz1euW3HbdOR8UmmFK06TD+A9frYWv+g==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-callback-ref@1.1.0': + resolution: {integrity: sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-controllable-state@1.1.0': + resolution: {integrity: sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-escape-keydown@1.1.0': + resolution: {integrity: sha512-L7vwWlR1kTTQ3oh7g1O0CBF3YCyyTj8NmhLR+phShpyA50HCfBFKVJTpshm9PzLiKmehsrQzTYTpX9HvmC9rhw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-layout-effect@1.1.0': + resolution: {integrity: sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@react-aria/breadcrumbs@3.5.19': resolution: {integrity: sha512-mVngOPFYVVhec89rf/CiYQGTfaLRfHFtX+JQwY7sNYNqSA+gO8p4lNARe3Be6bJPgH+LUQuruIY9/ZDL6LT3HA==} peerDependencies: @@ -3907,6 +4088,9 @@ packages: '@tanstack/virtual-core@3.10.9': resolution: {integrity: sha512-kBknKOKzmeR7lN+vSadaKWXaLS0SZZG+oqpQ/k80Q6g9REn6zRHS/ZYdrIzHnpHgy/eWs00SujveUN/GJT2qTw==} + '@tokenizer/token@0.3.0': + resolution: {integrity: sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==} + '@tootallnate/once@1.1.2': resolution: {integrity: sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==} engines: {node: '>= 6'} @@ -4072,6 +4256,9 @@ packages: '@types/hast@3.0.4': resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} + '@types/hoist-non-react-statics@3.3.6': + resolution: {integrity: sha512-lPByRJUer/iN/xa4qpyL0qmL11DqNW81iU/IG1S3uvRUq4oKagz8VCxZjiWkumgt66YT3vOdDgZ0o32sGKtCEw==} + '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} @@ -4132,6 +4319,9 @@ packages: '@types/react-reconciler@0.28.8': resolution: {integrity: sha512-SN9c4kxXZonFhbX4hJrZy37yw9e7EIxcpHCxQv5JUS18wDE5ovkQKlqQEkufdJCCMfuI9BnjUJvhYeJ9x5Ra7g==} + '@types/react-redux@7.1.34': + resolution: {integrity: sha512-GdFaVjEbYv4Fthm2ZLvj1VSCedV7TqE5y1kNwnjSdBOTXuRSgowux6J8TAct15T3CKBr63UMk+2CO7ilRhyrAQ==} + '@types/react@18.2.8': resolution: {integrity: sha512-lTyWUNrd8ntVkqycEEplasWy2OxNlShj3zqS0LuB1ENUGis5HodmhM7DtCoUGbxj3VW/WsGA0DUhpG6XrM7gPA==} @@ -4368,6 +4558,10 @@ packages: argv-formatter@1.0.0: resolution: {integrity: sha512-F2+Hkm9xFaRg+GkaNnbwXNDV5O6pnCFEmqyhvfC/Ic5LbgOWjJh3L+mN/s91rxVL3znE7DYVpW0GJFT+4YBgWw==} + aria-hidden@1.2.4: + resolution: {integrity: sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A==} + engines: {node: '>=10'} + array-buffer-byte-length@1.0.1: resolution: {integrity: sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==} engines: {node: '>= 0.4'} @@ -4644,6 +4838,9 @@ packages: resolution: {integrity: sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==} engines: {node: '>=6.0'} + class-variance-authority@0.7.1: + resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==} + classnames@2.5.1: resolution: {integrity: sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==} @@ -4790,6 +4987,10 @@ packages: resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} engines: {node: '>= 0.6'} + content-type@1.0.5: + resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} + engines: {node: '>= 0.6'} + conventional-changelog-angular@8.0.0: resolution: {integrity: sha512-CLf+zr6St0wIxos4bmaKHRXWAcsCXrJU6F4VdNDrGRK3B8LDLKoX3zuMV5GhtbGkVR/LohZ6MT6im43vZLSjmA==} engines: {node: '>=18'} @@ -4843,6 +5044,10 @@ packages: core-js-pure@3.38.1: resolution: {integrity: sha512-BY8Etc1FZqdw1glX0XNOq2FDwfrg/VGqoZOZCdaL+UmdaqDwQwYXkMJT4t6In+zfEfOJDcM9T0KdbBeJg8KKCQ==} + core-js@2.6.12: + resolution: {integrity: sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==} + deprecated: core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js. + core-util-is@1.0.3: resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} @@ -4893,6 +5098,9 @@ packages: resolution: {integrity: sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==} engines: {node: '>=12'} + css-box-model@1.2.1: + resolution: {integrity: sha512-a7Vr4Q/kd/aw96bnJG332W9V9LkJO69JRcaCYDUqjp6/z0w6VcZjgAcTbgFxEPfBgdnAwlh3iwu+hLopa+flJw==} + css-declaration-sorter@6.4.1: resolution: {integrity: sha512-rtdthzxKuyq6IzqX6jEcIzQF/YqccluefyCYheovBOLhFT/drQA9zj/UbRAa9J7C0o6EG6u3E6g+vKkay7/k3g==} engines: {node: ^10 || ^12 || >=14} @@ -5582,6 +5790,10 @@ packages: resolution: {integrity: sha512-QlZ5yJC0VxHxQQsQhXvBaC7VRJ2uaxTf+Tfpu4Z/OcVQJVpZO+DGU0rkoVW5ce2SccxugvpBJoMvUs59iILYdw==} engines: {node: '>= 12'} + file-type@19.6.0: + resolution: {integrity: sha512-VZR5I7k5wkD0HgFnMsq5hOsSc710MJMu5Nc5QYsbe38NN5iPV/XTObYLc/cpttRTf6lX538+5uO1ZQRhYibiZQ==} + engines: {node: '>=18'} + file-uri-to-path@1.0.0: resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==} @@ -5986,6 +6198,9 @@ packages: highlightjs-vue@1.0.0: resolution: {integrity: sha512-PDEfEF102G23vHmPhLyPboFCD+BkMGu+GuJe2d9/eH4FsCwvgBpnc9n0pGE+ffKdph38s6foEZiEjdgHdzp+IA==} + hoist-non-react-statics@3.3.2: + resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} + hook-std@3.0.0: resolution: {integrity: sha512-jHRQzjSDzMtFy34AGj1DN+vq54WVuhSvKgrHf0OMiFQTwDD4L/qqofVEWjLOBMTn5+lCD3fPg32W9yOfnEJTTw==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -6602,6 +6817,10 @@ packages: lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + link@2.1.1: + resolution: {integrity: sha512-NV3AUVYBovJ6eVQcTeRoPnZSxzt2LOijNd+ugEZKRy/XeQlpTRhVRkuDv5kOlXwMAUx30vfUc7asRFb9RT65yg==} + hasBin: true + linkify-it@5.0.0: resolution: {integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==} @@ -6733,6 +6952,11 @@ packages: resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} engines: {node: '>=10'} + lucide-react@0.468.0: + resolution: {integrity: sha512-6koYRhnM2N0GGZIdXzSeiNwguv1gt/FAjZOiPl76roBi3xKEXa4WmfpxgQwTTL4KipXjefrnf3oV4IsYhi4JFA==} + peerDependencies: + react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc + luxon@3.4.4: resolution: {integrity: sha512-zobTr7akeGHnv7eBOXcRgMeCP6+uyYsczwmeRCauvpvaAltgNyTbLH/+VaEAPUeWBT+1GuNmz4wC/6jtQzbbVA==} engines: {node: '>=12'} @@ -6875,10 +7099,17 @@ packages: resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} engines: {node: '>= 0.6'} + media-typer@1.1.0: + resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} + engines: {node: '>= 0.8'} + memfs@3.5.3: resolution: {integrity: sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==} engines: {node: '>= 4.0.0'} + memoize-one@5.2.1: + resolution: {integrity: sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==} + meow@13.2.0: resolution: {integrity: sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==} engines: {node: '>=18'} @@ -7146,6 +7377,10 @@ packages: ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + music-metadata@10.6.4: + resolution: {integrity: sha512-42ekQ5CRic4Pvw/85FfzMKegeRDHyWBpCjSSI1B9PTGqaevZ17ASA4v4W6MRq1ELC5THn5rD8S+82iPQ6gv6lw==} + engines: {node: '>=16.0.0'} + mustache@4.2.0: resolution: {integrity: sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==} hasBin: true @@ -7699,6 +7934,10 @@ packages: resolution: {integrity: sha512-v6ZJ/efsBpGrGGknjtq9J/oC8tZWq0KWL5vQrk2GlzLEQPUDB1ex+13Rmidl1neNN358Jn9EHZw5y07FFtaC7A==} engines: {node: '>=6.8.1'} + peek-readable@5.3.1: + resolution: {integrity: sha512-GVlENSDW6KHaXcd9zkZltB7tCLosKB/4Hg0fqBJkAoBgYG2Tn1xtMgXtSUuMU9AK/gCm/tTdT8mgAeF4YNeeqw==} + engines: {node: '>=14.16'} + pg-cloudflare@1.1.1: resolution: {integrity: sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==} @@ -8189,6 +8428,9 @@ packages: radix3@1.1.2: resolution: {integrity: sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==} + raf-schd@4.0.3: + resolution: {integrity: sha512-tQkJl2GRWh83ui2DiPTJz9wEiMN20syf+5oKfB03yYP7ioZcJwsIK8FjrtLwH1m7C7e+Tt2yYBlrOpdT+dyeIQ==} + ramda-adjunct@5.1.0: resolution: {integrity: sha512-8qCpl2vZBXEJyNbi4zqcgdfHtcdsWjOGbiNSEnEBrM6Y0OKOT8UxJbIVGm1TIcjaSu2MxaWcgtsNlKlCk7o7qg==} engines: {node: '>=0.10.3'} @@ -8244,6 +8486,11 @@ packages: react: '>=16.2.0' react-dom: '>=16.2.0' + react-beautiful-dnd-next@11.0.5: + resolution: {integrity: sha512-kM5Mob41HkA3ShS9uXqeMkW51L5bVsfttxfrwwHucu7I6SdnRKCyN78t6QiLH/UJQQ8T4ukI6NeQAQQpGwolkg==} + peerDependencies: + react: ^16.8.5 + react-burger-menu@3.0.9: resolution: {integrity: sha512-Qy15hkCxwxNEKfqdAv43F+8ZSl+/c6KkqrBwGP0CesFYJ02onHtiUFUbuhSWCMtBH8/n0HhfekFlp/NyCdKYzQ==} engines: {node: '>=4.0.0'} @@ -8366,6 +8613,9 @@ packages: react-is@16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + react-is@17.0.2: + resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} + react-markdown@9.0.1: resolution: {integrity: sha512-186Gw/vF1uRkydbsOIkcGXw7aHq0sZOCRFFjGrr7b9+nVZg4UfA4enXCaxm4fUzecU38sWfrNDitGhshuU7rdg==} peerDependencies: @@ -8392,6 +8642,18 @@ packages: react: '>=16.8.0' react-dom: '>=16.8.0' + react-redux@7.2.9: + resolution: {integrity: sha512-Gx4L3uM182jEEayZfRbI/G11ZpYdNAnBs70lFVMNdHJI76XYtR+7m0MN+eAs7UHBPhWXcnFPaS+9owSCJQHNpQ==} + peerDependencies: + react: ^16.8.3 || ^17 || ^18 + react-dom: '*' + react-native: '*' + peerDependenciesMeta: + react-dom: + optional: true + react-native: + optional: true + react-redux@9.1.2: resolution: {integrity: sha512-0OA4dhM1W48l3uzmv6B7TXPCGmokUU4p1M44DGN2/D9a1FjVPukVjER1PcPX97jIg6aUeLq1XJo1IpfbgULn0w==} peerDependencies: @@ -8414,6 +8676,16 @@ packages: '@types/react': optional: true + react-remove-scroll-bar@2.3.8: + resolution: {integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + react-remove-scroll@2.6.0: resolution: {integrity: sha512-I2U4JVEsQenxDAKaVa3VZ/JeJZe0/2DxPWL8Tj8yLKctQJQiZM52pn/GWFpSp8dftjM3pSAHVJZscAnC/y+ySQ==} engines: {node: '>=10'} @@ -8424,6 +8696,16 @@ packages: '@types/react': optional: true + react-remove-scroll@2.6.2: + resolution: {integrity: sha512-KmONPx5fnlXYJQqC62Q+lwIeAk64ws/cUw6omIumRzMRPqgnYqhSSti99nbj0Ry13bv7dF+BKn7NB+OqkdZGTw==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + react-resizable@3.0.5: resolution: {integrity: sha512-vKpeHhI5OZvYn82kXOs1bC8aOXktGU5AmKAgaZS4F5JPburCtbmDPqE7Pzp+1kN4+Wb81LlF33VpGwWwtXem+w==} peerDependencies: @@ -8439,6 +8721,16 @@ packages: '@types/react': optional: true + react-style-singleton@2.2.3: + resolution: {integrity: sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + react-syntax-highlighter@15.6.1: resolution: {integrity: sha512-OqJ2/vL7lEeV5zTJyG7kmARppUjiB9h9udl4qHQjjgEos66z00Ia0OckwYfRxCSFrW8RJIBnsBwQsHZbVPspqg==} peerDependencies: @@ -8503,6 +8795,9 @@ packages: peerDependencies: immutable: ^3.8.1 || ^4.0.0-rc.1 + redux@4.2.1: + resolution: {integrity: sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==} + redux@5.0.1: resolution: {integrity: sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==} @@ -9015,6 +9310,14 @@ packages: strnum@1.0.5: resolution: {integrity: sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==} + strtok3@10.0.1: + resolution: {integrity: sha512-7OOJepVlvlcgjW/fLNCsIqpNleAoi1y0LTRWGnOpABOSpRmw+65HvnruoOCnjpaQ1efnlYpQ/JwHKuaombnuXQ==} + engines: {node: '>=16'} + + strtok3@9.1.1: + resolution: {integrity: sha512-FhwotcEqjr241ZbjFzjlIYg6c5/L/s4yBGWSMvJ9UoExiSqL+FnFA/CaeZx17WGaZMS/4SOZp8wH18jSS4R4lw==} + engines: {node: '>=16'} + style-inject@0.3.0: resolution: {integrity: sha512-IezA2qp+vcdlhJaVm5SOdPPTUu0FCEqfNSli2vRuSIBbu5Nq5UvygTk/VzeCqfLz2Atj3dVII5QBKGZRZ0edzw==} @@ -9202,6 +9505,9 @@ packages: resolution: {integrity: sha512-75voc/9G4rDIJleOo4jPvN4/YC4GRZrY8yy1uU4lwrB3XEQbWve8zXoO5No4eFrGcTAMYyoY67p8jRQdtA1HbA==} engines: {node: '>=12'} + tiny-invariant@1.3.3: + resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} + tiny-warning@1.0.3: resolution: {integrity: sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==} @@ -9223,6 +9529,10 @@ packages: resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} engines: {node: '>=0.6'} + token-types@6.0.0: + resolution: {integrity: sha512-lbDrTLVsHhOMljPscd0yitpozq7Ga2M5Cvez5AjGg8GASBjtt6iERCAJ93yommPmz62fb45oFIXHEZ3u9bfJEA==} + engines: {node: '>=14.16'} + totalist@3.0.1: resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} engines: {node: '>=6'} @@ -9422,6 +9732,10 @@ packages: engines: {node: '>=0.8.0'} hasBin: true + uint8array-extras@1.4.0: + resolution: {integrity: sha512-ZPtzy0hu4cZjv3z5NW9gfKnNLjoz4y6uv4HlelAjDK7sY/xOkKZv9xK/WQpcsBB3jEybChz9DPC2U/+cusjJVQ==} + engines: {node: '>=18'} + unbox-primitive@1.0.2: resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} @@ -9562,6 +9876,16 @@ packages: '@types/react': optional: true + use-callback-ref@1.3.3: + resolution: {integrity: sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + use-composed-ref@1.3.0: resolution: {integrity: sha512-GLMG0Jc/jiKov/3Ulid1wbv3r54K9HlMW29IWcDFPEqFkSO2nS0MuefWgMJpeHQ9YJeXDL3ZUF+P3jdXlZX/cQ==} peerDependencies: @@ -9585,6 +9909,11 @@ packages: '@types/react': optional: true + use-memo-one@1.1.3: + resolution: {integrity: sha512-g66/K7ZQGYrI6dy8GLpVcMsBp4s17xNkYJVSMvTEevGy3nDxHOfE6z8BVE22+5G5x7t3+bhzrlTDB7ObrEE0cQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + use-sidecar@1.1.2: resolution: {integrity: sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==} engines: {node: '>=10'} @@ -11295,6 +11624,11 @@ snapshots: '@babel/types': 7.26.0 esutils: 2.0.3 + '@babel/runtime-corejs2@7.26.0': + dependencies: + core-js: 2.6.12 + regenerator-runtime: 0.14.1 + '@babel/runtime-corejs3@7.25.7': dependencies: core-js-pure: 3.38.1 @@ -11323,7 +11657,7 @@ snapshots: '@babel/parser': 7.25.8 '@babel/template': 7.25.7 '@babel/types': 7.25.8 - debug: 4.3.7 + debug: 4.4.0 globals: 11.12.0 transitivePeerDependencies: - supports-color @@ -13217,6 +13551,141 @@ snapshots: '@radix-ui/colors@3.0.0': {} + '@radix-ui/primitive@1.1.1': {} + + '@radix-ui/react-compose-refs@1.1.1(@types/react@18.2.8)(react@18.3.1)': + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 18.2.8 + + '@radix-ui/react-context@1.1.1(@types/react@18.2.8)(react@18.3.1)': + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 18.2.8 + + '@radix-ui/react-dialog@1.1.4(@types/react-dom@18.3.1)(@types/react@18.2.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.1 + '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.2.8)(react@18.3.1) + '@radix-ui/react-context': 1.1.1(@types/react@18.2.8)(react@18.3.1) + '@radix-ui/react-dismissable-layer': 1.1.3(@types/react-dom@18.3.1)(@types/react@18.2.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-focus-guards': 1.1.1(@types/react@18.2.8)(react@18.3.1) + '@radix-ui/react-focus-scope': 1.1.1(@types/react-dom@18.3.1)(@types/react@18.2.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-id': 1.1.0(@types/react@18.2.8)(react@18.3.1) + '@radix-ui/react-portal': 1.1.3(@types/react-dom@18.3.1)(@types/react@18.2.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-presence': 1.1.2(@types/react-dom@18.3.1)(@types/react@18.2.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.0.1(@types/react-dom@18.3.1)(@types/react@18.2.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-slot': 1.1.1(@types/react@18.2.8)(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.2.8)(react@18.3.1) + aria-hidden: 1.2.4 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-remove-scroll: 2.6.2(@types/react@18.2.8)(react@18.3.1) + optionalDependencies: + '@types/react': 18.2.8 + '@types/react-dom': 18.3.1 + + '@radix-ui/react-dismissable-layer@1.1.3(@types/react-dom@18.3.1)(@types/react@18.2.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.1 + '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.2.8)(react@18.3.1) + '@radix-ui/react-primitive': 2.0.1(@types/react-dom@18.3.1)(@types/react@18.2.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.2.8)(react@18.3.1) + '@radix-ui/react-use-escape-keydown': 1.1.0(@types/react@18.2.8)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.2.8 + '@types/react-dom': 18.3.1 + + '@radix-ui/react-focus-guards@1.1.1(@types/react@18.2.8)(react@18.3.1)': + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 18.2.8 + + '@radix-ui/react-focus-scope@1.1.1(@types/react-dom@18.3.1)(@types/react@18.2.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.2.8)(react@18.3.1) + '@radix-ui/react-primitive': 2.0.1(@types/react-dom@18.3.1)(@types/react@18.2.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.2.8)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.2.8 + '@types/react-dom': 18.3.1 + + '@radix-ui/react-id@1.1.0(@types/react@18.2.8)(react@18.3.1)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.2.8)(react@18.3.1) + react: 18.3.1 + optionalDependencies: + '@types/react': 18.2.8 + + '@radix-ui/react-portal@1.1.3(@types/react-dom@18.3.1)(@types/react@18.2.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-primitive': 2.0.1(@types/react-dom@18.3.1)(@types/react@18.2.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.2.8)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.2.8 + '@types/react-dom': 18.3.1 + + '@radix-ui/react-presence@1.1.2(@types/react-dom@18.3.1)(@types/react@18.2.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.2.8)(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.2.8)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.2.8 + '@types/react-dom': 18.3.1 + + '@radix-ui/react-primitive@2.0.1(@types/react-dom@18.3.1)(@types/react@18.2.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-slot': 1.1.1(@types/react@18.2.8)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.2.8 + '@types/react-dom': 18.3.1 + + '@radix-ui/react-slot@1.1.1(@types/react@18.2.8)(react@18.3.1)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.2.8)(react@18.3.1) + react: 18.3.1 + optionalDependencies: + '@types/react': 18.2.8 + + '@radix-ui/react-use-callback-ref@1.1.0(@types/react@18.2.8)(react@18.3.1)': + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 18.2.8 + + '@radix-ui/react-use-controllable-state@1.1.0(@types/react@18.2.8)(react@18.3.1)': + dependencies: + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.2.8)(react@18.3.1) + react: 18.3.1 + optionalDependencies: + '@types/react': 18.2.8 + + '@radix-ui/react-use-escape-keydown@1.1.0(@types/react@18.2.8)(react@18.3.1)': + dependencies: + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.2.8)(react@18.3.1) + react: 18.3.1 + optionalDependencies: + '@types/react': 18.2.8 + + '@radix-ui/react-use-layout-effect@1.1.0(@types/react@18.2.8)(react@18.3.1)': + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 18.2.8 + '@react-aria/breadcrumbs@3.5.19(react@18.3.1)': dependencies: '@react-aria/i18n': 3.12.4(react@18.3.1) @@ -15253,6 +15722,8 @@ snapshots: '@tanstack/virtual-core@3.10.9': {} + '@tokenizer/token@0.3.0': {} + '@tootallnate/once@1.1.2': optional: true @@ -15427,6 +15898,11 @@ snapshots: dependencies: '@types/unist': 3.0.3 + '@types/hoist-non-react-statics@3.3.6': + dependencies: + '@types/react': 18.2.8 + hoist-non-react-statics: 3.3.2 + '@types/json-schema@7.0.15': {} '@types/katex@0.16.7': {} @@ -15486,6 +15962,13 @@ snapshots: dependencies: '@types/react': 18.2.8 + '@types/react-redux@7.1.34': + dependencies: + '@types/hoist-non-react-statics': 3.3.6 + '@types/react': 18.2.8 + hoist-non-react-statics: 3.3.2 + redux: 4.2.1 + '@types/react@18.2.8': dependencies: '@types/prop-types': 15.7.13 @@ -15637,14 +16120,14 @@ snapshots: agent-base@6.0.2: dependencies: - debug: 4.3.7 + debug: 4.4.0 transitivePeerDependencies: - supports-color optional: true agent-base@7.1.1: dependencies: - debug: 4.3.7 + debug: 4.4.0 transitivePeerDependencies: - supports-color @@ -15735,6 +16218,10 @@ snapshots: argv-formatter@1.0.0: {} + aria-hidden@1.2.4: + dependencies: + tslib: 2.8.0 + array-buffer-byte-length@1.0.1: dependencies: call-bind: 1.0.7 @@ -16076,6 +16563,10 @@ snapshots: chrome-trace-event@1.0.4: {} + class-variance-authority@0.7.1: + dependencies: + clsx: 2.1.1 + classnames@2.5.1: {} clean-stack@2.2.0: {} @@ -16212,6 +16703,8 @@ snapshots: dependencies: safe-buffer: 5.2.1 + content-type@1.0.5: {} + conventional-changelog-angular@8.0.0: dependencies: compare-func: 2.0.0 @@ -16258,6 +16751,8 @@ snapshots: core-js-pure@3.38.1: {} + core-js@2.6.12: {} + core-util-is@1.0.3: {} cors@2.8.5: @@ -16319,6 +16814,10 @@ snapshots: dependencies: type-fest: 1.4.0 + css-box-model@1.2.1: + dependencies: + tiny-invariant: 1.3.3 + css-declaration-sorter@6.4.1(postcss@8.4.47): dependencies: postcss: 8.4.47 @@ -17094,6 +17593,13 @@ snapshots: dependencies: tslib: 2.8.0 + file-type@19.6.0: + dependencies: + get-stream: 9.0.1 + strtok3: 9.1.1 + token-types: 6.0.0 + uint8array-extras: 1.4.0 + file-uri-to-path@1.0.0: {} filelist@1.0.4: @@ -17583,6 +18089,10 @@ snapshots: highlightjs-vue@1.0.0: {} + hoist-non-react-statics@3.3.2: + dependencies: + react-is: 16.13.1 + hook-std@3.0.0: {} hosted-git-info@7.0.2: @@ -17640,7 +18150,7 @@ snapshots: dependencies: '@tootallnate/once': 1.1.2 agent-base: 6.0.2 - debug: 4.3.7 + debug: 4.4.0 transitivePeerDependencies: - supports-color optional: true @@ -17664,7 +18174,7 @@ snapshots: https-proxy-agent@5.0.1: dependencies: agent-base: 6.0.2 - debug: 4.3.7 + debug: 4.4.0 transitivePeerDependencies: - supports-color optional: true @@ -18184,6 +18694,8 @@ snapshots: lines-and-columns@1.2.4: {} + link@2.1.1: {} + linkify-it@5.0.0: dependencies: uc.micro: 2.1.0 @@ -18303,6 +18815,10 @@ snapshots: dependencies: yallist: 4.0.0 + lucide-react@0.468.0(react@18.3.1): + dependencies: + react: 18.3.1 + luxon@3.4.4: {} magic-string@0.25.9: @@ -18600,10 +19116,14 @@ snapshots: media-typer@0.3.0: {} + media-typer@1.1.0: {} + memfs@3.5.3: dependencies: fs-monkey: 1.0.6 + memoize-one@5.2.1: {} + meow@13.2.0: {} merge-descriptors@1.0.3: {} @@ -18821,7 +19341,7 @@ snapshots: micromark@4.0.0: dependencies: '@types/debug': 4.1.12 - debug: 4.3.7 + debug: 4.4.0 decode-named-character-reference: 1.0.2 devlop: 1.1.0 micromark-core-commonmark: 2.0.1 @@ -18969,6 +19489,20 @@ snapshots: ms@2.1.3: {} + music-metadata@10.6.4: + dependencies: + '@tokenizer/token': 0.3.0 + content-type: 1.0.5 + debug: 4.4.0 + file-type: 19.6.0 + link: 2.1.1 + media-typer: 1.1.0 + strtok3: 10.0.1 + token-types: 6.0.0 + uint8array-extras: 1.4.0 + transitivePeerDependencies: + - supports-color + mustache@4.2.0: {} mz@2.7.0: @@ -19455,6 +19989,8 @@ snapshots: transitivePeerDependencies: - supports-color + peek-readable@5.3.1: {} + pg-cloudflare@1.1.1: optional: true @@ -19908,6 +20444,8 @@ snapshots: radix3@1.1.2: {} + raf-schd@4.0.3: {} + ramda-adjunct@5.1.0(ramda@0.30.1): dependencies: ramda: 0.30.1 @@ -19971,6 +20509,21 @@ snapshots: transitivePeerDependencies: - encoding + react-beautiful-dnd-next@11.0.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime-corejs2': 7.26.0 + css-box-model: 1.2.1 + memoize-one: 5.2.1 + raf-schd: 4.0.3 + react: 18.3.1 + react-redux: 7.2.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + redux: 4.2.1 + tiny-invariant: 1.3.3 + use-memo-one: 1.1.3(react@18.3.1) + transitivePeerDependencies: + - react-dom + - react-native + react-burger-menu@3.0.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: browserify-optional: 1.0.1 @@ -20141,6 +20694,8 @@ snapshots: react-is@16.13.1: {} + react-is@17.0.2: {} + react-markdown@9.0.1(@types/react@18.2.8)(react@18.3.1): dependencies: '@types/hast': 3.0.4 @@ -20176,6 +20731,18 @@ snapshots: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) + react-redux@7.2.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.25.7 + '@types/react-redux': 7.1.34 + hoist-non-react-statics: 3.3.2 + loose-envify: 1.4.0 + prop-types: 15.8.1 + react: 18.3.1 + react-is: 17.0.2 + optionalDependencies: + react-dom: 18.3.1(react@18.3.1) + react-redux@9.1.2(@types/react@18.2.8)(react@18.3.1)(redux@5.0.1): dependencies: '@types/use-sync-external-store': 0.0.3 @@ -20193,6 +20760,14 @@ snapshots: optionalDependencies: '@types/react': 18.2.8 + react-remove-scroll-bar@2.3.8(@types/react@18.2.8)(react@18.3.1): + dependencies: + react: 18.3.1 + react-style-singleton: 2.2.3(@types/react@18.2.8)(react@18.3.1) + tslib: 2.8.0 + optionalDependencies: + '@types/react': 18.2.8 + react-remove-scroll@2.6.0(@types/react@18.2.8)(react@18.3.1): dependencies: react: 18.3.1 @@ -20204,6 +20779,17 @@ snapshots: optionalDependencies: '@types/react': 18.2.8 + react-remove-scroll@2.6.2(@types/react@18.2.8)(react@18.3.1): + dependencies: + react: 18.3.1 + react-remove-scroll-bar: 2.3.8(@types/react@18.2.8)(react@18.3.1) + react-style-singleton: 2.2.1(@types/react@18.2.8)(react@18.3.1) + tslib: 2.8.0 + use-callback-ref: 1.3.3(@types/react@18.2.8)(react@18.3.1) + use-sidecar: 1.1.2(@types/react@18.2.8)(react@18.3.1) + optionalDependencies: + '@types/react': 18.2.8 + react-resizable@3.0.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: prop-types: 15.8.1 @@ -20221,6 +20807,14 @@ snapshots: optionalDependencies: '@types/react': 18.2.8 + react-style-singleton@2.2.3(@types/react@18.2.8)(react@18.3.1): + dependencies: + get-nonce: 1.0.1 + react: 18.3.1 + tslib: 2.8.0 + optionalDependencies: + '@types/react': 18.2.8 + react-syntax-highlighter@15.6.1(react@18.3.1): dependencies: '@babel/runtime': 7.25.7 @@ -20313,6 +20907,10 @@ snapshots: dependencies: immutable: 3.8.2 + redux@4.2.1: + dependencies: + '@babel/runtime': 7.25.7 + redux@5.0.1: {} reflect-metadata@0.2.2: {} @@ -20763,7 +21361,7 @@ snapshots: socks-proxy-agent@6.2.1: dependencies: agent-base: 6.0.2 - debug: 4.3.7 + debug: 4.4.0 socks: 2.8.3 transitivePeerDependencies: - supports-color @@ -20950,6 +21548,16 @@ snapshots: strnum@1.0.5: {} + strtok3@10.0.1: + dependencies: + '@tokenizer/token': 0.3.0 + peek-readable: 5.3.1 + + strtok3@9.1.1: + dependencies: + '@tokenizer/token': 0.3.0 + peek-readable: 5.3.1 + style-inject@0.3.0: {} style-to-object@1.0.8: @@ -21214,6 +21822,8 @@ snapshots: dependencies: convert-hrtime: 5.0.0 + tiny-invariant@1.3.3: {} + tiny-warning@1.0.3: {} tinyexec@0.3.1: {} @@ -21228,6 +21838,11 @@ snapshots: toidentifier@1.0.1: {} + token-types@6.0.0: + dependencies: + '@tokenizer/token': 0.3.0 + ieee754: 1.2.1 + totalist@3.0.1: {} tough-cookie@4.1.4: @@ -21407,6 +22022,8 @@ snapshots: uglify-js@3.19.3: optional: true + uint8array-extras@1.4.0: {} + unbox-primitive@1.0.2: dependencies: call-bind: 1.0.7 @@ -21567,6 +22184,13 @@ snapshots: optionalDependencies: '@types/react': 18.2.8 + use-callback-ref@1.3.3(@types/react@18.2.8)(react@18.3.1): + dependencies: + react: 18.3.1 + tslib: 2.8.0 + optionalDependencies: + '@types/react': 18.2.8 + use-composed-ref@1.3.0(react@18.3.1): dependencies: react: 18.3.1 @@ -21584,6 +22208,10 @@ snapshots: optionalDependencies: '@types/react': 18.2.8 + use-memo-one@1.1.3(react@18.3.1): + dependencies: + react: 18.3.1 + use-sidecar@1.1.2(@types/react@18.2.8)(react@18.3.1): dependencies: detect-node-es: 1.1.0 diff --git a/prisma/migrations/20241218023101_0_27_0/migration.sql b/prisma/migrations/20241218023101_0_27_0/migration.sql new file mode 100644 index 00000000..5db902fb --- /dev/null +++ b/prisma/migrations/20241218023101_0_27_0/migration.sql @@ -0,0 +1,5 @@ +-- AlterTable +ALTER TABLE "attachments" ADD COLUMN "sortOrder" INTEGER NOT NULL DEFAULT 0; + +-- AlterTable +ALTER TABLE "tag" ADD COLUMN "sortOrder" INTEGER NOT NULL DEFAULT 0; diff --git a/prisma/migrations/20241219062514_0_27_7/migration.sql b/prisma/migrations/20241219062514_0_27_7/migration.sql new file mode 100644 index 00000000..3b33e6bf --- /dev/null +++ b/prisma/migrations/20241219062514_0_27_7/migration.sql @@ -0,0 +1,3 @@ +-- AlterTable +ALTER TABLE "attachments" ADD COLUMN "depth" INTEGER, +ADD COLUMN "perfixPath" VARCHAR DEFAULT ''; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index a708c675..3c0880b6 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -36,37 +36,40 @@ model attachments { size Decimal @default(0) @db.Decimal type String @default("") @db.VarChar noteId Int @default(0) + sortOrder Int @default(0) createdAt DateTime @default(now()) @db.Timestamptz(6) updatedAt DateTime @updatedAt @db.Timestamptz(6) + perfixPath String? @default("") @db.VarChar // folder1,folder2,folder3 + depth Int? note notes @relation(fields: [noteId], references: [id]) } model config { - id Int @id @default(autoincrement()) - key String @default("") @db.VarChar - config Json? @db.Json - userId Int? + id Int @id @default(autoincrement()) + key String @default("") @db.VarChar + config Json? @db.Json + userId Int? user accounts? @relation(fields: [userId], references: [id]) } model notes { - id Int @id @default(autoincrement()) - type Int @default(0) - content String @default("") @db.VarChar - isArchived Boolean @default(false) - isRecycle Boolean @default(false) - isShare Boolean @default(false) - isTop Boolean @default(false) - isReviewed Boolean @default(false) - sharePassword String @default("") @db.VarChar - metadata Json? @db.Json - accountId Int? - createdAt DateTime @default(now()) @db.Timestamptz(6) - updatedAt DateTime @updatedAt @db.Timestamptz(6) + id Int @id @default(autoincrement()) + type Int @default(0) + content String @default("") @db.VarChar + isArchived Boolean @default(false) + isRecycle Boolean @default(false) + isShare Boolean @default(false) + isTop Boolean @default(false) + isReviewed Boolean @default(false) + sharePassword String @default("") @db.VarChar + metadata Json? @db.Json + accountId Int? + createdAt DateTime @default(now()) @db.Timestamptz(6) + updatedAt DateTime @updatedAt @db.Timestamptz(6) attachments attachments[] tags tagsToNote[] - account accounts? @relation(fields: [accountId], references: [id]) + account accounts? @relation(fields: [accountId], references: [id]) referencedBy noteReference[] @relation("ReferencedNote") references noteReference[] @relation("ReferencingNote") } @@ -76,10 +79,11 @@ model tag { name String @default("") @db.VarChar icon String @default("") @db.VarChar parent Int @default(0) - accountId Int? + accountId Int? createdAt DateTime @default(now()) @db.Timestamptz(6) updatedAt DateTime @updatedAt @db.Timestamptz(6) tagsToNote tagsToNote[] + sortOrder Int @default(0) account accounts? @relation(fields: [accountId], references: [id]) } @@ -104,13 +108,13 @@ model scheduledTask { } model noteReference { - id Int @id @default(autoincrement()) - fromNoteId Int - toNoteId Int - createdAt DateTime @default(now()) @db.Timestamptz(6) - - fromNote notes @relation("ReferencingNote", fields: [fromNoteId], references: [id]) - toNote notes @relation("ReferencedNote", fields: [toNoteId], references: [id]) + id Int @id @default(autoincrement()) + fromNoteId Int + toNoteId Int + createdAt DateTime @default(now()) @db.Timestamptz(6) + + fromNote notes @relation("ReferencingNote", fields: [fromNoteId], references: [id]) + toNote notes @relation("ReferencedNote", fields: [toNoteId], references: [id]) @@unique([fromNoteId, toNoteId]) } diff --git a/prisma/seed.ts b/prisma/seed.ts index 30c5232a..ad527fcf 100644 --- a/prisma/seed.ts +++ b/prisma/seed.ts @@ -284,6 +284,36 @@ async function main() { break } } + try { + // update attachments depth and perfixPath + const attachmentsWithoutDepth = await prisma.attachments.findMany({ + where: { + OR: [ + { depth: null }, + { perfixPath: null } + ] + } + }); + + if (attachmentsWithoutDepth.length > 0) { + for (const attachment of attachmentsWithoutDepth) { + const pathParts = attachment.path + .replace('/api/file/', '') + .replace('/api/s3file/', '') + .split('/'); + + await prisma.attachments.update({ + where: { id: attachment.id }, + data: { + depth: pathParts.length - 1, + perfixPath: pathParts.slice(0, -1).join(',') + } + }); + } + } + } catch (error) { + console.log(error) + } } main() diff --git a/public/favicon.ico b/public/favicon.ico index a110df44..32469e9b 100644 Binary files a/public/favicon.ico and b/public/favicon.ico differ diff --git a/public/icon-192x192.png b/public/icon-192x192.png deleted file mode 100644 index 405548b9..00000000 Binary files a/public/icon-192x192.png and /dev/null differ diff --git a/public/icon-512x512.png b/public/icon-512x512.png deleted file mode 100644 index 86cbc800..00000000 Binary files a/public/icon-512x512.png and /dev/null differ diff --git a/public/icons/icon-128x128.png b/public/icons/icon-128x128.png new file mode 100644 index 00000000..32469e9b Binary files /dev/null and b/public/icons/icon-128x128.png differ diff --git a/public/icons/icon-144x144.png b/public/icons/icon-144x144.png new file mode 100644 index 00000000..ec2a7ca7 Binary files /dev/null and b/public/icons/icon-144x144.png differ diff --git a/public/icons/icon-152x152.png b/public/icons/icon-152x152.png new file mode 100644 index 00000000..50aa373a Binary files /dev/null and b/public/icons/icon-152x152.png differ diff --git a/public/icons/icon-192x192.png b/public/icons/icon-192x192.png new file mode 100644 index 00000000..8d5b9333 Binary files /dev/null and b/public/icons/icon-192x192.png differ diff --git a/public/icons/icon-256x256.png b/public/icons/icon-256x256.png new file mode 100644 index 00000000..adc4d0de Binary files /dev/null and b/public/icons/icon-256x256.png differ diff --git a/public/loading.mp4 b/public/loading.mp4 new file mode 100644 index 00000000..f8bc1c80 Binary files /dev/null and b/public/loading.mp4 differ diff --git a/public/locales/ar/translation.json b/public/locales/ar/translation.json index 8de0437c..365b76ea 100644 --- a/public/locales/ar/translation.json +++ b/public/locales/ar/translation.json @@ -1,4 +1,6 @@ { + "spotify-consumer-key-tip": "كان يُستخدم للحصول على غلاف موسيقى mp3", + "spotify-consumer-key-tip-2": "احصل على مفتاح API من https://developer.spotify.com/", "hello": "مرحبًا", "blinko": "بلينكو", "notes": "ملاحظات", @@ -344,5 +346,29 @@ "insert-attachment-or-note": "الإرفاق بملف أو المذكرة؟", "context": "السياق", "paste-to-note-or-attachment": "هل أنت متأكد من لصق النص في السياق أو المرفق؟", - "attachment": "المرفق" + "attachment": "المرفق", + "after-deletion-all-user-data-will-be-cleared-and-unrecoverable": "بعد الحذف، سيتم مسح جميع بيانات المستخدم ولن يمكن استرجاعها.", + "upload-completed": "تم الرفع بنجاح", + "upload-cancelled": "تم إلغاء التحميل", + "upload-failed": "فشل التحميل", + "import-from-bko-tip": "تحميل إلى s3 للإسترداد غير مدعوم في الوقت الحالي. يُرجى تعطيل الخيار s3 مؤقتًا عند رغبتك في الاسترداد.", + "edit-time": "وقت التحرير", + "ai-write": "الذكاء الاصطناعي", + "download": "تحميل", + "rename": "إعادة تسمية", + "move-up": "تحرك لأعلى", + "cut": "قطع", + "paste": "لصق", + "confirm-delete": "تأكيد الحذف", + "confirm-delete-content": "هل أنت متأكد أنك تريد حذف {{name}}؟ لا يمكن التراجع عن هذا الإجراء.", + "folder-name": "اسم المجلد", + "file-name": "اسم الملف", + "operation-success": "نجاح العملية", + "cloud-file": "ملف سحابي", + "move-to-parent": "الانتقال إلى الوالد", + "no-resources-found": "لم يتم العثور على موارد", + "operation-in-progress": "العملية قيد التقدم", + "new-folder": "مجلد جديد", + "folder-name-exists": "الاسم المجلد موجود", + "folder-name-required": "اسم المجلد مطلوب" } diff --git a/public/locales/de/translation.json b/public/locales/de/translation.json index a7b46a8c..ff42e782 100644 --- a/public/locales/de/translation.json +++ b/public/locales/de/translation.json @@ -1,15 +1,17 @@ { + "spotify-consumer-key-tip": "Verwendet, um MP3-Musikcover zu erhalten.", + "spotify-consumer-key-tip-2": "Holen Sie sich den API-Schlüssel von https://developer.spotify.com/", "blinko": "Blinko", "notes": "Anmerkungen", "resources": "Ressourcen", "archived": "Archiviert", "settings": "Einstellungen", - "total-tags": "TOTAL TAGS", + "total-tags": "ALLE TAGS", "search": "Suche...", "i-have-a-new-idea": "Ich habe eine neue Idee...", "add-tag": "Tag hinzufügen", - "all-notes-have-been-loaded": "Alle {{Items}} Notizen wurden geladen", - "already-have-an-account-direct-login": "Sie haben bereits ein Konto? Direkt anmelden", + "all-notes-have-been-loaded": "Alle {{items}} Notizen wurden geladen", + "already-have-an-account-direct-login": "Konto bereits vorhanden? Direkt anmelden", "api-endpoint": "API-Endpunkt", "archive": "Archiv", "ask-about-your-notes": "Fragen Sie nach Ihren Notizen", @@ -21,36 +23,36 @@ "change-type": "Typ ändern", "change-user-info": "Benutzerinformationen ändern", "check-list": "Checkliste", - "confirm": "Bestätigen Sie", - "confirm-password": "Bestätigen Sie Ihr Passwort", - "convert-to": "Umrechnen in", + "confirm": "Bestätigen", + "confirm-password": "Passwort bestätigen", + "convert-to": "Konvertieren zu", "convert-to-blinko": "Zu Blinko konvertieren", - "convert-to-note": "In Note umwandeln", - "create-successfully": "Erfolgreich erstellen", - "create-successfully-is-about-to-jump-to-the-login": "Erfolgreich erstellen, springt gleich zum Login", + "convert-to-note": "Zu Anmerkung konvertieren", + "create-successfully": "Erfolgreich erstellt", + "create-successfully-is-about-to-jump-to-the-login": "Erfolgreich erstellt, Umleitung zum Login", "daily-review": "Tägliche Überprüfung", "delete": "Löschen", - "delete-confirm": "Löschen Bestätigen", - "delete-success": "Erfolgreich löschen", + "delete-confirm": "Löschen bestätigen", + "delete-success": "Erfolgreich gelöscht", "edit": "bearbeiten", - "enter-your-name": "Geben Sie Ihren Namen ein", - "enter-your-password": "Geben Sie Ihr Passwort ein", - "enter-your-username": "Geben Sie Ihren Benutzernamen ein", + "enter-your-name": "Namen eingeben", + "enter-your-password": "Passwort eingeben", + "enter-your-username": "Benutzernamen eingeben", "every-half-year": "Jedes halbe Jahr", "every-month": "Jeden Monat", "hello": "hallo", - "hi-user-name-i-can-search-for-the-notes-for-you-how-can-i-help-you-today": "Hallo {{Name}}, ich kann die Notizen für Sie suchen, wie kann ich Ihnen heute helfen?", + "hi-user-name-i-can-search-for-the-notes-for-you-how-can-i-help-you-today": "Hallo {{name}}, ich kann in Notizen suchen, wie kann ich heute helfen?", "import-from-bko": "Importieren von .bko", "import": "Importieren", - "insert-hashtag": "Hashtag einfügen", + "insert-hashtag": "Tag einfügen", "items": "Artikel", "language": "Sprache", "logout": "Abmeldung", "multiple-select": "Mehrfachauswahl", "name": "Name", "name-db": "NAME", - "need-to-create-an-account": "Möchten Sie ein Konto erstellen?", - "new-version-detected-click-to-get-the-latest-version": "🎉Neue Version entdeckt, klicken Sie, um die neueste Version zu erhalten", + "need-to-create-an-account": "Konto erstellen?", + "new-version-detected-click-to-get-the-latest-version": "🎉Neue Version entdeckt, hier klicken, um die neueste Version zu erhalten", "nickname": "Spitzname", "no-data-here-well-then-time-to-write-a-note": "Keine Daten hier? Dann wird es Zeit, eine Notiz zu schreiben!", "no-tag-found": "Kein Tag gefunden", @@ -63,26 +65,26 @@ "recording": "Aufnahme", "required-items-cannot-be-empty": "Erforderliche Elemente können nicht leer sein", "rest-user-password": "Restliches Benutzerpasswort", - "running": "Laufen", - "save": "Speichern Sie", - "schedule-archive-blinko": "Zeitplan Archiv Blinko", - "schedule-back-up": "Zeitplan für Back-up", - "schedule-task": "Zeitplan Aufgabe", + "running": "Läuft", + "save": "Speichern", + "schedule-archive-blinko": "Blinko archivieren", + "schedule-back-up": "Zeitplan für Backup", + "schedule-task": "Aufgaben Zeitplan", "show-less": "Weniger anzeigen", "show-more": "Mehr anzeigen", - "show-navigation-bar-on-mobile": "Ausgeblendete Navigationsleiste auf dem Handy", - "sign-in": "Eintragen", - "sign-up": "Anmeldung", + "show-navigation-bar-on-mobile": "Navigationsleiste auf dem Handy ausblenden", + "sign-in": "Anmelden", + "sign-up": "Registrieren", "status": "STATUS", "stopped": "Gestoppt", - "the-two-passwords-are-inconsistent": "Die beiden Kennwörter sind inkonsistent", - "theme": "Thema", - "there-are-no-resources-yet-go-upload-them-now": "Es sind noch keine Ressourcen vorhanden, laden Sie sie jetzt hoch", - "this-operation-removes-the-associated-label-and-cannot-be-restored-please-confirm": "Bei diesem Vorgang wird das zugehörige Etikett entfernt und kann nicht wiederhergestellt werden.", - "this-operation-will-be-delete-resource-are-you-sure": "Sind Sie sicher, dass es sich bei diesem Vorgang um das Löschen von Ressourcen handelt?", - "top": "Top", + "the-two-passwords-are-inconsistent": "Die beiden Kennwörter sind unterschiedlich", + "theme": "Modus", + "there-are-no-resources-yet-go-upload-them-now": "Es sind noch keine Ressourcen vorhanden, jetzt hochladen", + "this-operation-removes-the-associated-label-and-cannot-be-restored-please-confirm": "Bei diesem Vorgang wird der zugehörige Tag entfernt und kann nicht wiederhergestellt werden.", + "this-operation-will-be-delete-resource-are-you-sure": "Bei diesem Vorgang wird die Ressource gelöscht und kann nicht wiederhergestellt werden.", + "top": "Anheften", "total": "Insgesamt", - "update-successfully": "Erfolgreich aktualisieren", + "update-successfully": "Erfolgreich aktualisiert", "upload-file": "Datei hochladen", "use-ai": "Ai verwenden", "user-custom-openai-api-key": "Benutzerdefinierter OpenAI Api-Schlüssel", @@ -90,82 +92,82 @@ "user-custom-azureopenai-api-deployment": "Azure OpenAI Bereitstellungsname", "user-custom-azureopenai-api-version": "API version", "user-or-password-error": "Benutzer- oder Passwortfehler", - "username": "Nutzername", - "your-changes-have-been-saved": "Ihre Änderungen wurden gespeichert!", - "confirm-your-password": "Bestätigen Sie Ihr Passwort", - "confrim": "Bestätigen Sie", + "username": "Benutzername", + "your-changes-have-been-saved": "Änderungen wurden gespeichert!", + "confirm-your-password": "Passwort bestätigen", + "confrim": "Bestätigen", "every-day": "Jeden Tag", "every-three-month": "Alle drei Monate", "every-week": "Jede Woche", - "insert-sandpack": "Sandpäckchen einlegen", + "insert-sandpack": "Code-Sandbox einfügen", "insert-table": "Tabelle einfügen", "schedule": "SCHEDULE", "ai-model": "AI-Modell", - "confirm-to-delete": "Bestätigen Sie die Löschung!", + "confirm-to-delete": "Löschen bestätigen!", "congratulations-youve-reviewed-everything-today": "Sie haben heute alles durchgesehen.", - "detail": "Einzelheiten", - "enter-send-shift-enter-for-new-line": "Enter senden, Shift+Enter für neue Zeile", + "detail": "Details", + "enter-send-shift-enter-for-new-line": "Enter zum Senden, Shift+Enter für neue Zeile", "in-progress": "In Arbeit...", - "insert-codeblock": "CodeBlock einfügen", - "keep-sign-in": "Anmeldung beibehalten", - "last-run": "LETZTER LAUF", + "insert-codeblock": "Code-Block einfügen", + "keep-sign-in": "Angemeldet bleiben", + "last-run": "ZULETZT GESTARTET", "model-provider": "Modell-Anbieter", "must-start-with-http-s-or-use-api-openai-as-default": "Muss mit http(s):// beginnen oder /api/openai als Standard verwenden", - "recovery": "Erholung", + "recovery": "Wiederherstellen", "reviewed": "Überprüft", "created-in": "Erstellt in", - "set-as-public": "Als öffentlich festlegen", - "unset-as-public": "Nicht als öffentlich gekennzeichnet", - "no-tag": "Keine Markierung", - "with-link": "Mit Link", - "has-file": "Hat Datei", + "set-as-public": "Veröffentlichen", + "unset-as-public": "Nicht mehr veröffentlichen", + "no-tag": "Kein Tag", + "with-link": "Link vorhanden", + "has-file": "Datei vorhanden", "created-at": "Erstellen am", "role": "Rolle", "create-user": "Benutzer erstellen", "action": "Aktion", "original-password": "Original-Passwort", "import-from-memos-memos_prod-db": "Import aus Memos(memos_prod.db)", - "when-exporting-memos_prod-db-please-close-the-memos-container-to-avoid-partial-loss-of-data": "Wenn Sie memos_prod.db exportieren, schließen Sie bitte den Memos-Container, um einen teilweisen Datenverlust zu vermeiden.", + "when-exporting-memos_prod-db-please-close-the-memos-container-to-avoid-partial-loss-of-data": "Wenn Sie memos_prod.db exportieren, beenden Sie bitte den Memos-Container, um einen teilweisen Datenverlust zu vermeiden.", "go-to-share-page": "Zur Freigabeseite gehen", "import-done": "Import abgeschlossen", - "rebuilding-embedding-progress": "Wiederaufbau Einbettung des Fortschritts", - "rebuild": "Wiederherstellen", - "notes-imported-by-other-means-may-not-have-embedded-vectors": "Auf anderem Wege importierte Noten haben möglicherweise keine eingebetteten Vektoren", - "if-you-have-a-lot-of-notes-you-may-consume-a-certain-number-of-tokens": "Wenn Sie viele Scheine haben, können Sie eine bestimmte Anzahl von Token verbrauchen.", + "rebuilding-embedding-progress": "Fortschritt Neuaufbau der Einbettungsvektoren", + "rebuild": "Neu aufbauen", + "notes-imported-by-other-means-may-not-have-embedded-vectors": "Auf anderem Weg importierte Noten haben möglicherweise keine eingebetteten Vektoren", + "if-you-have-a-lot-of-notes-you-may-consume-a-certain-number-of-tokens": "Wenn viele Notizen vorhanden sind, kann eine große Anzahl von Token verbrauchen werden.", "time-format": "Zeitformat", "version": "Version", "new-version-available": "Neue Version verfügbar", - "storage": "Lagerung", + "storage": "Speicher", "local-file-system": "Lokales Dateisystem", "object-storage": "Speicherung von Objekten", - "in-addition-to-the-gpt-model-there-is-a-need-to-ensure-that-it-is-possible-to-invoke-the": "Zusätzlich zum GPT-Modell muss sichergestellt werden, dass es möglich ist, die", + "in-addition-to-the-gpt-model-there-is-a-need-to-ensure-that-it-is-possible-to-invoke-the": "Zusätzlich zum GPT-Modell muss sichergestellt werden, dass folgende Einbettung aktiv ist", "speech-recognition-requires-the-use-of": "Die Spracherkennung erfordert die Verwendung von", - "ai-expand": "AI Erweitern", - "ai-polish": "AI Polnisch", + "ai-expand": "AI Text erweitern", + "ai-polish": "AI Text verbessern", "accept": "Akzeptieren", "reject": "Ablehnen", "stop": "Stopp", - "card-columns": "Kartenspalten", - "select-a-columns": "Wählen Sie eine Spalte", - "width-less-than-1024px": "Breite weniger als 1024px", - "width-less-than": "Breite weniger als", - "small-device-card-columns": "Spalten für kleine Gerätekarten", - "medium-device-card-columns": "Medium-Gerätekarten-Spalten", - "large-device-card-columns": "Große Gerätekartenspalten", - "device-card-columns": "Spalten der Gerätekarte", - "columns-for-different-devices": "Säulen für verschiedene Geräte", + "card-columns": "Karten Spalten", + "select-a-columns": "Spalte auswählen", + "width-less-than-1024px": "Breite kleiner als 1024px", + "width-less-than": "Breite kleiner als", + "small-device-card-columns": "Spalten für kleine Bildschirme", + "medium-device-card-columns": "Spalten für mittlere Bildschirme", + "large-device-card-columns": "Spalten für große Bildschirme", + "device-card-columns": "Spaltenanzahl der Geräte", + "columns-for-different-devices": "Anzahl der Spalten für verschiedene Geräte", "mobile": "Mobil", - "tablet": "Tablette", - "desktop": "Schreibtisch", + "tablet": "Tablet", + "desktop": "Desktop", "chars": "Zeichen", - "text-fold-length": "Text Falzlänge", + "text-fold-length": "Maximal ungekürzte Textlänge", "title-first-line-of-the-text": "Titel (erste Zeile des Textes)", - "content-rest-of-the-text-if-the-text-is-longer-than-the-length": "Inhalt (Rest des Textes, wenn der Text länger als die Länge ist)", + "content-rest-of-the-text-if-the-text-is-longer-than-the-length": "Inhalt (Rest des Textes, wenn der Text länger als die maximale Länge ist)", "ai-tag": "AI-Tag", "article": "Artikel", "embedding-model": "Einbettungsmodell", - "force-rebuild": "Force Rebuild", - "force-rebuild-embedding-index": "Erzwungener Neuaufbau stellt alle Daten wieder her, die vollständig indiziert wurden.", + "force-rebuild": "Neuaufbau erzwingen", + "force-rebuild-embedding-index": "Ein erzwungener Neuaufbau stellt alle Daten wieder her, die vollständig indiziert wurden.", "embedding-model-description": "Der Index muss nach einem Wechsel des eingebetteten Modells neu aufgebaut werden", "top-k-description": "Maximale Anzahl von Dokumenten, die zurückgegeben werden", "embedding-score-description": "Die Ähnlichkeitsschwelle für Abfragen ist im Allgemeinen der euklidische Summenabstand", @@ -174,13 +176,13 @@ "delete-only-tag": "Nur Tag löschen", "delete-tag-with-note": "Tag mit Notiz löschen", "update-tag-name": "Tag-Name aktualisieren", - "thinking": "Ich denke...", + "thinking": "Denke...", "select-all": "Alle auswählen", "insert-before": "Einfügen vor", "insert-after": "Einfügen nach", - "update-name": "Update Name", + "update-name": "Name aktualisieren", "ai-emoji": "Ai-Emoji", - "custom-icon": "Benutzerdefinierte Ikone", + "custom-icon": "Benutzerdefiniertes Icon", "ai-enhanced-search": "AI-erweiterte Suche", "preview-mode": "Vorschau-Modus", "source-code": "Quellcode", @@ -191,38 +193,38 @@ "heading": "Überschrift", "paragraph": "Absatz", "quote": "Zitat", - "bold": "Kühn", - "remove-italic": "Kursivschrift entfernen", - "underline": "Unterstreichen Sie", + "bold": "Fett", + "remove-italic": "Kursiv entfernen", + "underline": "Unterstreichen", "italic": "Kursiv", - "remove-bold": "Fettdruck entfernen", + "remove-bold": "Fett entfernen", "remove-underline": "Unterstrich entfernen", "select-block-type": "Blocktyp auswählen", "block-type-select-placeholder": "Block Typ", - "trash": "Müll", + "trash": "Papierkorb", "page-size": "Größe der Seite", "toolbar-visibility": "Sichtbarkeit der Symbolleiste", "always-hide-toolbar": "Immer verstecken", "always-show-toolbar": "Immer anzeigen", "hide-toolbar-on-mobile": "Auf dem Handy ausblenden", "select-toolbar-visibility": "Sichtbarkeit der Symbolleiste auswählen", - "select-a-time-format": "Wählen Sie ein Zeitformat", + "select-a-time-format": "Zeitformat auswählen", "enter-code-shown-on-authenticator-app": "Code eingeben, der in der Authenticator-App angezeigt wird", "open-your-third-party-authentication-app-and-enter-the-codeshown-on-screen": "Öffnen Sie die Authentifizierungs-App eines Drittanbieters und geben Sie die auf dem Bildschirm angezeigten Codes ein.", "two-factor-authentication": "Zwei-Faktoren-Authentifizierung", "scan-this-qr-code-with-your-authenticator-app": "Scannen Sie diesen QR-Code mit Ihrer Authenticator-App", "or-enter-this-code-manually": "Oder geben Sie diesen Code manuell ein:", - "verify": "Überprüfen Sie", + "verify": "Überprüfen", "about": "Über", "upload": "Hochladen", "days": "Tage", "select-model-provider": "Modellanbieter auswählen", - "allow-register": "Register zulassen", + "allow-register": "Benutzerregisterungen zulassen", "access-token": "Zugangs-Token", - "bucket": "Eimer", + "bucket": "Bucket", "region": "Region", - "access-key-secret": "Zugangsschlüssel geheim", - "access-key-id": "Zugangsschlüssel id", + "access-key-secret": "Geheimer Zugangsschlüssel", + "access-key-id": "ID Zugangsschlüssel", "share-and-copy-link": "Link teilen und kopieren", "copy-share-link": "Link zur Freigabe kopieren", "endpoint": "Endpunkt", @@ -231,8 +233,8 @@ "time-range": "Zeitspanne", "all": "Alle", "exporting": "Exportieren...", - "has-image": "Hat Image", - "has-link": "Hat Link", + "has-image": "Bild vorhanden", + "has-link": "Link vorhanden", "filter-settings": "Filter-Einstellungen", "tag-status": "Tag Status", "all-notes": "Alle Anmerkungen", @@ -242,7 +244,7 @@ "additional-conditions": "Zusätzliche Bedingungen", "apply-filter": "Filter anwenden", "to": "An", - "start-date": "Datum des Beginns", + "start-date": "Startdatum", "end-date": "Enddatum", "reset": "Zurücksetzen", "no-condition": "Keine Bedingung", @@ -271,20 +273,20 @@ "column": "Spalte", "content-theme": "Vorschau des Inhaltsdesigns", "dark-mode": "Dunkelmodus", - "delete-column": "Zeile löschen", - "delete-row": "Spalte löschen", + "delete-column": "Spalte löschen", + "delete-row": "Zeile löschen", "down": "Nach unten", "download-tip": "Der Browser unterstützt die Download-Funktion nicht.", "edit-mode": "Umschalten in den Bearbeitungsmodus", "edit-user": "Benutzer bearbeiten", "emoji": "Emoji\n\nEmoji", - "exclude-tag-from-embedding": "Schließen Sie markierten Inhalt aus.", - "exclude-tag-from-embedding-desc": "Wählen Sie ein Tag aus, um die zugehörigen Notizen von der Generierung des KI-Einbettungsvektors auszuschließen.", + "exclude-tag-from-embedding": "Markierten Inhalt ausschließen", + "exclude-tag-from-embedding-desc": "Tag auswählen, um die zugehörigen Notizen von der Generierung des KI-Einbettungsvektors auszuschließen.", "exclude-tag-from-embedding-tip": "Notizen mit diesem Tag werden von der KI-Einbettungsverarbeitung ausgeschlossen.", "file-type-error": "Dateityp ist fehlerhaft", "follow-system": "Folgen Sie dem System.", "footnote-ref": "Fußnotenverweis", - "fullscreen": "Vollbild umschalten", + "fullscreen": "Vollbild anzeigen", "generate": "Generieren", "heading1": "Überschrift 1", "heading2": "Überschrift 2", @@ -298,38 +300,61 @@ "indent": "Einzug", "info": "Informationen", "inline-code": "Inline-Code", - "insert-column-left": "Fügen Sie 1 links ein.", - "insert-column-right": "Fügen Sie 1 richtig ein.", - "insert-row-above": "Fügen Sie 1 oben ein", - "insert-row-below": "Fügen Sie unten die 1 ein.", + "insert-column-left": "Spalte links einfügen", + "insert-column-right": "Spalte rechts einfügen", + "insert-row-above": "Zeile oben einfügen", + "insert-row-below": "Zeile unten einfügen", "instant-rendering": "Echtzeit-Rendering", "light-mode": "Hellmodus", "line": "Linie", - "link": "Verbindung", - "link-ref": "Link-Verweis", + "link": "Verknüpfung", + "link-ref": "Verweis", "list": "Liste", "more": "Mehr", "name-empty": "Der Name ist leer", "order-by-create-time": "Sortieren nach Erstellungszeit", - "ordered-list": "Bestellliste", + "ordered-list": "Numerierte Liste", "outdent": "Ausrücken", "outline": "Gliederung", "over": "über", "performance-tip": "Echtzeitvorschau benötigt ${x}ms, du kannst sie schließen.", "preview": "Vorschau", - "rebuild-embedding-index": "Erstellen Sie den Einbettungsindex neu", - "record": "Startaufnahme/Endaufnahme", + "rebuild-embedding-index": "Einbettungsindex neu erstellen", + "record": "Start-/Endaufnahme", "record-tip": "Das Gerät unterstützt keine Aufzeichnung.", - "redo": "Neu machen", + "redo": "Wiederholen", "remove": "Entfernen", - "row": "Reihe", + "row": "Zeile", "spin": "Drehen", "split-view": "geteilte Ansicht", - "strike": "Schlag", - "table": "Tisch", + "strike": "Durchgestrichen", + "table": "Tabelle", "search-tags": "Suchbegriffe", "insert-attachment-or-note": "In Anhang oder Notiz einfügen?", "context": "Kontext", "paste-to-note-or-attachment": "Sind Sie sicher, dass Sie in den Kontext oder als Anhang einfügen möchten?", - "attachment": "Anhang" + "attachment": "Anhang", + "after-deletion-all-user-data-will-be-cleared-and-unrecoverable": "Nach dem Löschen werden alle Benutzerdaten gelöscht und können nicht wiederhergestellt werden.", + "upload-completed": "Hochladen abgeschlossen", + "upload-cancelled": "Hochladen abgebrochen", + "upload-failed": "Hochladen fehlgeschlagen", + "import-from-bko-tip": "Das Hochladen zur Wiederherstellung auf S3 wird derzeit nicht unterstützt. Deaktivieren Sie die S3-Option vorübergehend, wenn Sie eine Wiederherstellung durchführen möchten.", + "edit-time": "Bearbeitungszeit", + "ai-write": "AI Write\n\nAI schreiben", + "download": "Herunterladen", + "rename": "Umbenennen", + "move-up": "Nach oben verschieben", + "cut": "Schneiden", + "paste": "Einfügen", + "confirm-delete": "Bestätigen Löschen", + "confirm-delete-content": "Sind Sie sicher, dass Sie {{name}} löschen möchten? Diese Aktion kann nicht rückgängig gemacht werden.", + "folder-name": "Ordnername", + "file-name": "Dateiname", + "operation-success": "Operation erfolgreich", + "cloud-file": "Cloud-Datei", + "move-to-parent": "Zum übergeordneten Element wechseln", + "no-resources-found": "Keine Ressourcen gefunden", + "new-folder": "Neuer Ordner", + "folder-name-exists": "Der Ordnername existiert.", + "folder-name-required": "Ordnername erforderlich" } diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json index 8e87dcd1..e1755185 100644 --- a/public/locales/en/translation.json +++ b/public/locales/en/translation.json @@ -342,5 +342,36 @@ "insert-attachment-or-note": "Insert to attachment or note?", "paste-to-note-or-attachment": "Are you sure to paste to context or attachment? ", "context": "Context", - "attachment": "Attachment" + "attachment": "Attachment", + "after-deletion-all-user-data-will-be-cleared-and-unrecoverable": "After deletion, all user data will be cleared and unrecoverable.", + "upload-completed": "Upload completed", + "upload-cancelled": "Upload cancelled", + "upload-failed": "Upload Failed", + "import-from-bko-tip": "Uploading to s3 for recovery is not supported at this time. Please disable the s3 option temporarily when you want to recover.", + "music-settings": "Music Settings", + "spotify-consumer-key": "Spotify API Key", + "spotify-consumer-secret": "Spotify API Secret", + "enter-spotify-consumer-key": "Enter Spotify API Key", + "enter-spotify-consumer-secret": "Enter Spotify Consumer Secret", + "spotify-consumer-key-tip": "Used to get mp3 music cover", + "spotify-consumer-key-tip-2": "Get API Key from https://developer.spotify.com/", + "edit-time": "Edit Time", + "ai-write": "AI Write", + "download": "Download", + "rename": "Rename", + "move-up": "Move Up", + "cut": "Cut", + "paste": "Paste", + "confirm-delete-content": "Are you sure you want to delete {{name}}? This action cannot be undone.", + "confirm-delete": "Confirm Delete", + "folder-name": "Folder Name", + "file-name": "File Name", + "operation-success": "Operation Successful", + "cloud-file": "Cloud File", + "move-to-parent": "Move To Parent", + "no-resources-found": "No Resources Found", + "operation-in-progress": "Opearation in progress", + "new-folder": "New Folder", + "folder-name-required": "Folder Name Required", + "folder-name-exists": "Folder Name Exists" } diff --git a/public/locales/es/translation.json b/public/locales/es/translation.json index 18131fcf..7f728fd0 100644 --- a/public/locales/es/translation.json +++ b/public/locales/es/translation.json @@ -1,4 +1,6 @@ { + "spotify-consumer-key-tip": "Solía obtener portadas de música mp3.", + "spotify-consumer-key-tip-2": "Obtén la clave de API en https://developer.spotify.com/", "blinko": "Blinko", "notes": "Notas", "resources": "Recursos", @@ -322,5 +324,29 @@ "insert-attachment-or-note": "¿Insertar en el archivo adjunto o en la nota?", "context": "Contexto", "paste-to-note-or-attachment": "¿Estás seguro de pegar en el contexto o adjuntar?", - "attachment": "Adjunto" + "attachment": "Adjunto", + "after-deletion-all-user-data-will-be-cleared-and-unrecoverable": "Después de la eliminación, todos los datos de usuario serán borrados y no se podrán recuperar.", + "upload-completed": "Carga completada", + "upload-cancelled": "Subida cancelada", + "upload-failed": "Error al subir", + "import-from-bko-tip": "La carga a s3 para recuperación no está soportada en este momento. Por favor, deshabilite temporalmente la opción s3 cuando desee recuperar.", + "edit-time": "Tiempo de edición", + "ai-write": "IA Escribir", + "download": "Descargar", + "rename": "Rebautizar", + "move-up": "Subir", + "cut": "Cortar", + "paste": "Pegar", + "confirm-delete": "Confirmar Eliminar", + "confirm-delete-content": "¿Estás seguro de que deseas eliminar {{name}}? Esta acción no se puede deshacer.", + "folder-name": "Nombre de la carpeta", + "file-name": "Nombre del archivo", + "operation-success": "Operación exitosa", + "cloud-file": "Archivo en la nube", + "move-to-parent": "Mover al padre", + "no-resources-found": "No se encontraron recursos.", + "operation-in-progress": "Operación en progreso", + "new-folder": "Nueva Carpeta", + "folder-name-exists": "Nombre de carpeta existente", + "folder-name-required": "Nombre de carpeta requerido" } diff --git a/public/locales/fr/translation.json b/public/locales/fr/translation.json index 9bae94d6..c61a97d5 100644 --- a/public/locales/fr/translation.json +++ b/public/locales/fr/translation.json @@ -1,4 +1,6 @@ { + "spotify-consumer-key-tip": "Utilisé pour obtenir la couverture de musique mp3", + "spotify-consumer-key-tip-2": "Obtenez la clé API à partir de https://developer.spotify.com/", "blinko": "Blinko", "notes": "Notes", "resources": "Ressources", @@ -319,5 +321,29 @@ "insert-attachment-or-note": "Insérer dans la pièce jointe ou la note ?", "context": "Contexte", "paste-to-note-or-attachment": "Êtes-vous sûr de coller dans le contexte ou en pièce jointe ?", - "attachment": "Pièce jointe" + "attachment": "Pièce jointe", + "after-deletion-all-user-data-will-be-cleared-and-unrecoverable": "Après la suppression, toutes les données utilisateur seront effacées et définitivement irrécupérables.", + "upload-completed": "Téléchargement terminé", + "upload-cancelled": "Téléchargement annulé", + "upload-failed": "Téléchargement échoué", + "import-from-bko-tip": "Le téléversement vers s3 pour la récupération n'est pas pris en charge actuellement. Veuillez désactiver temporairement l'option s3 lorsque vous souhaitez récupérer.", + "edit-time": "Modifier l'heure", + "ai-write": "Écrire en IA", + "download": "Télécharger", + "rename": "Renommer", + "move-up": "Monter", + "cut": "Couper", + "paste": "Coller", + "confirm-delete": "Confirmer la suppression", + "confirm-delete-content": "Êtes-vous sûr de vouloir supprimer {{name}} ? Cette action est irréversible.", + "folder-name": "Nom du dossier", + "file-name": "Nom du fichier", + "operation-success": "Opération réussie", + "cloud-file": "Fichier en nuage", + "move-to-parent": "Déplacer vers le parent", + "no-resources-found": "Aucune ressource trouvée", + "operation-in-progress": "Opération en cours", + "new-folder": "Nouveau Dossier", + "folder-name-exists": "Le nom du dossier existe", + "folder-name-required": "Nom du dossier requis" } diff --git a/public/locales/ja/translation.json b/public/locales/ja/translation.json index 53f7d576..98fe913d 100644 --- a/public/locales/ja/translation.json +++ b/public/locales/ja/translation.json @@ -1,4 +1,6 @@ { + "spotify-consumer-key-tip": "mp3音楽のカバーを取得するために使用されました", + "spotify-consumer-key-tip-2": "https://developer.spotify.com/ から API キーを取得します。", "blinko": "ブリンコ", "notes": "備考", "resources": "リソース", @@ -304,5 +306,29 @@ "insert-attachment-or-note": "添付ファイルに挿入しますか、それともメモに書き込みますか?", "context": "コンテクスト", "paste-to-note-or-attachment": "コンテキストや添付ファイルに貼り付けることを確認しますか?", - "attachment": "添付" + "attachment": "添付", + "after-deletion-all-user-data-will-be-cleared-and-unrecoverable": "削除後、すべてのユーザーデータは消去され、回復不能になります。", + "upload-completed": "アップロードが完了しました", + "upload-cancelled": "アップロードがキャンセルされました", + "upload-failed": "アップロードに失敗しました", + "import-from-bko-tip": "この時点では、S3 へのアップロードによるリカバリはサポートされていません。リカバリを行う際には一時的に S3 オプションを無効にしてください。", + "edit-time": "編集時間", + "ai-write": "AIライト", + "download": "ダウンロードします。", + "rename": "名前を変更", + "move-up": "上に移動する", + "cut": "切る", + "paste": "ペースト", + "confirm-delete": "削除を確認します。", + "confirm-delete-content": "「{{name}}」を削除しますか?この操作は取り消すことができません。", + "folder-name": "フォルダー名", + "file-name": "ファイル名", + "operation-success": "手術が成功しました", + "cloud-file": "クラウドファイル", + "move-to-parent": "親に移動", + "no-resources-found": "リソースが見つかりません", + "operation-in-progress": "進行中の操作", + "new-folder": "新しいフォルダ", + "folder-name-exists": "フォルダ名が既に存在します", + "folder-name-required": "フォルダ名が必要です。" } diff --git a/public/locales/ko/translation.json b/public/locales/ko/translation.json index 0bc94a16..c0abacd4 100644 --- a/public/locales/ko/translation.json +++ b/public/locales/ko/translation.json @@ -1,4 +1,7 @@ { + "enter-spotify-consumer-key": "Spotify 소비자 키를 입력하세요.", + "spotify-consumer-key-tip": "mp3 음악 커버를 얻었던 것", + "spotify-consumer-key-tip-2": "https://developer.spotify.com/에서 API 키를 받으세요.", "blinko": "Blinko", "notes": "참고", "resources": "리소스", @@ -326,5 +329,29 @@ "insert-attachment-or-note": "첨부 파일이나 메모로 삽입하시겠습니까?", "context": "문맥", "paste-to-note-or-attachment": "컨텍스트나 첨부 파일에 붙여 넣을 확신이 있습니까?", - "attachment": "첨부 파일" + "attachment": "첨부 파일", + "after-deletion-all-user-data-will-be-cleared-and-unrecoverable": "삭제 후 모든 사용자 데이터가 지워지고 복구할 수 없게 됩니다.", + "upload-completed": "업로드가 완료되었습니다.", + "upload-cancelled": "업로드가 취소되었습니다.", + "upload-failed": "업로드 실패", + "import-from-bko-tip": "현재 s3로의 업로드는 복구되지 않습니다. 복구를 원할 경우 s3 옵션을 일시적으로 비활성화하십시오.", + "edit-time": "편집 시간", + "ai-write": "인공지능 작성", + "download": "다운로드", + "rename": "이름 바꾸기", + "move-up": "위로 이동", + "cut": "잘라 내다", + "paste": "붙여 넣다", + "confirm-delete": "삭제 확인", + "confirm-delete-content": "{{name}}을(를) 삭제하시겠습니까? 이 작업은 되돌릴 수 없습니다.", + "folder-name": "폴더 이름", + "file-name": "파일 이름", + "operation-success": "작업 성공적입니다.", + "cloud-file": "클라우드 파일", + "move-to-parent": "부모로 이동", + "no-resources-found": "자원을 찾을 수 없습니다", + "operation-in-progress": "진행 중인 작업", + "new-folder": "새 폴더", + "folder-name-exists": "폴더 이름이 이미 있습니다.", + "folder-name-required": "폴더 이름 필요함" } diff --git a/public/locales/pt/translation.json b/public/locales/pt/translation.json index d5788ccb..12674646 100644 --- a/public/locales/pt/translation.json +++ b/public/locales/pt/translation.json @@ -1,4 +1,7 @@ { + "enter-spotify-consumer-key": "Insira a Chave do Consumidor do Spotify", + "spotify-consumer-key-tip": "Costumava obter a capa de músicas em mp3.", + "spotify-consumer-key-tip-2": "Obtenha a Chave da API em https://developer.spotify.com/", "blinko": "Blinko", "notes": "Notas", "resources": "Recursos", @@ -254,5 +257,29 @@ "insert-attachment-or-note": "Inserir em anexo ou nota?", "context": "Contexto", "paste-to-note-or-attachment": "Tem certeza de que deseja colar no contexto ou no anexo?", - "attachment": "Anexo" + "attachment": "Anexo", + "after-deletion-all-user-data-will-be-cleared-and-unrecoverable": "Após a exclusão, todos os dados do usuário serão apagados e não poderão ser recuperados.", + "upload-completed": "Carregamento concluído", + "upload-cancelled": "Envio cancelado", + "upload-failed": "Falha ao Carregar", + "import-from-bko-tip": "O envio para o s3 para recuperação não é suportado neste momento. Por favor, desative temporariamente a opção s3 quando desejar recuperar.", + "edit-time": "Tempo de Edição", + "ai-write": "IA Escreva", + "download": "Baixar", + "rename": "Renomear", + "move-up": "Subir", + "cut": "Corte", + "paste": "Colar", + "confirm-delete": "Confirmar Excluir", + "confirm-delete-content": "Tem certeza de que deseja excluir {{name}}? Essa ação não pode ser desfeita.", + "folder-name": "Nome da Pasta", + "file-name": "Nome do arquivo", + "operation-success": "Operação bem-sucedida", + "cloud-file": "Arquivo na Nuvem", + "move-to-parent": "Mover Para o Pai", + "no-resources-found": "Recursos não encontrados", + "operation-in-progress": "Operação em andamento", + "new-folder": "Nova Pasta", + "folder-name-exists": "Nome da Pasta Já Existe", + "folder-name-required": "Nome da pasta obrigatório" } diff --git a/public/locales/ru/translation.json b/public/locales/ru/translation.json index e8205df4..18aab49b 100644 --- a/public/locales/ru/translation.json +++ b/public/locales/ru/translation.json @@ -259,5 +259,35 @@ "insert-attachment-or-note": "Вставить во вложение или заметку?", "context": "Контекст", "paste-to-note-or-attachment": "Вы уверены, что хотите вставить в контекст или прикрепление?", - "attachment": "Прикрепление" + "attachment": "Прикрепление", + "after-deletion-all-user-data-will-be-cleared-and-unrecoverable": "После удаления все данные пользователей будут очищены и не поддадутся восстановлению.", + "upload-completed": "Загрузка завершена", + "upload-cancelled": "Загрузка отменена", + "upload-failed": "Не удалось загрузить", + "import-from-bko-tip": "Загрузка в s3 для восстановления в настоящее время не поддерживается. Пожалуйста, временно отключите опцию s3, когда планируете выполнить восстановление.", + "music-settings": "Настройки музыки", + "enter-spotify-consumer-secret": "Введите секрет потребителя Spotify", + "spotify-consumer-secret": "Spotify Секрет потребителя", + "enter-spotify-consumer-key": "Введите ключ потребителя Spotify", + "spotify-consumer-key-tip": "Использовались для получения обложек музыкальных альбомов в формате mp3.", + "spotify-consumer-key-tip-2": "Получите ключ API на https://developer.spotify.com/", + "edit-time": "Время редактирования", + "ai-write": "Искусственный интеллект.", + "download": "Скачать", + "rename": "Переименовать", + "move-up": "Двигаться вверх", + "cut": "Порезать", + "paste": "Вставить", + "confirm-delete": "Подтвердить удаление", + "confirm-delete-content": "Вы уверены, что хотите удалить {{name}}? Это действие нельзя отменить.", + "folder-name": "Имя папки", + "file-name": "Имя файла", + "operation-success": "Операция успешно выполнена", + "cloud-file": "Облачный файл", + "move-to-parent": "Переместиться к родительскому элементу", + "no-resources-found": "Ресурсы не найдены", + "operation-in-progress": "Операция в процессе", + "new-folder": "Новая папка", + "folder-name-exists": "Имя папки существует", + "folder-name-required": "Название папки обязательно" } diff --git a/public/locales/tr/translation.json b/public/locales/tr/translation.json index d6141f25..52a41a86 100644 --- a/public/locales/tr/translation.json +++ b/public/locales/tr/translation.json @@ -1,4 +1,7 @@ { + "enter-spotify-consumer-key": "Spotify Tüketici Anahtarı Giriniz", + "spotify-consumer-key-tip": "Mp3 müzik kapağı almak için kullanılan.", + "spotify-consumer-key-tip-2": "https://developer.spotify.com/ adresinden API Anahtarı alın.", "blinko": "Blinko", "notes": "Notlar", "resources": "Kaynaklar", @@ -272,5 +275,29 @@ "insert-attachment-or-note": "Eklentiye mi yoksa notlara mı ekleyeceksiniz?", "context": "Bağlam", "paste-to-note-or-attachment": "Metin veya ek dosyaya yapıştırmak istediğinizden emin misiniz?", - "attachment": "Eklenti" + "attachment": "Eklenti", + "after-deletion-all-user-data-will-be-cleared-and-unrecoverable": "Silme işleminden sonra, tüm kullanıcı verileri temizlenecek ve kurtarılamaz hale gelecektir.", + "upload-completed": "Yükleme tamamlandı", + "upload-cancelled": "Yükleme iptal edildi", + "upload-failed": "Yükleme Başarısız oldu", + "import-from-bko-tip": "Bu zamanda s3'e yükleme yapılmamaktadır. Kurtarma işlemi gerçekleştirmek istediğinizde lütfen geçici olarak s3 seçeneğini devre dışı bırakın.", + "edit-time": "Zamanı Düzenle", + "ai-write": "Yapay Zeka Yazısı", + "download": "İndir", + "rename": "Yeniden adlandırma", + "move-up": "Yukarı taşı.", + "cut": "kesmek", + "paste": "Yapıştır", + "confirm-delete": "Silme işlemini onayla", + "confirm-delete-content": "{{name}}'i silmek istediğinizden emin misiniz? Bu işlem geri alınamaz.", + "folder-name": "Klasör Adı", + "file-name": "Dosya Adı", + "operation-success": "Operasyon Başarılı", + "cloud-file": "Bulut Dosyası", + "move-to-parent": "Ebeveyn Düzeyine Taşı", + "no-resources-found": "Kaynak Bulunamadı", + "operation-in-progress": "Devam eden işlem", + "new-folder": "Yeni Klasör", + "folder-name-exists": "Klasör Adı Mevcut", + "folder-name-required": "Klasör Adı Gerekli" } diff --git a/public/locales/zh-TW/translation.json b/public/locales/zh-TW/translation.json index e74d5981..267303ba 100644 --- a/public/locales/zh-TW/translation.json +++ b/public/locales/zh-TW/translation.json @@ -317,7 +317,7 @@ "insert-row-above": "插入上文 1", "insert-row-below": "下面插入 1", "instant-rendering": "即时渲染", - "light-mode": "灯光模式", + "light-mode": "浅色模式", "line": "线路", "link": "链接", "link-ref": "链接参考", @@ -356,5 +356,35 @@ "insert-attachment-or-note": "插入至附件或備註?", "context": "正文", "paste-to-note-or-attachment": "您確定要貼上到內容或附件嗎?", - "attachment": "附件" + "attachment": "附件", + "after-deletion-all-user-data-will-be-cleared-and-unrecoverable": "刪除後,所有用戶資料將被清空且無法恢復。", + "upload-completed": "上傳完成", + "upload-cancelled": "上傳已取消", + "upload-failed": "上傳失敗", + "import-from-bko-tip": "目前不支援上傳至 s3 進行還原。當您想要進行恢復操作時,請暫時停用 s3 選項。", + "music-settings": "音樂設定", + "enter-spotify-consumer-secret": "輸入 Spotify 用戶秘密", + "spotify-consumer-secret": "Spotify 用戶端秘鑰", + "enter-spotify-consumer-key": "輸入 Spotify 使用者金鑰", + "spotify-consumer-key-tip": "以前用來取得 MP3 音樂封面", + "spotify-consumer-key-tip-2": "從 https://developer.spotify.com/ 取得 API 金鑰", + "edit-time": "編輯時間", + "ai-write": "AI 寫", + "download": "下載", + "rename": "重新命名", + "move-up": "提升", + "cut": "剪辑", + "paste": "貼上", + "confirm-delete": "確認刪除", + "confirm-delete-content": "您確定要刪除{{name}}嗎?此操作無法撤銷。", + "folder-name": "資料夾名稱", + "file-name": "檔案名稱", + "operation-success": "操作成功", + "cloud-file": "雲端檔案", + "move-to-parent": "移至上層目錄", + "no-resources-found": "找不到資源", + "operation-in-progress": "正在進行操作", + "new-folder": "新資料夾", + "folder-name-required": "需要資料夾名稱", + "folder-name-exists": "資料夾名稱已存在" } diff --git a/public/locales/zh/translation.json b/public/locales/zh/translation.json index e5dc47b6..a2d05009 100644 --- a/public/locales/zh/translation.json +++ b/public/locales/zh/translation.json @@ -119,7 +119,7 @@ "with-link": "带链接", "has-file": "包含文件", "no-tag": "无标签", - "created-at": "在", + "created-at": "创建时间", "user-list": "用户列表", "action": "操作", "original-password": "原始密码", @@ -165,7 +165,7 @@ "ai-tag": "AI标签", "article": "文章", "embedding-model": "嵌入式模型", - "if-you-have-a-lot-of-notes-you-may-consume-a-certain-number-of-tokens": "如果您有大量笔记,您可能会消耗一定数量的代币", + "if-you-have-a-lot-of-notes-you-may-consume-a-certain-number-of-tokens": "如果您有大量笔记,可能会消耗一定数量的代币", "force-rebuild": "强制重建", "force-rebuild-embedding-index": "强制重建将完全重建已索引的所有数据", "embedding-model-description": "切换嵌入式模型后必须重建索引", @@ -192,7 +192,7 @@ "reference": "引用", "reference-note": "参考说明", "source-code-mode": "源码模式", - "heading": "标题", + "heading": "标��", "paragraph": "段落", "quote": "引用", "bold": "粗体", @@ -263,17 +263,6 @@ "ollama-ai-model-tooltip": "输入要使用的模型名称,如 llama3.2", "ollama-default-endpoint-is-http-localhost-11434": "Ollama 默认端点为 http://localhost:11434", "your-azure-openai-instance-name": "您的 Azure OpenAI 实例名称", - "api-key": "", - "days-ago": "", - "enter-your-api-key": "", - "hours-ago": "", - "impoort-from-bko": "", - "minutes-ago": "", - "months-ago": "", - "superadmin": "", - "user": "", - "weeks-ago": "", - "years-ago": "", "2fa-setup-successful": "2FA设置成功", "align-center": "中心", "align-left": "左", @@ -348,7 +337,7 @@ "undo": "撤销", "up": "向上", "update": "更新", - "updated-at": "更新于", + "updated-at": "更新时间", "upload-error": "上传错误", "uploading": "上传中...", "wysiwyg": "所见即所得", @@ -356,5 +345,36 @@ "insert-attachment-or-note": "插入附件还是笔记中?", "paste-to-note-or-attachment": "您确定要粘贴到上下文或附件吗?", "context": "正文", - "attachment": "附件" + "attachment": "附件", + "after-deletion-all-user-data-will-be-cleared-and-unrecoverable": "删除后,所有用户数据将被清除且无法恢复。", + "upload-completed": "上传完成", + "upload-cancelled": "上传已取消", + "upload-failed": "上传失败", + "import-from-bko-tip": "目前不支持将上传到S3进行恢复。当您想要恢复时,请暂时禁用S3选项。", + "music-settings": "音乐设置", + "spotify-consumer-key": "Spotify接口Key", + "spotify-consumer-secret": "Spotify接口密钥", + "enter-spotify-consumer-key": "输入 Spotify 接口 Key", + "enter-spotify-consumer-secret": "输入Spotify接口密钥", + "spotify-consumer-key-tip": "用来获得mp3音乐封面", + "spotify-consumer-key-tip-2": "从https://developer.spotify.com/获取API密钥。", + "edit-time": "编辑时间", + "ai-write": "AI写作", + "download": "下载", + "rename": "重命名", + "move-up": "向上移动", + "cut": "剪切", + "paste": "粘贴", + "confirm-delete": "确认删除", + "confirm-delete-content": "您确定要删除{{name}}吗?此操作无法撤销。", + "folder-name": "文件夹名称", + "file-name": "文件名", + "operation-success": "操作成功", + "cloud-file": "云文件", + "move-to-parent": "移至上级", + "no-resources-found": "未找到资源", + "operation-in-progress": "操作进行中", + "new-folder": "新建文件夹", + "folder-name-exists": "文件夹名称已存在", + "folder-name-required": "文件夹名称为必填项" } diff --git a/public/logo-dark.png b/public/logo-dark.png new file mode 100644 index 00000000..4499b94b Binary files /dev/null and b/public/logo-dark.png differ diff --git a/public/logo-dark.svg b/public/logo-dark.svg index 959342f9..2432674b 100644 --- a/public/logo-dark.svg +++ b/public/logo-dark.svg @@ -1 +1,112 @@ - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/logo-light.png b/public/logo-light.png new file mode 100644 index 00000000..4b2ec521 Binary files /dev/null and b/public/logo-light.png differ diff --git a/public/logo-light.svg b/public/logo-light.svg new file mode 100644 index 00000000..a1d4af99 --- /dev/null +++ b/public/logo-light.svg @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/logo.svg b/public/logo.svg index d1317ed0..11836e3a 100644 --- a/public/logo.svg +++ b/public/logo.svg @@ -1 +1,114 @@ - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/manifest.json b/public/manifest.json index 3a1031a8..829a33be 100644 --- a/public/manifest.json +++ b/public/manifest.json @@ -3,14 +3,29 @@ "short_name": "Blinko", "icons": [ { - "src": "/icon-192x192.png", - "sizes": "192x192", + "src": "/icons/icon-128x128.png", + "sizes": "128x128", "type": "image/png", "purpose": "any maskable" }, { - "src": "/icon-512x512.png", - "sizes": "512x512", + "src": "/icons/icon-144x144.png", + "sizes": "144x144", + "type": "image/png" + }, + { + "src": "/icons/icon-152x152.png", + "sizes": "152x152", + "type": "image/png" + }, + { + "src": "/icons/icon-192x192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "/icons/icon-256x256.png", + "sizes": "256x256", "type": "image/png" } ], diff --git a/public/single-logo.svg b/public/single-logo.svg deleted file mode 100644 index 40ba6c13..00000000 --- a/public/single-logo.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/components/BlinkoAi/index.tsx b/src/components/BlinkoAi/index.tsx index 132a609c..316ee267 100644 --- a/src/components/BlinkoAi/index.tsx +++ b/src/components/BlinkoAi/index.tsx @@ -8,17 +8,16 @@ import { motion } from "motion/react" import { AiStore } from "@/store/aiStore"; import { useEffect, useRef } from "react"; import { useMediaQuery } from "usehooks-ts"; -import { DialogStore } from "@/store/module/Dialog"; import { UserStore } from "@/store/user"; import { useTranslation } from "react-i18next"; import { ScrollArea, ScrollAreaHandles } from "../Common/ScrollArea"; import Link from "next/link"; -import DraggableDiv from "../Common/DragContainer"; import dayjs from "@/lib/dayjs"; import { FilesAttachmentRender } from "../Common/AttachmentRender"; import { ResizableWrapper } from "../Common/ResizableWrapper"; import { useRouter } from "next/router"; import { MarkdownRender } from "../Common/MarkdownRender"; +import { useIsIOS } from "@/lib/hooks"; export const BlinkoAiChat = observer(() => { const ai = RootStore.Get(AiStore) @@ -26,6 +25,7 @@ export const BlinkoAiChat = observer(() => { const router = useRouter() const scrollAreaRef = useRef(null); const { t } = useTranslation() + const isIOS = useIsIOS() useEffect(() => { scrollAreaRef.current?.scrollToBottom() }, [ai.scrollTicker]) @@ -35,7 +35,7 @@ export const BlinkoAiChat = observer(() => { onBottom={() => { }} ref={scrollAreaRef} key='BlinkoAiChat' - className={`mx-1 w-full flex-1`}> + className={`mx-1 w-full flex-1 ${isIOS ? 'max-h-[70vh]' : ''}`}> { ai.chatHistory.list.length == 0 &&
diff --git a/src/components/BlinkoMusicPlayer/index.tsx b/src/components/BlinkoMusicPlayer/index.tsx new file mode 100644 index 00000000..e9a78908 --- /dev/null +++ b/src/components/BlinkoMusicPlayer/index.tsx @@ -0,0 +1,283 @@ +import { observer } from 'mobx-react-lite'; +import { RootStore } from '@/store'; +import { MusicManagerStore } from '@/store/musicManagerStore'; +import { Icon } from '@iconify/react'; +import { useEffect, useRef, useState } from 'react'; +import { motion, AnimatePresence } from 'framer-motion'; +import { useMediaQuery } from 'usehooks-ts'; + +export const BlinkoMusicPlayer = observer(() => { + const musicManager = RootStore.Get(MusicManagerStore); + const isPc = useMediaQuery('(min-width: 768px)'); + const progressRef = useRef(null); + const currentTrack = musicManager.currentTrack; + const metadata = currentTrack?.metadata; + const [isCompact, setIsCompact] = useState(false); + + const formatTime = (seconds: number): string => { + const minutes = Math.floor(seconds / 60); + const remainingSeconds = Math.floor(seconds % 60); + return `${minutes}:${remainingSeconds.toString().padStart(2, '0')}`; + }; + + const handleProgressBarClick = (e: React.MouseEvent) => { + if (!musicManager.audioElement) return; + const progressBar = e.currentTarget; + const rect = progressBar.getBoundingClientRect(); + const x = e.clientX - rect.left; + const percentage = x / rect.width; + musicManager.seek(musicManager.duration * percentage); + }; + + useEffect(() => { + const updateProgress = () => { + if (!progressRef.current || !musicManager.audioElement) return; + const percentage = (musicManager.currentTime / musicManager.duration) * 100; + progressRef.current.style.width = `${percentage}%`; + }; + + const interval = setInterval(updateProgress, 100); + return () => clearInterval(interval); + }, [musicManager.currentTrack]); + + useEffect(() => { + if (musicManager.showMiniPlayer) { + const timer = setTimeout(() => { + setIsCompact(true); + }, 5000); + return () => clearTimeout(timer); + } else { + setIsCompact(false); + } + }, [musicManager.showMiniPlayer]); + + return ( + + {musicManager.showMiniPlayer && currentTrack && ( + setIsCompact(false)} + onMouseLeave={() => setIsCompact(true)} + > + + {metadata?.coverUrl ? ( + <> +
+
+ + ) : ( + <> +
+
+ + )} + +
+ + {metadata?.coverUrl ? ( + Album Cover + ) : ( +
+ +
+ )} +
+ +
+ + + + {metadata?.trackName || currentTrack.file.name} + + + {metadata?.artists && metadata.artists.join(', ')} + + + + + + + + + + + + + + {formatTime(musicManager.currentTime)} + +
+ +
+ + {formatTime(musicManager.duration)} + +
+
+
+ + + )} + + ); +}); diff --git a/src/components/BlinkoResource/ResourceContextMenu.tsx b/src/components/BlinkoResource/ResourceContextMenu.tsx new file mode 100644 index 00000000..7e5371ca --- /dev/null +++ b/src/components/BlinkoResource/ResourceContextMenu.tsx @@ -0,0 +1,250 @@ +import { observer } from "mobx-react-lite"; +import { Dropdown, DropdownTrigger, DropdownMenu, DropdownItem, Button, Input } from '@nextui-org/react'; +import { Icon } from '@iconify/react'; +import { useTranslation } from 'react-i18next'; +import { RootStore } from "@/store"; +import { ResourceStore } from "@/store/resourceStore"; +import { api } from "@/lib/trpc"; +import { DialogStore } from "@/store/module/Dialog"; +import { useState, useCallback } from "react"; +import { helper } from "@/lib/helper"; +import { showTipsDialog } from "../Common/TipsDialog"; +import { PromiseCall } from "@/store/standard/PromiseState"; +import { ToastPlugin } from "@/store/module/Toast/Toast"; +import { DialogStandaloneStore } from "@/store/module/DialogStandalone"; + +const MenuItem = ({ icon, label, className = '' }: { icon: string; label: string; className?: string }) => ( +
+ + {label} +
+); + +interface ResourceContextMenuProps { + onTrigger: () => void; +} + +export const ResourceContextMenu = observer(({ onTrigger }: ResourceContextMenuProps) => { + const { t } = useTranslation(); + const resourceStore = RootStore.Get(ResourceStore); + const resource = resourceStore.contextMenuResource; + + const handleDownload = (() => { + if (!resource) return; + if (!resource.path) return; + helper.download.downloadByLink(resource.path); + }); + + const handleRename = async () => { + if (!resource) return; + const currentName = resource.isFolder ? resource.folderName : resource.name; + const resourceStore = RootStore.Get(ResourceStore); + + RootStore.Get(DialogStore).setData({ + isOpen: true, + title: t('rename'), + content: () => { + const getNameAndExt = (filename: string) => { + const lastDotIndex = filename.lastIndexOf('.'); + if (lastDotIndex === -1 || resource.isFolder) return { name: filename, ext: '' }; + return { + name: filename.substring(0, lastDotIndex), + ext: filename.substring(lastDotIndex) + }; + }; + + const { name: initialName, ext } = getNameAndExt(currentName || ''); + const [newName, setNewName] = useState(initialName); + + const getFullFolderPath = (name: string) => { + if (!resource.isFolder) return undefined; + if (resourceStore.currentFolder) { + return `${resourceStore.currentFolder}/${name}`; + } + return name; + }; + + return ( +
+ {ext}
} + onChange={(e) => setNewName(e.target.value)} + /> + +
+ ); + } + }); + }; + + const handleDelete = async () => { + if (!resource) return; + showTipsDialog({ + title: t('confirm-delete'), + content: t('confirm-delete-content', { + name: resource.isFolder ? resource.folderName : resource.name + }), + onConfirm: async () => { + if (resource.isFolder) { + const folderPath = resourceStore.currentFolder + ? `${resourceStore.currentFolder}/${resource.folderName}` + : resource.folderName; + + await RootStore.Get(ToastPlugin).promise( + PromiseCall(api.attachments.delete.mutate({ + id: resource.id!, + isFolder: resource.isFolder, + folderPath: folderPath?.split('/').join(',') + }), { autoAlert: false }), { + loading: t("operation-in-progress"), + success: t("operation-success"), + error: t("operation-failed") + } + ); + } else { + await PromiseCall(api.attachments.delete.mutate({ + id: resource.id!, + isFolder: false + })); + } + RootStore.Get(DialogStandaloneStore).close(); + resourceStore.refreshTicker++; + } + }); + }; + + const handleCut = () => { + if (!resource) return; + resourceStore.setCutItems([resource]); + }; + + const handlePaste = async () => { + if (!resource) return; + if (!resourceStore.clipboard || !resource.isFolder) return; + + const { items } = resourceStore.clipboard; + if (resourceStore.clipboard.type === 'cut') { + const targetPath = resourceStore.currentFolder + ? `${resourceStore.currentFolder}/${resource.folderName}` + : resource.folderName; + await RootStore.Get(ToastPlugin).promise(PromiseCall(api.attachments.move.mutate({ + sourceIds: items.map(item => item.id!), + targetFolder: targetPath!.split('/').join(',') + }), { autoAlert: false }), { + loading: t("operation-in-progress"), + success: t("operation-success"), + error: t("operation-failed") + }); + + resourceStore.clearClipboard(); + resourceStore.refreshTicker++; + } + }; + + const canPaste = () => { + if (!resource) return; + return resource.isFolder && + resourceStore.clipboard !== null && + resourceStore.clipboard.items.length > 0; + }; + + const handleMoveToParent = async () => { + if (!resource || !resourceStore.currentFolder) return; + await RootStore.Get(ToastPlugin).promise( + resourceStore.moveToParentFolder([resource]), + { + loading: t("operation-in-progress"), + success: t("operation-success"), + error: t("operation-failed") + } + ); + }; + + return ( + onTrigger()}> + + + + + { + resource?.isFolder ? null : ( + + + + ) + } + + + + + + {resourceStore.currentFolder ? ( + + + + ) : null} + + { + resource?.isFolder ? null : ( + + + + ) + } + + {canPaste() ? ( + + + + ) : null} + + + + + + + ); +}); \ No newline at end of file diff --git a/src/components/BlinkoResource/ResourceItem.tsx b/src/components/BlinkoResource/ResourceItem.tsx new file mode 100644 index 00000000..22f0bb70 --- /dev/null +++ b/src/components/BlinkoResource/ResourceItem.tsx @@ -0,0 +1,252 @@ +import { Card, Image, Checkbox, Chip, Tooltip } from "@nextui-org/react"; +import { Icon } from "@iconify/react"; +import { PhotoProvider, PhotoView } from "react-photo-view"; +import { filesize } from "filesize"; +import dayjs from "@/lib/dayjs"; +import { FileIcons } from "@/components/Common/AttachmentRender/FileIcon"; +import { memo, useState, useEffect, useCallback, useMemo } from "react"; +import { Draggable, Droppable } from 'react-beautiful-dnd-next'; +import { useTranslation } from "react-i18next"; +import { type ResourceType } from "@/server/types"; +import { ResourceContextMenu } from './ResourceContextMenu'; +import { ContextMenuTrigger } from '@/components/Common/ContextMenu'; +import { RootStore } from "@/store"; +import { ResourceStore } from "@/store/resourceStore"; +import { _ } from "@/lib/lodash"; +import { observer } from "mobx-react-lite"; +import { toJS } from "mobx"; +import { motion } from "framer-motion"; + +interface ResourceItemProps { + item: ResourceType; + index: number; + onSelect: (id: number) => void; + isSelected: boolean; + onFolderClick: (folderName: string) => void; +} + +interface ResourceCardProps { + item: ResourceType; + isSelected: boolean; + onSelect: (id: number) => void; + isDragging?: boolean; + isDraggingOver?: boolean; + // children?: React.ReactNode; +} + +const getCardClassName = (isDragging?: boolean, isDraggingOver?: boolean) => { + const baseClasses = "mb-2 p-4 hover:bg-hover bg-background transition-all duration-200"; + + if (isDraggingOver) { + return `${baseClasses} relative overflow-visible ring-2 ring-primary/30 bg-primary/10`; + } + + if (isDragging) { + return `${baseClasses} opacity-50 bg-primary/10`; + } + + return baseClasses; +}; + +const ResourceCard = observer(({ + item, + isSelected, + onSelect, + isDragging, + isDraggingOver, +}: ResourceCardProps) => { + const { t } = useTranslation(); + const resourceStore = RootStore.Get(ResourceStore); + const isImage = item.type?.startsWith('image/'); + + const fileNameAndExt = useMemo(() => { + const fileName = toJS(item.name); + const lastDotIndex = fileName.lastIndexOf('.'); + if (lastDotIndex === -1) return { name: fileName, ext: '' }; + return { + name: fileName.substring(0, lastDotIndex), + ext: fileName.substring(lastDotIndex + 1) + }; + }, [item.name]); + + const isS3File = useMemo(() => item.path?.includes('s3file'), [item.path]); + + const handleContextMenu = useCallback(() => { + console.log('handlContetxmet') + resourceStore.setContextMenuResource(_.cloneDeep(item)); + }, [item, resourceStore]); + + const cardProps = { + className: getCardClassName(isDragging, isDraggingOver) + }; + + if (item.isFolder) { + return ( + +
+
+ +
+
+
{item.folderName}
+
+ +
+
+ ); + } + + return ( + +
+ onSelect(item.id!)} + className="z-10" + /> + {isImage ? ( + + + {item.name} + + + ) : ( +
+ +
+ )} +
+
+ {fileNameAndExt.name} + {isS3File && ( + + + + )} +
+
+ + {fileNameAndExt.ext} + + {filesize(Number(item.size))} · {dayjs(item.createdAt).format('YYYY-MM-DD HH:mm')} +
+
+ +
+
+ ); +}); + +const ResourceItem = observer(({ item, index, onSelect, isSelected, onFolderClick }: ResourceItemProps) => { + const { t } = useTranslation(); + + const handleClick = useMemo(() => (e: React.MouseEvent) => { + if (item.isFolder) { + e.preventDefault(); + onFolderClick(item.folderName || ''); + } + }, [item.isFolder, item.folderName, onFolderClick]); + + const draggableId = useMemo(() => + item.isFolder ? `folder-${item.folderName}` : String(item.id), + [item.isFolder, item.folderName, item.id] + ); + + const droppableId = useMemo(() => + item.isFolder ? `folder-${item.folderName}` : undefined, + [item.isFolder, item.folderName] + ); + + return ( + + {(provided: any, snapshot: any) => { + const draggableStyle = { + cursor: item.isFolder ? 'pointer' : 'default', + ...provided.draggableProps.style, + transform: item.isFolder ? 'none' : provided.draggableProps.style?.transform + }; + + return ( +
+ + {item.isFolder ? ( + + {(dropProvided, dropSnapshot) => ( +
+ + {dropProvided.placeholder} +
+ )} +
+ ) : ( + + )} +
+
+ ); + }} +
+ ); +}); + +export const MemoizedResourceItem = memo(ResourceItem, (prevProps, nextProps) => { + const prevItem = toJS(prevProps.item); + const nextItem = toJS(nextProps.item); + + if (prevItem.isFolder && nextItem.isFolder) { + return ( + prevItem.folderName === nextItem.folderName && + prevProps.isSelected === nextProps.isSelected && + prevProps.index === nextProps.index + ); + } + + return ( + prevItem.id === nextItem.id && + prevItem.name === nextItem.name && + prevItem.path === nextItem.path && + prevItem.size === nextItem.size && + prevProps.isSelected === nextProps.isSelected && + prevProps.index === nextProps.index + ); +}); \ No newline at end of file diff --git a/src/components/BlinkoRightClickMenu/index.tsx b/src/components/BlinkoRightClickMenu/index.tsx index 8cd0239f..75a08c1a 100644 --- a/src/components/BlinkoRightClickMenu/index.tsx +++ b/src/components/BlinkoRightClickMenu/index.tsx @@ -1,6 +1,6 @@ import { observer } from "mobx-react-lite"; import { BlinkoStore } from '@/store/blinkoStore'; -import { Divider, Dropdown, DropdownTrigger, DropdownMenu, DropdownItem, Button } from '@nextui-org/react'; +import { Divider, Dropdown, DropdownTrigger, DropdownMenu, DropdownItem, Button, DatePicker } from '@nextui-org/react'; import { _ } from '@/lib/lodash'; import { useTranslation } from 'react-i18next'; import { ContextMenu, ContextMenuItem } from '@/components/Common/ContextMenu'; @@ -15,6 +15,61 @@ import { NoteType } from "@/server/types"; import { useRouter } from "next/router"; import { AiStore } from "@/store/aiStore"; import { FocusEditorFixMobile } from "../Common/Editor/editorUtils"; +import { parseAbsoluteToLocal } from "@internationalized/date"; +import i18n from "@/lib/i18n"; + +export const ShowEditTimeModel = () => { + const blinko = RootStore.Get(BlinkoStore) + RootStore.Get(DialogStore).setData({ + size: 'sm' as any, + isOpen: true, + onlyContent: true, + isDismissable: false, + showOnlyContentCloseButton: true, + content: () => { + const [createdAt, setCreatedAt] = useState(blinko.curSelectedNote?.createdAt ? + parseAbsoluteToLocal(blinko.curSelectedNote.createdAt.toISOString()) : null); + + const [updatedAt, setUpdatedAt] = useState(blinko.curSelectedNote?.updatedAt ? + parseAbsoluteToLocal(blinko.curSelectedNote.updatedAt.toISOString()) : null); + + const handleSave = () => { + if (!createdAt || !updatedAt) return; + + blinko.upsertNote.call({ + id: blinko.curSelectedNote?.id, + createdAt: createdAt.toDate(), + updatedAt: updatedAt.toDate() + }); + + RootStore.Get(DialogStore).close(); + } + return
+
+ + + +
+
+ } + }) +} export const ShowEditBlinkoModel = (size: string = '2xl', mode: 'create' | 'edit' = 'edit') => { const blinko = RootStore.Get(BlinkoStore) @@ -187,6 +242,14 @@ export const DeleteItem = observer(() => {
}) +export const EditTimeItem = observer(() => { + const { t } = useTranslation(); + return
+ +
{t('edit-time')}
+
+}) + export const BlinkoRightClickMenu = observer(() => { const [isDetailPage, setIsDetailPage] = useState(false) const router = useRouter() @@ -205,6 +268,10 @@ export const BlinkoRightClickMenu = observer(() => { : <>} + ShowEditTimeModel()}> + + + @@ -265,6 +332,7 @@ export const LeftCickMenu = observer(({ onTrigger, className }: { onTrigger: () { handleMultiSelect() }}> + ShowEditTimeModel()}> diff --git a/src/components/BlinkoSettings/BasicSetting.tsx b/src/components/BlinkoSettings/BasicSetting.tsx index f6bfaaa8..fa9c5f26 100644 --- a/src/components/BlinkoSettings/BasicSetting.tsx +++ b/src/components/BlinkoSettings/BasicSetting.tsx @@ -18,6 +18,7 @@ import React, { useEffect, useState } from "react"; import { motion, AnimatePresence } from "motion/react"; import { ShowGen2FATokenModal } from "../Common/TwoFactorModal/gen2FATokenModal"; import { CollapsibleCard } from "../Common/CollapsibleCard"; +import { eventBus } from "@/lib/event"; export const BasicSetting = observer(() => { const [isCollapsed, setIsCollapsed] = useState(false); @@ -240,9 +241,7 @@ export const BasicSetting = observer(() => { } /> diff --git a/src/components/BlinkoSettings/ImportSetting.tsx b/src/components/BlinkoSettings/ImportSetting.tsx index 3c5aa12d..e31ace31 100644 --- a/src/components/BlinkoSettings/ImportSetting.tsx +++ b/src/components/BlinkoSettings/ImportSetting.tsx @@ -7,7 +7,7 @@ import { helper } from "@/lib/helper"; import dayjs from "@/lib/dayjs"; import { Icon } from "@iconify/react"; import { api, streamApi } from "@/lib/trpc"; -import { Item } from "./Item"; +import { Item, ItemWithTooltip } from "./Item"; import { useTranslation } from "react-i18next"; import { UploadFileWrapper } from "../Common/UploadFile"; import { ToastPlugin } from "@/store/module/Toast/Toast"; @@ -26,13 +26,12 @@ export const ImportSetting = observer(() => { title={t('import')} > {t('import-from-bko')}} + leftContent={{t('import-from-bko-tip')}
} />} rightContent={<> { if (!fileName.endsWith('.bko')) { return RootStore.Get(ToastPlugin).error(t('not-a-bko-file')) } - // PromiseCall(api.task.restoreDB.query({ fileName })) ShowBlinkoProgressDialog(filePath) }}> diff --git a/src/components/BlinkoSettings/Item.tsx b/src/components/BlinkoSettings/Item.tsx index 53ecb956..ae9414f1 100644 --- a/src/components/BlinkoSettings/Item.tsx +++ b/src/components/BlinkoSettings/Item.tsx @@ -31,7 +31,7 @@ export const Item = observer(({ leftContent, rightContent, type = 'row', hidden export const ItemWithTooltip = observer(({ content, toolTipContent }: { content: any, toolTipContent: any }) => { return
{content} - + {toolTipContent}
}> diff --git a/src/components/BlinkoSettings/MusicSetting.tsx b/src/components/BlinkoSettings/MusicSetting.tsx new file mode 100644 index 00000000..0f9eb604 --- /dev/null +++ b/src/components/BlinkoSettings/MusicSetting.tsx @@ -0,0 +1,58 @@ +import { observer } from "mobx-react-lite"; +import { Input } from "@nextui-org/react"; +import { useTranslation } from "react-i18next"; +import { Item, ItemWithTooltip } from "./Item"; +import { RootStore } from "@/store"; +import { BlinkoStore } from "@/store/blinkoStore"; +import { PromiseCall } from "@/store/standard/PromiseState"; +import { api } from "@/lib/trpc"; +import { CollapsibleCard } from "../Common/CollapsibleCard"; + +export const MusicSetting = observer(() => { + const { t } = useTranslation(); + const blinko = RootStore.Get(BlinkoStore); + + return ( + + + {t('spotify-consumer-key')}} toolTipContent={t('spotify-consumer-key-tip')} /> +
{t('spotify-consumer-key-tip-2')}
+
} + rightContent={ + { + PromiseCall(api.config.update.mutate({ + key: 'spotifyConsumerKey', + value: e.target.value + })); + }} + placeholder={t('enter-spotify-consumer-key')} + /> + } + /> + + {t('spotify-consumer-secret')}} + rightContent={ + { + PromiseCall(api.config.update.mutate({ + key: 'spotifyConsumerSecret', + value: e.target.value + })); + }} + placeholder={t('enter-spotify-consumer-secret')} + /> + } + /> + + ); +}); diff --git a/src/components/BlinkoSettings/StorageSetting.tsx b/src/components/BlinkoSettings/StorageSetting.tsx index e5f26480..e217e7f7 100644 --- a/src/components/BlinkoSettings/StorageSetting.tsx +++ b/src/components/BlinkoSettings/StorageSetting.tsx @@ -1,5 +1,5 @@ import { observer } from "mobx-react-lite"; -import { Button, Card, DropdownItem, DropdownMenu, DropdownTrigger, Dropdown, Input } from "@nextui-org/react"; +import { Button, DropdownItem, DropdownMenu, DropdownTrigger, Dropdown, Input } from "@nextui-org/react"; import { RootStore } from "@/store"; import { BlinkoStore } from "@/store/blinkoStore"; import { PromiseCall } from "@/store/standard/PromiseState"; @@ -10,6 +10,7 @@ import { useTranslation } from "react-i18next"; import { useMediaQuery } from "usehooks-ts"; import { useEffect } from "react"; import { PasswordInput } from "@/components/Common/PasswordInput"; +import { CollapsibleCard } from "@/components/Common/CollapsibleCard"; export const StorageSetting = observer(() => { @@ -23,6 +24,7 @@ export const StorageSetting = observer(() => { s3Region: "", s3Bucket: "", s3CustomPath: "", + localCustomPath: "", })) useEffect(() => { @@ -32,16 +34,16 @@ export const StorageSetting = observer(() => { store.s3Region = blinko.config.value?.s3Region! store.s3Bucket = blinko.config.value?.s3Bucket! store.s3CustomPath = blinko.config.value?.s3CustomPath! + store.localCustomPath = blinko.config.value?.localCustomPath! }, [blinko.config.value]) - return -
- -
{t('storage')}
-
+ return + leftContent={
{t('object-storage')}
} rightContent={
@@ -60,9 +62,29 @@ export const StorageSetting = observer(() => { {t('local-file-system')} S3 + +
} /> + {blinko.config.value?.objectStorage != 's3' && + +
{t('custom-path')}
+ } + rightContent={ store.localCustomPath = e.target.value} + placeholder="/custom/path/" + onBlur={async (e) => { + await PromiseCall(api.config.update.mutate({ + key: 'localCustomPath', + value: e.target.value + })) + }} />} + /> + } + { blinko.config.value?.objectStorage === 's3' && <> { }} />} /> } -
+ + + }) \ No newline at end of file diff --git a/src/components/BlinkoSettings/UserSetting.tsx b/src/components/BlinkoSettings/UserSetting.tsx index 96770130..42d21dd0 100644 --- a/src/components/BlinkoSettings/UserSetting.tsx +++ b/src/components/BlinkoSettings/UserSetting.tsx @@ -13,6 +13,9 @@ import { DialogStore } from "@/store/module/Dialog"; import { signOut } from "next-auth/react"; import { PasswordInput } from "../Common/PasswordInput"; import { CollapsibleCard } from "../Common/CollapsibleCard"; +import { showTipsDialog } from "../Common/TipsDialog"; +import { ToastPlugin } from "@/store/module/Toast/Toast"; +import { DialogStandaloneStore } from "@/store/module/DialogStandalone"; const UpdateUserInfo = observer(({ id, name, password }: { id?: number, name: string, password: string }) => { const { t } = useTranslation() @@ -55,7 +58,7 @@ export const UserSetting = observer(() => { useEffect(() => { blinko.userList.call() }, []) - + return ( { content: }) }}>{t('create-user')} - } + } /> { {i.role} - + }) } - : null} + : null + } /> - + ); }); \ No newline at end of file diff --git a/src/components/Common/AttachmentRender/DraggableFileGrid.tsx b/src/components/Common/AttachmentRender/DraggableFileGrid.tsx new file mode 100644 index 00000000..5730f743 --- /dev/null +++ b/src/components/Common/AttachmentRender/DraggableFileGrid.tsx @@ -0,0 +1,97 @@ +import React from 'react'; +import { DragDropContext, Draggable, Droppable } from 'react-beautiful-dnd-next'; +import { FileType } from '../Editor/type'; +import { FileIcons } from './FileIcon'; +import { DeleteIcon } from './icons'; +import { helper } from '@/lib/helper'; +import { api } from '@/lib/trpc'; + +type DraggableFileGridProps = { + files: FileType[]; + preview?: boolean; + columns?: number; + onReorder?: (newFiles: FileType[]) => void; + type: 'image' | 'other'; + className?: string; + renderItem?: (file: FileType) => React.ReactNode; +}; + +export const DraggableFileGrid = ({ + files, + preview = false, + onReorder, + type, + className, + renderItem +}: DraggableFileGridProps) => { + const handleDragEnd = async (result: any) => { + if (!result.destination) return; + + const { source, destination } = result; + const filteredFiles = files.filter(i => i.previewType === type); + const allFiles = Array.from(files); + + const [reorderedItem] = filteredFiles.splice(source.index, 1); + if (reorderedItem) { + filteredFiles.splice(destination.index, 0, reorderedItem); + + const newFiles = allFiles.map(file => { + if (file.previewType === type) { + return filteredFiles.shift() || file; + } + return file; + }); + + onReorder?.(newFiles); + + try { + await api.notes.updateAttachmentsOrder.mutate({ + attachments: newFiles.map((file, index) => ({ + name: file.name, + sortOrder: index + })) + }); + } catch (error) { + console.error('Failed to update attachments order:', error); + } + } + }; + + return ( + + + {(provided, snapshot) => ( +
+ {files.filter(i => i.previewType === type).map((file, index) => ( + + {(provided, snapshot) => ( +
+ {renderItem?.(file)} +
+ )} +
+ ))} + {provided.placeholder} +
+ )} +
+
+ ); +}; \ No newline at end of file diff --git a/src/components/Common/FileIcon.tsx b/src/components/Common/AttachmentRender/FileIcon.tsx similarity index 98% rename from src/components/Common/FileIcon.tsx rename to src/components/Common/AttachmentRender/FileIcon.tsx index 2f7746b5..64e71ae2 100644 --- a/src/components/Common/FileIcon.tsx +++ b/src/components/Common/AttachmentRender/FileIcon.tsx @@ -3,6 +3,7 @@ import { Icon } from '@iconify/react'; import { observer } from 'mobx-react-lite'; import { FileIcon, defaultStyles } from 'react-file-icon'; +//path xxx.png export const FileIcons = observer(({ path, size = 15, className, isLoading = false }: { path: string, size?: number, className?: string, isLoading?: boolean }) => { const extension = helper.getFileExtension(path) return
diff --git a/src/components/Common/AttachmentRender/audioRender.tsx b/src/components/Common/AttachmentRender/audioRender.tsx new file mode 100644 index 00000000..db031e95 --- /dev/null +++ b/src/components/Common/AttachmentRender/audioRender.tsx @@ -0,0 +1,289 @@ +import React, { useEffect, useState, useRef } from 'react'; +import { api } from '@/lib/trpc'; +import { FileType } from '../Editor/type'; +import { DeleteIcon, DownloadIcon } from './icons'; +import { Icon } from '@iconify/react'; +import { RootStore, useStore } from '@/store'; +import { MusicManagerStore } from '@/store/musicManagerStore' +import { observer } from 'mobx-react-lite'; +import { motion, AnimatePresence } from 'framer-motion'; +import { Button } from '@nextui-org/react'; + +interface AudioMetadata { + coverUrl?: string; + trackName?: string; + albumName?: string; + artists?: string[]; + previewUrl?: string; +} + +interface Props { + files: FileType[]; + preview?: boolean; +} + +export const AudioRender = observer(({ files, preview = false }: Props) => { + const [audioMetadata, setAudioMetadata] = useState>({}); + const musicManager = RootStore.Get(MusicManagerStore); + const [isPlaying, setIsPlaying] = useState>({}); + const audioRefs = useRef>({}); + const progressRefs = useRef>({}); + const [currentTime, setCurrentTime] = useState>({}); + const [duration, setDuration] = useState>({}); + const audioRef = useRef(null); + + useEffect(() => { + if (audioRef.current) { + musicManager.initAudio(audioRef.current); + } + }, []); + + const getMetadata = async (file: FileType) => { + try { + const metadata = await api.public.musicMetadata.query({ + filePath: file.preview.includes('s3file') ? new URL(file.preview, window.location.href).href : file.preview + }); + setAudioMetadata(prev => ({ + ...prev, + [file.name]: metadata + })); + } catch (error) { + console.error('Failed to fetch audio metadata:', error); + } + }; + + useEffect(() => { + files?.filter(i => i.previewType === 'audio').forEach(file => { + getMetadata(file); + }); + }, [files]); + + const isCurrentPlaying = (fileName: string) => { + return musicManager.isPlaying && musicManager.currentTrack?.file.name === fileName; + }; + + const togglePlay = async (fileName: string) => { + const audioFiles = files.filter(i => i.previewType === 'audio'); + const file = audioFiles.find(f => f.name === fileName); + if (!file) { + return; + } + + if (musicManager.currentTrack?.file.name === fileName) { + await musicManager.togglePlay(); + return; + } + + musicManager.addToPlaylist(file, audioMetadata[fileName], true); + }; + + const formatTime = (seconds: number): string => { + const minutes = Math.floor(seconds / 60); + const remainingSeconds = Math.floor(seconds % 60); + return `${minutes}:${remainingSeconds.toString().padStart(2, '0')}`; + }; + + useEffect(() => { + const updateProgress = () => { + if (!musicManager.audioElement) return; + + const fileName = musicManager.currentTrack?.file.name; + if (!fileName) return; + + const progress = progressRefs.current[fileName]; + if (!progress) return; + + const percentage = (musicManager.currentTime / musicManager.duration) * 100; + progress.style.width = `${percentage}%`; + + setCurrentTime(prev => ({ + ...prev, + [fileName]: formatTime(musicManager.currentTime) + })); + + if (musicManager.duration) { + setDuration(prev => ({ + ...prev, + [fileName]: formatTime(musicManager.duration) + })); + } + }; + + const interval = setInterval(updateProgress, 100); + return () => clearInterval(interval); + }, [musicManager.currentTrack]); + + const handleProgressBarClick = (e: React.MouseEvent, fileName: string) => { + if (!musicManager.audioElement || musicManager.currentTrack?.file.name !== fileName) return; + + const progressBar = e.currentTarget; + const rect = progressBar.getBoundingClientRect(); + const x = e.clientX - rect.left; + const percentage = x / rect.width; + + musicManager.seek(musicManager.duration * percentage); + }; + + const handleEnded = (fileName: string) => { + setIsPlaying(prev => ({ ...prev, [fileName]: false })); + const progress = progressRefs.current[fileName]; + if (progress) { + progress.style.width = '0%'; + } + }; + + const handleProgressBarDrag = (e: React.MouseEvent, fileName: string) => { + if (!musicManager.audioElement || musicManager.currentTrack?.file.name !== fileName) return; + + const progressBar = e.currentTarget; + const updateTimeFromMousePosition = (e: MouseEvent) => { + const rect = progressBar.getBoundingClientRect(); + const x = Math.max(0, Math.min(e.clientX - rect.left, rect.width)); + const percentage = x / rect.width; + musicManager.seek(musicManager.duration * percentage); + }; + + const handleMouseMove = (e: MouseEvent) => { + e.preventDefault(); + updateTimeFromMousePosition(e); + }; + + const handleMouseUp = () => { + document.removeEventListener('mousemove', handleMouseMove); + document.removeEventListener('mouseup', handleMouseUp); + }; + + document.addEventListener('mousemove', handleMouseMove); + document.addEventListener('mouseup', handleMouseUp); + }; + + const getBackgroundStyle = (coverUrl?: string) => { + if (!coverUrl) { + return 'bg-gradient-to-r from-gray-100 to-gray-50 dark:from-blue-500/10 dark:to-purple-500/10 backdrop-blur-sm hover:bg-opacity-90 border border-black/5 dark:border-white/5'; + } + return 'bg-cover bg-center relative overflow-hidden hover:bg-opacity-90'; + }; + + return ( +
+