From 82e329023f51f97e4dda911a592979eaa7be8007 Mon Sep 17 00:00:00 2001 From: Kalle Struik Date: Wed, 19 Mar 2025 17:27:27 +0100 Subject: [PATCH] Port everything from v2 except for things that require a server --- README.md | 5 - package.json | 24 +- pnpm-lock.yaml | 3059 ++++++++++++++++- src/components/app_sidebar.tsx | 253 ++ .../collection/collection_header.tsx | 46 + .../collection/collection_settings_dialog.tsx | 211 ++ .../collection/new_collection_dialog.tsx | 76 + src/components/editor/link_icon.tsx | 17 + src/components/editor/task_icon.tsx | 13 + src/components/editor/term_icon.tsx | 18 + src/components/form/collection_picker.tsx | 85 + src/components/form/color_picker.tsx | 84 + src/components/form/icon_picker.tsx | 82 + .../form/property_type_combobox.tsx | 57 + src/components/note/new_note_dialog.tsx | 120 + src/components/note/note_canvas.tsx | 76 + src/components/note/note_header.tsx | 96 + .../note/note_properties_dialog.tsx | 114 + src/components/note/note_settings_dialog.tsx | 75 + src/components/note/notes_grid.tsx | 105 + src/components/ui/alert-dialog.tsx | 155 + src/components/ui/avatar.tsx | 51 + src/components/ui/command.tsx | 175 + src/components/ui/dialog.tsx | 133 + src/components/ui/dropdown-menu.tsx | 255 ++ src/components/ui/label.tsx | 22 + src/components/ui/popover.tsx | 46 + src/components/ui/radio-group.tsx | 45 + src/components/ui/sidebar.tsx | 2 +- src/components/ui/toggle-group.tsx | 71 + src/components/ui/toggle.tsx | 47 + src/components/user/user_avatar.tsx | 51 + src/components/yjs/metadata-inspector.tsx | 64 + src/editor/Editor.tsx | 90 + src/editor/editor_utils.ts | 26 + src/editor/nodes/focusable_node.ts | 32 + src/editor/nodes/formatted_text.ts | 109 + src/editor/nodes/header_node.ts | 108 + src/editor/nodes/link_node.tsx | 184 + src/editor/nodes/task_node.tsx | 159 + src/editor/nodes/term_node.tsx | 142 + src/editor/plugins/formatted_text_plugin.tsx | 102 + src/editor/plugins/header_plugin.tsx | 78 + src/editor/plugins/link_plugin.tsx | 136 + src/editor/plugins/paragraph_plugin.tsx | 48 + src/editor/plugins/selection_plugin.tsx | 49 + src/editor/plugins/task_plugin.tsx | 116 + src/editor/plugins/term_plugin.tsx | 83 + src/editor/serialized_editor_content.ts | 263 ++ src/global.d.ts | 8 + src/hooks/use-metadata.tsx | 65 + src/hooks/use-note.tsx | 81 + src/hooks/use-observe.ts | 22 + src/hooks/use-redraw.ts | 7 + src/hooks/use-theme.tsx | 47 + src/hooks/use-ydoc.ts | 33 + src/lib/color.ts | 26 + src/lib/icon.ts | 12 + src/lib/metadata.ts | 99 + src/lib/property.ts | 42 + src/lib/yjs.ts | 48 + src/main.tsx | 18 +- src/routeTree.gen.ts | 369 ++ .../RootLayout.tsx => routes/__root.tsx} | 17 +- src/routes/app.tsx | 52 + src/routes/app/calendar.tsx | 9 + src/routes/app/collection.$id.tsx | 20 + src/routes/app/graph.tsx | 9 + src/routes/app/inbox.tsx | 15 + src/routes/app/index.tsx | 12 + src/routes/app/note.$id.tsx | 36 + src/routes/app/search.tsx | 13 + src/routes/app/term.$term.tsx | 10 + src/routes/app/todo.tsx | 9 + src/{pages/Root.tsx => routes/index.tsx} | 12 +- src/styles.css | 4 +- vite.config.js | 4 + 77 files changed, 8505 insertions(+), 52 deletions(-) create mode 100644 src/components/app_sidebar.tsx create mode 100644 src/components/collection/collection_header.tsx create mode 100644 src/components/collection/collection_settings_dialog.tsx create mode 100644 src/components/collection/new_collection_dialog.tsx create mode 100644 src/components/editor/link_icon.tsx create mode 100644 src/components/editor/task_icon.tsx create mode 100644 src/components/editor/term_icon.tsx create mode 100644 src/components/form/collection_picker.tsx create mode 100644 src/components/form/color_picker.tsx create mode 100644 src/components/form/icon_picker.tsx create mode 100644 src/components/form/property_type_combobox.tsx create mode 100644 src/components/note/new_note_dialog.tsx create mode 100644 src/components/note/note_canvas.tsx create mode 100644 src/components/note/note_header.tsx create mode 100644 src/components/note/note_properties_dialog.tsx create mode 100644 src/components/note/note_settings_dialog.tsx create mode 100644 src/components/note/notes_grid.tsx create mode 100644 src/components/ui/alert-dialog.tsx create mode 100644 src/components/ui/avatar.tsx create mode 100644 src/components/ui/command.tsx create mode 100644 src/components/ui/dialog.tsx create mode 100644 src/components/ui/dropdown-menu.tsx create mode 100644 src/components/ui/label.tsx create mode 100644 src/components/ui/popover.tsx create mode 100644 src/components/ui/radio-group.tsx create mode 100644 src/components/ui/toggle-group.tsx create mode 100644 src/components/ui/toggle.tsx create mode 100644 src/components/user/user_avatar.tsx create mode 100644 src/components/yjs/metadata-inspector.tsx create mode 100644 src/editor/Editor.tsx create mode 100644 src/editor/editor_utils.ts create mode 100644 src/editor/nodes/focusable_node.ts create mode 100644 src/editor/nodes/formatted_text.ts create mode 100644 src/editor/nodes/header_node.ts create mode 100644 src/editor/nodes/link_node.tsx create mode 100644 src/editor/nodes/task_node.tsx create mode 100644 src/editor/nodes/term_node.tsx create mode 100644 src/editor/plugins/formatted_text_plugin.tsx create mode 100644 src/editor/plugins/header_plugin.tsx create mode 100644 src/editor/plugins/link_plugin.tsx create mode 100644 src/editor/plugins/paragraph_plugin.tsx create mode 100644 src/editor/plugins/selection_plugin.tsx create mode 100644 src/editor/plugins/task_plugin.tsx create mode 100644 src/editor/plugins/term_plugin.tsx create mode 100644 src/editor/serialized_editor_content.ts create mode 100644 src/global.d.ts create mode 100644 src/hooks/use-metadata.tsx create mode 100644 src/hooks/use-note.tsx create mode 100644 src/hooks/use-observe.ts create mode 100644 src/hooks/use-redraw.ts create mode 100644 src/hooks/use-theme.tsx create mode 100644 src/hooks/use-ydoc.ts create mode 100644 src/lib/color.ts create mode 100644 src/lib/icon.ts create mode 100644 src/lib/metadata.ts create mode 100644 src/lib/property.ts create mode 100644 src/lib/yjs.ts create mode 100644 src/routeTree.gen.ts rename src/{layouts/RootLayout.tsx => routes/__root.tsx} (58%) create mode 100644 src/routes/app.tsx create mode 100644 src/routes/app/calendar.tsx create mode 100644 src/routes/app/collection.$id.tsx create mode 100644 src/routes/app/graph.tsx create mode 100644 src/routes/app/inbox.tsx create mode 100644 src/routes/app/index.tsx create mode 100644 src/routes/app/note.$id.tsx create mode 100644 src/routes/app/search.tsx create mode 100644 src/routes/app/term.$term.tsx create mode 100644 src/routes/app/todo.tsx rename src/{pages/Root.tsx => routes/index.tsx} (80%) diff --git a/README.md b/README.md index fe7a7b1..9b004c9 100644 --- a/README.md +++ b/README.md @@ -6,11 +6,6 @@ Yes, another rewrite was needed. Again. - Yes all of it - Client - Overview - - Search - Graph - Calendar - Todo - - Collection pages - - Note pages - - Lexical - - Excalidraw diff --git a/package.json b/package.json index 32c1b8b..ce12a62 100644 --- a/package.json +++ b/package.json @@ -9,25 +9,47 @@ "test": "vitest run" }, "dependencies": { + "@excalidraw/excalidraw": "^0.18.0", + "@lexical/react": "^0.28.0", + "@lexical/utils": "^0.28.0", + "@lexical/yjs": "^0.28.0", + "@mdi/js": "^7.4.47", + "@mdi/react": "^1.6.1", + "@radix-ui/react-alert-dialog": "^1.1.6", + "@radix-ui/react-avatar": "^1.1.3", "@radix-ui/react-dialog": "^1.1.6", + "@radix-ui/react-dropdown-menu": "^2.1.6", + "@radix-ui/react-label": "^2.1.2", + "@radix-ui/react-popover": "^1.1.6", + "@radix-ui/react-radio-group": "^1.2.3", "@radix-ui/react-separator": "^1.1.2", "@radix-ui/react-slot": "^1.1.2", + "@radix-ui/react-toggle": "^1.1.2", + "@radix-ui/react-toggle-group": "^1.1.2", "@radix-ui/react-tooltip": "^1.1.8", "@tailwindcss/vite": "^4.0.6", "@tanstack/react-router": "^1.114.3", "@tanstack/react-router-devtools": "^1.114.3", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", + "cmdk": "1.0.0", + "lexical": "^0.28.0", "lucide-react": "^0.483.0", "react": "^19.0.0", "react-dom": "^19.0.0", "tailwind-merge": "^3.0.2", "tailwindcss": "^4.0.6", - "tw-animate-css": "^1.2.4" + "tw-animate-css": "^1.2.4", + "y-excalidraw": "^2.0.12", + "y-indexeddb": "^9.0.12", + "y-websocket": "^2.1.0", + "yjs": "^13.6.24", + "zod": "^3.24.2" }, "devDependencies": { "@serwist/vite": "^9.0.12", "@serwist/window": "^9.0.12", + "@tanstack/router-plugin": "^1.114.25", "@testing-library/dom": "^10.4.0", "@testing-library/react": "^16.2.0", "@types/react": "^19.0.8", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0d04a95..d40d79e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,21 +8,63 @@ importers: .: dependencies: + '@excalidraw/excalidraw': + specifier: ^0.18.0 + version: 0.18.0(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@lexical/react': + specifier: ^0.28.0 + version: 0.28.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(yjs@13.6.24) + '@lexical/utils': + specifier: ^0.28.0 + version: 0.28.0 + '@lexical/yjs': + specifier: ^0.28.0 + version: 0.28.0(yjs@13.6.24) + '@mdi/js': + specifier: ^7.4.47 + version: 7.4.47 + '@mdi/react': + specifier: ^1.6.1 + version: 1.6.1 + '@radix-ui/react-alert-dialog': + specifier: ^1.1.6 + version: 1.1.6(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-avatar': + specifier: ^1.1.3 + version: 1.1.3(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) '@radix-ui/react-dialog': specifier: ^1.1.6 version: 1.1.6(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-dropdown-menu': + specifier: ^2.1.6 + version: 2.1.6(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-label': + specifier: ^2.1.2 + version: 2.1.2(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-popover': + specifier: ^1.1.6 + version: 1.1.6(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-radio-group': + specifier: ^1.2.3 + version: 1.2.3(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) '@radix-ui/react-separator': specifier: ^1.1.2 version: 1.1.2(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) '@radix-ui/react-slot': specifier: ^1.1.2 version: 1.1.2(@types/react@19.0.11)(react@19.0.0) + '@radix-ui/react-toggle': + specifier: ^1.1.2 + version: 1.1.2(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-toggle-group': + specifier: ^1.1.2 + version: 1.1.2(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) '@radix-ui/react-tooltip': specifier: ^1.1.8 version: 1.1.8(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) '@tailwindcss/vite': specifier: ^4.0.6 - version: 4.0.14(vite@6.2.2(jiti@2.4.2)(lightningcss@1.29.2)) + version: 4.0.14(vite@6.2.2(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.51.0)(tsx@4.19.3)) '@tanstack/react-router': specifier: ^1.114.3 version: 1.114.25(react-dom@19.0.0(react@19.0.0))(react@19.0.0) @@ -35,6 +77,12 @@ importers: clsx: specifier: ^2.1.1 version: 2.1.1 + cmdk: + specifier: 1.0.0 + version: 1.0.0(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + lexical: + specifier: ^0.28.0 + version: 0.28.0 lucide-react: specifier: ^0.483.0 version: 0.483.0(react@19.0.0) @@ -53,13 +101,31 @@ importers: tw-animate-css: specifier: ^1.2.4 version: 1.2.4 + y-excalidraw: + specifier: ^2.0.12 + version: 2.0.12(@excalidraw/excalidraw@0.18.0(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(yjs@13.6.24) + y-indexeddb: + specifier: ^9.0.12 + version: 9.0.12(yjs@13.6.24) + y-websocket: + specifier: ^2.1.0 + version: 2.1.0(yjs@13.6.24) + yjs: + specifier: ^13.6.24 + version: 13.6.24 + zod: + specifier: ^3.24.2 + version: 3.24.2 devDependencies: '@serwist/vite': specifier: ^9.0.12 - version: 9.0.12(typescript@5.8.2)(vite@6.2.2(jiti@2.4.2)(lightningcss@1.29.2)) + version: 9.0.12(typescript@5.8.2)(vite@6.2.2(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.51.0)(tsx@4.19.3)) '@serwist/window': specifier: ^9.0.12 version: 9.0.12(typescript@5.8.2) + '@tanstack/router-plugin': + specifier: ^1.114.25 + version: 1.114.25(@tanstack/react-router@1.114.25(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(vite@6.2.2(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.51.0)(tsx@4.19.3)) '@testing-library/dom': specifier: ^10.4.0 version: 10.4.0 @@ -74,7 +140,7 @@ importers: version: 19.0.4(@types/react@19.0.11) '@vitejs/plugin-react': specifier: ^4.3.4 - version: 4.3.4(vite@6.2.2(jiti@2.4.2)(lightningcss@1.29.2)) + version: 4.3.4(vite@6.2.2(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.51.0)(tsx@4.19.3)) jsdom: specifier: ^26.0.0 version: 26.0.0 @@ -86,10 +152,10 @@ importers: version: 5.8.2 vite: specifier: ^6.1.0 - version: 6.2.2(jiti@2.4.2)(lightningcss@1.29.2) + version: 6.2.2(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.51.0)(tsx@4.19.3) vitest: specifier: ^3.0.5 - version: 3.0.9(jiti@2.4.2)(jsdom@26.0.0)(lightningcss@1.29.2) + version: 3.0.9(@types/debug@4.1.12)(jiti@2.4.2)(jsdom@26.0.0)(lightningcss@1.29.2)(sass@1.51.0)(tsx@4.19.3) web-vitals: specifier: ^4.2.4 version: 4.2.4 @@ -158,6 +224,18 @@ packages: engines: {node: '>=6.0.0'} hasBin: true + '@babel/plugin-syntax-jsx@7.25.9': + resolution: {integrity: sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-typescript@7.25.9': + resolution: {integrity: sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + '@babel/plugin-transform-react-jsx-self@7.25.9': resolution: {integrity: sha512-y8quW6p0WHkEhmErnfe58r7x0A70uKphQm8Sp8cV7tjNQwK56sNVK0M73LK3WuYmsuyrftut4xAkjjgU0twaMg==} engines: {node: '>=6.9.0'} @@ -186,6 +264,9 @@ packages: resolution: {integrity: sha512-emqcG3vHrpxUKTrxcblR36dcrcoRDvKmnL/dCL6ZsHaShW80qxCAcNhzQZrpeM765VzEos+xOi4s+r4IXzTwdQ==} engines: {node: '>=6.9.0'} + '@braintree/sanitize-url@6.0.2': + resolution: {integrity: sha512-Tbsj02wXCbqGmzdnXNk0SOF19ChhRU70BsroIi4Pm6Ehp56in6vch94mfbdQ17DozxkL3BAVjbZ4Qc1a0HFRAg==} + '@csstools/color-helpers@5.0.2': resolution: {integrity: sha512-JqWH1vsgdGcw2RR6VliXXdA0/59LttzlU8UlRT/iUUsEeWfYq8I+K0yhihEUTTHLRm1EXvpsCx3083EU15ecsA==} engines: {node: '>=18'} @@ -364,6 +445,25 @@ packages: cpu: [x64] os: [win32] + '@excalidraw/excalidraw@0.18.0': + resolution: {integrity: sha512-QkIiS+5qdy8lmDWTKsuy0sK/fen/LRDtbhm2lc2xcFcqhv2/zdg95bYnl+wnwwXGHo7kEmP65BSiMHE7PJ3Zpw==} + peerDependencies: + react: ^17.0.2 || ^18.2.0 || ^19.0.0 + react-dom: ^17.0.2 || ^18.2.0 || ^19.0.0 + + '@excalidraw/laser-pointer@1.3.1': + resolution: {integrity: sha512-psA1z1N2qeAfsORdXc9JmD2y4CmDwmuMRxnNdJHZexIcPwaNEyIpNcelw+QkL9rz9tosaN9krXuKaRqYpRAR6g==} + + '@excalidraw/markdown-to-text@0.1.2': + resolution: {integrity: sha512-1nDXBNAojfi3oSFwJswKREkFm5wrSjqay81QlyRv2pkITG/XYB5v+oChENVBQLcxQwX4IUATWvXM5BcaNhPiIg==} + + '@excalidraw/mermaid-to-excalidraw@1.1.2': + resolution: {integrity: sha512-hAFv/TTIsOdoy0dL5v+oBd297SQ+Z88gZ5u99fCIFuEMHfQuPgLhU/ztKhFSTs7fISwVo6fizny/5oQRR3d4tQ==} + + '@excalidraw/random-username@1.1.0': + resolution: {integrity: sha512-nULYsQxkWHnbmHvcs+efMkJ4/9TtvNyFeLyHdeGxW0zHs6P+jYVqcRff9A6Vq9w9JXeDRnRh2VKvTtS19GW2qA==} + engines: {node: '>=10'} + '@floating-ui/core@1.6.9': resolution: {integrity: sha512-uMXCuQ3BItDUbAMhIXw7UPXRfAlOAvZzdK9BWpE60MCn+Svt3aLn9jsPTi/WNGlRUu2uI0v5S7JiIUsbsvh3fw==} @@ -401,13 +501,109 @@ packages: '@jridgewell/trace-mapping@0.3.25': resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + '@lexical/clipboard@0.28.0': + resolution: {integrity: sha512-LYqion+kAwFQJStA37JAEMxTL/m1WlZbotDfM/2WuONmlO0yWxiyRDI18oeCwhBD6LQQd9c3Ccxp9HFwUG1AVw==} + + '@lexical/code@0.28.0': + resolution: {integrity: sha512-9LOKSWdRhxqAKRq5yveNC21XKtW4h2rmFNTucwMWZ9vLu9xteOHEwZdO1Qv82PFUmgCpAhg6EntmnZu9xD3K7Q==} + + '@lexical/devtools-core@0.28.0': + resolution: {integrity: sha512-Fk4itAjZ+MqTYXN84aE5RDf+wQX67N5nyo3JVxQTFZGAghx7Ux1xLWHB25zzD0YfjMtJ0NQROAbE3xdecZzxcQ==} + peerDependencies: + react: '>=17.x' + react-dom: '>=17.x' + + '@lexical/dragon@0.28.0': + resolution: {integrity: sha512-T6T8YaHnhU863ruuqmRHTLUYa8sfg/ArYcrnNGZGfpvvFTfFjpWb/ELOvOWo8N6Y/4fnSLjQ20aXexVW1KcTBQ==} + + '@lexical/hashtag@0.28.0': + resolution: {integrity: sha512-zcqX9Qna4lj96bAUfwSQSVEhYQ0O5erSjrIhOVqEgeQ5ubz0EvqnnMbbwNHIb2n6jzSwAvpD/3UZJZtolh+zVg==} + + '@lexical/history@0.28.0': + resolution: {integrity: sha512-CHzDxaGDn6qCFFhU0YKP1B8sgEb++0Ksqsj6BfDL/6TMxoLNQwRQhP3BUNNXl1kvUhxTQZgk3b9MjJZRaFKG9Q==} + + '@lexical/html@0.28.0': + resolution: {integrity: sha512-ayb0FPxr55Ko99/d9ewbfrApul4L0z+KpU2ZG03im7EvUPVLyIGLx4S0QguMDvQh0Vu+eJ7/EESuonDs5BCe3A==} + + '@lexical/link@0.28.0': + resolution: {integrity: sha512-T5VKxpOnML5DcXv2lW3Le0vjNlcbdohZjS9f6PAvm6eX8EzBKDpLQCopr1/0KGdlLd1QrzQsykQrdU7ieC4LRg==} + + '@lexical/list@0.28.0': + resolution: {integrity: sha512-3a8QcZ75n2TLxP+xkSPJ2V15jsysMLMe0YoObG+ew/sioVelIU8GciYsWBo5GgQmwSzJNQJeK5cJ9p1b71z2cg==} + + '@lexical/mark@0.28.0': + resolution: {integrity: sha512-v5PzmTACsJrw3GvNZy2rgPxrNn9InLvLFoKqrSlNhhyvYNIAcuC4KVy00LKLja43Gw/fuB3QwKohYfAtM3yR3g==} + + '@lexical/markdown@0.28.0': + resolution: {integrity: sha512-F3JXClqN4cjmXYLDK0IztxkbZuqkqS/AVbxnhGvnDYHQ9Gp8l7BonczhOiPwmJCDubJrAACP0L9LCqyt0jDRFw==} + + '@lexical/offset@0.28.0': + resolution: {integrity: sha512-/SMDQgBPeWM936t04mtH6UAn3xAjP/meu9q136bcT3S7p7V8ew9JfNp9aznTPTx+2W3brJORAvUow7Xn1fSHmw==} + + '@lexical/overflow@0.28.0': + resolution: {integrity: sha512-ppmhHXEZVicBm05w9EVflzwFavTVNAe4q0bkabWUeW0IoCT3Vg2A3JT7PC9ypmp+mboUD195foFEr1BBSv1Y8Q==} + + '@lexical/plain-text@0.28.0': + resolution: {integrity: sha512-Jj2dCMDEfRuVetfDKcUes8J5jvAfZrLnILFlHxnu7y+lC+7R/NR403DYb3NJ8H7+lNiH1K15+U2K7ewbjxS6KQ==} + + '@lexical/react@0.28.0': + resolution: {integrity: sha512-dWPnxrKrbQFjNqExqnaAsV0UEUgw/5M1ZYRWd5FGBGjHqVTCaX2jNHlKLMA68Od0VPIoOX2Zy1TYZ8ZKtsj5Dg==} + peerDependencies: + react: '>=17.x' + react-dom: '>=17.x' + + '@lexical/rich-text@0.28.0': + resolution: {integrity: sha512-y+vUWI+9uFupIb9UvssKU/DKcT9dFUZuQBu7utFkLadxCNyXQHeRjxzjzmvFiM3DBV0guPUDGu5VS5TPnIA+OA==} + + '@lexical/selection@0.28.0': + resolution: {integrity: sha512-AJDi67Nsexyejzp4dEQSVoPov4P+FJ0t1v6DxUU+YmcvV56QyJQi6ue0i/xd8unr75ZufzLsAC0cDJJCEI7QDA==} + + '@lexical/table@0.28.0': + resolution: {integrity: sha512-HMPCwXdj0sRWdlDzsHcNWRgbeKbEhn3L8LPhFnTq7q61gZ4YW2umdmuvQFKnIBcKq49drTH8cUwZoIwI8+AEEw==} + + '@lexical/text@0.28.0': + resolution: {integrity: sha512-PT/A2RZv+ktn7SG/tJkOpGlYE6zjOND59VtRHnV/xciZ+jEJVaqAHtWjhbWibAIZQAkv/O7UouuDqzDaNTSGAA==} + + '@lexical/utils@0.28.0': + resolution: {integrity: sha512-Qw00DjkS1nRK7DLSgqJpJ77Ti2AuiOQ6m5eM38YojoWXkVmoxqKAUMaIbVNVKqjFgrQvKFF46sXxIJPbUQkB0w==} + + '@lexical/yjs@0.28.0': + resolution: {integrity: sha512-rKHpUEd3nrvMY7ghmOC0AeGSYT7YIviba+JViaOzrCX4/Wtv5C/3Sl7Io12Z9k+s1BKmy7C28bOdQHvRWaD7vQ==} + peerDependencies: + yjs: '>=13.5.22' + + '@mdi/js@7.4.47': + resolution: {integrity: sha512-KPnNOtm5i2pMabqZxpUz7iQf+mfrYZyKCZ8QNz85czgEt7cuHcGorWfdzUMWYA0SD+a6Hn4FmJ+YhzzzjkTZrQ==} + + '@mdi/react@1.6.1': + resolution: {integrity: sha512-4qZeDcluDFGFTWkHs86VOlHkm6gnKaMql13/gpIcUQ8kzxHgpj31NuCkD8abECVfbULJ3shc7Yt4HJ6Wu6SN4w==} + '@pkgjs/parseargs@0.11.0': resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} + '@radix-ui/primitive@1.0.0': + resolution: {integrity: sha512-3e7rn8FDMin4CgeL7Z/49smCA3rFYY3Ha2rUQ7HRWFadS5iCRw08ZgVT1LaNTCNqgvrUiyczLflrVrF0SRQtNA==} + + '@radix-ui/primitive@1.0.1': + resolution: {integrity: sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==} + '@radix-ui/primitive@1.1.1': resolution: {integrity: sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA==} + '@radix-ui/react-alert-dialog@1.1.6': + resolution: {integrity: sha512-p4XnPqgej8sZAAReCAKgz1REYZEBLR8hU9Pg27wFnCWIMc8g1ccCs0FjBcy05V15VTu8pAePw/VDYeOm/uZ6yQ==} + 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-arrow@1.1.2': resolution: {integrity: sha512-G+KcpzXHq24iH0uGG/pF8LyzpFJYGD4RfLjCIBfGdSLXvjLHST31RUiRVrupIBMvIppMgSzQ6l66iAxl03tdlg==} peerDependencies: @@ -421,6 +617,52 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-avatar@1.1.3': + resolution: {integrity: sha512-Paen00T4P8L8gd9bNsRMw7Cbaz85oxiv+hzomsRZgFm2byltPFDtfcoqlWJ8GyZlIBWgLssJlzLCnKU0G0302g==} + 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-collection@1.0.1': + resolution: {integrity: sha512-uuiFbs+YCKjn3X1DTSx9G7BHApu4GHbi3kgiwsnFUbOKCrwejAJv4eE4Vc8C0Oaxt9T0aV4ox0WCOdx+39Xo+g==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + + '@radix-ui/react-collection@1.1.2': + resolution: {integrity: sha512-9z54IEKRxIa9VityapoEYMuByaG42iSy1ZXlY2KcuLSEtq8x4987/N6m15ppoMffgZX72gER2uHe1D9Y6Unlcw==} + 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-compose-refs@1.0.0': + resolution: {integrity: sha512-0KaSv6sx787/hK3eF53iOkiSLwAGlFMx5lotrqD2pTjB18KbybKoEIgkNZTKC60YECDQTKGTRcDBILwZVqVKvA==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + + '@radix-ui/react-compose-refs@1.0.1': + resolution: {integrity: sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@radix-ui/react-compose-refs@1.1.1': resolution: {integrity: sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw==} peerDependencies: @@ -430,6 +672,20 @@ packages: '@types/react': optional: true + '@radix-ui/react-context@1.0.0': + resolution: {integrity: sha512-1pVM9RfOQ+n/N5PJK33kRSKsr1glNxomxONs5c49MliinBY6Yw2Q995qfBUUo0/Mbg05B/sGA0gkgPI7kmSHBg==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + + '@radix-ui/react-context@1.0.1': + resolution: {integrity: sha512-ebbrdFoYTcuZ0v4wG5tedGnp9tzcV8awzsxYph7gXUyvnNLuTIcCk1q17JEbnVhXAKG9oX3KtchwiMIAYp9NLg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@radix-ui/react-context@1.1.1': resolution: {integrity: sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q==} peerDependencies: @@ -439,6 +695,19 @@ packages: '@types/react': optional: true + '@radix-ui/react-dialog@1.0.5': + resolution: {integrity: sha512-GjWJX/AUpB703eEBanuBnIWdIXg6NvJFCXcNlSZk4xdszCdhrJgBoUd1cGk67vFO+WdA2pfI/plOpqz/5GUP6Q==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-dialog@1.1.6': resolution: {integrity: sha512-/IVhJV5AceX620DUJ4uYVMymzsipdKBzo3edo+omeskCKGm9FRHM0ebIdbPnlQVJqyuHbuBltQUOG2mOTq2IYw==} peerDependencies: @@ -452,6 +721,33 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-direction@1.0.0': + resolution: {integrity: sha512-2HV05lGUgYcA6xgLQ4BKPDmtL+QbIZYH5fCOTAOOcJ5O0QbWS3i9lKaurLzliYUDhORI2Qr3pyjhJh44lKA3rQ==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + + '@radix-ui/react-direction@1.1.0': + resolution: {integrity: sha512-BUuBvgThEiAXh2DWu93XsT+a3aWrGqolGlqqw5VU1kG7p/ZH2cuDlM1sRLNnY3QcBS69UIz2mcKhMxDsdewhjg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-dismissable-layer@1.0.5': + resolution: {integrity: sha512-aJeDjQhywg9LBu2t/At58hCvr7pEm0o2Ke1x33B+MhjNmmZ17sy4KImo0KPLgsnc/zN7GPdce8Cnn0SWvwZO7g==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-dismissable-layer@1.1.5': resolution: {integrity: sha512-E4TywXY6UsXNRhFrECa5HAvE5/4BFcGyfTyK36gP+pAW1ed7UTK4vKwdr53gAJYwqbfCWC6ATvJa3J3R/9+Qrg==} peerDependencies: @@ -465,6 +761,28 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-dropdown-menu@2.1.6': + resolution: {integrity: sha512-no3X7V5fD487wab/ZYSHXq3H37u4NVeLDKI/Ks724X/eEFSSEFYZxWgsIlr1UBeEyDaM29HM5x9p1Nv8DuTYPA==} + 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.0.1': + resolution: {integrity: sha512-Rect2dWbQ8waGzhMavsIbmSVCgYxkXLxxR3ZvCX79JOglzdEy4JXMb98lq4hPxUbLr77nP0UOGf4rcMU+s1pUA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@radix-ui/react-focus-guards@1.1.1': resolution: {integrity: sha512-pSIwfrT1a6sIoDASCSpFwOasEwKTZWDw/iBdtnqKO7v6FeOzYJ7U53cPzYFVR3geGGXgVHaH+CdngrrAzqUGxg==} peerDependencies: @@ -474,6 +792,19 @@ packages: '@types/react': optional: true + '@radix-ui/react-focus-scope@1.0.4': + resolution: {integrity: sha512-sL04Mgvf+FmyvZeYfNu1EPAaaxD+aw7cYeIB9L9Fvq8+urhltTRaEo5ysKOpHuKPclsZcSUMKlN05x4u+CINpA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-focus-scope@1.1.2': resolution: {integrity: sha512-zxwE80FCU7lcXUGWkdt6XpTTCKPitG1XKOwViTxHVKIJhZl9MvIl2dVHeZENCWD9+EdWv05wlaEkRXUykU27RA==} peerDependencies: @@ -487,6 +818,20 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-id@1.0.0': + resolution: {integrity: sha512-Q6iAB/U7Tq3NTolBBQbHTgclPmGWE3OlktGGqrClPozSw4vkQ1DfQAOtzgRPecKsMdJINE05iaoDUG8tRzCBjw==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + + '@radix-ui/react-id@1.0.1': + resolution: {integrity: sha512-tI7sT/kqYp8p96yGWY1OAnLHrqDgzHefRBKQ2YAkBS5ja7QLcZ9Z/uY7bEjPUatf8RomoXM8/1sMj1IJaE5UzQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@radix-ui/react-id@1.1.0': resolution: {integrity: sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA==} peerDependencies: @@ -496,6 +841,45 @@ packages: '@types/react': optional: true + '@radix-ui/react-label@2.1.2': + resolution: {integrity: sha512-zo1uGMTaNlHehDyFQcDZXRJhUPDuukcnHz0/jnrup0JA6qL+AFpAnty+7VKa9esuU5xTblAZzTGYJKSKaBxBhw==} + 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-menu@2.1.6': + resolution: {integrity: sha512-tBBb5CXDJW3t2mo9WlO7r6GTmWV0F0uzHZVFmlRmYpiSK1CDU5IKojP1pm7oknpBOrFZx/YgBRW9oorPO2S/Lg==} + 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-popover@1.1.6': + resolution: {integrity: sha512-NQouW0x4/GnkFJ/pRqsIS3rM/k97VzKnVb2jB7Gq7VEGPy5g7uNV1ykySFt7eWSp3i2uSGFwaJcvIRJBAHmmFg==} + 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-popper@1.2.2': resolution: {integrity: sha512-Rvqc3nOpwseCyj/rgjlJDYAgyfw7OC1tTkKn2ivhaMGcYt8FSBlahHOZak2i3QwkRXUXgGgzeEe2RuqeEHuHgA==} peerDependencies: @@ -509,6 +893,19 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-portal@1.0.4': + resolution: {integrity: sha512-Qki+C/EuGUVCQTOTD5vzJzJuMUlewbzuKyUy+/iHM2uwGiru9gZeBJtHAPKAEkB5KWGi9mP/CHKcY0wt1aW45Q==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-portal@1.1.4': resolution: {integrity: sha512-sn2O9k1rPFYVyKd5LAJfo96JlSGVFpa1fS6UuBJfrZadudiw5tAmru+n1x7aMRQ84qDM71Zh1+SzK5QwU0tJfA==} peerDependencies: @@ -522,6 +919,25 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-presence@1.0.0': + resolution: {integrity: sha512-A+6XEvN01NfVWiKu38ybawfHsBjWum42MRPnEuqPsBZ4eV7e/7K321B5VgYMPv3Xx5An6o1/l9ZuDBgmcmWK3w==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + + '@radix-ui/react-presence@1.0.1': + resolution: {integrity: sha512-UXLW4UAbIY5ZjcvzjfRFo5gxva8QirC9hF7wRE4U5gz+TP0DbRk+//qyuAQ1McDxBt1xNMBTaciFGvEmJvAZCg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + 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: @@ -535,6 +951,25 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-primitive@1.0.1': + resolution: {integrity: sha512-fHbmislWVkZaIdeF6GZxF0A/NH/3BjrGIYj+Ae6eTmTCr7EB0RQAAVEiqsXK6p3/JcRqVSBQoceZroj30Jj3XA==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + + '@radix-ui/react-primitive@1.0.3': + resolution: {integrity: sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-primitive@2.0.2': resolution: {integrity: sha512-Ec/0d38EIuvDF+GZjcMU/Ze6MxntVJYO/fRlCPhCaVUyPY9WTalHJw54tp9sXeJo3tlShWpy41vQRgLRGOuz+w==} peerDependencies: @@ -548,6 +983,38 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-radio-group@1.2.3': + resolution: {integrity: sha512-xtCsqt8Rp09FK50ItqEqTJ7Sxanz8EM8dnkVIhJrc/wkMMomSmXHvYbhv3E7Zx4oXh98aaLt9W679SUYXg4IDA==} + 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-roving-focus@1.0.2': + resolution: {integrity: sha512-HLK+CqD/8pN6GfJm3U+cqpqhSKYAWiOJDe+A+8MfxBnOue39QEeMa43csUn2CXCHQT0/mewh1LrrG4tfkM9DMA==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + + '@radix-ui/react-roving-focus@1.1.2': + resolution: {integrity: sha512-zgMQWkNO169GtGqRvYrzb0Zf8NhMHS2DuEB/TiEmVnpr5OqPU3i8lfbxaAmC2J/KYuIQxyoQQ6DxepyXp61/xw==} + 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-separator@1.1.2': resolution: {integrity: sha512-oZfHcaAp2Y6KFBX6I5P1u7CQoy4lheCGiYj+pGFrHy8E/VNRb5E39TkTr3JrV520csPBTZjkuKFdEsjS5EUNKQ==} peerDependencies: @@ -561,6 +1028,20 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-slot@1.0.1': + resolution: {integrity: sha512-avutXAFL1ehGvAXtPquu0YK5oz6ctS474iM3vNGQIkswrVhdrS52e3uoMQBzZhNRAIE0jBnUyXWNmSjGHhCFcw==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + + '@radix-ui/react-slot@1.0.2': + resolution: {integrity: sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@radix-ui/react-slot@1.1.2': resolution: {integrity: sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ==} peerDependencies: @@ -570,6 +1051,38 @@ packages: '@types/react': optional: true + '@radix-ui/react-tabs@1.0.2': + resolution: {integrity: sha512-gOUwh+HbjCuL0UCo8kZ+kdUEG8QtpdO4sMQduJ34ZEz0r4922g9REOBM+vIsfwtGxSug4Yb1msJMJYN2Bk8TpQ==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + + '@radix-ui/react-toggle-group@1.1.2': + resolution: {integrity: sha512-JBm6s6aVG/nwuY5eadhU2zDi/IwYS0sDM5ZWb4nymv/hn3hZdkw+gENn0LP4iY1yCd7+bgJaCwueMYJIU3vk4A==} + 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-toggle@1.1.2': + resolution: {integrity: sha512-lntKchNWx3aCHuWKiDY+8WudiegQvBpDRAYL8dKLRvKEH8VOpl0XX6SSU/bUBqIRJbcTy4+MW06Wv8vgp10rzQ==} + 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-tooltip@1.1.8': resolution: {integrity: sha512-YAA2cu48EkJZdAMHC0dqo9kialOcRStbtiY4nJPaht7Ptrhcvpo+eDChaM6BIs8kL6a8Z5l5poiqLnXcNduOkA==} peerDependencies: @@ -583,6 +1096,20 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-use-callback-ref@1.0.0': + resolution: {integrity: sha512-GZtyzoHz95Rhs6S63D2t/eqvdFCm7I+yHMLVQheKM7nBD8mbZIt+ct1jz4536MDnaOGKIxynJ8eHTkVGVVkoTg==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + + '@radix-ui/react-use-callback-ref@1.0.1': + resolution: {integrity: sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@radix-ui/react-use-callback-ref@1.1.0': resolution: {integrity: sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw==} peerDependencies: @@ -592,6 +1119,20 @@ packages: '@types/react': optional: true + '@radix-ui/react-use-controllable-state@1.0.0': + resolution: {integrity: sha512-FohDoZvk3mEXh9AWAVyRTYR4Sq7/gavuofglmiXB2g1aKyboUD4YtgWxKj8O5n+Uak52gXQ4wKz5IFST4vtJHg==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + + '@radix-ui/react-use-controllable-state@1.0.1': + resolution: {integrity: sha512-Svl5GY5FQeN758fWKrjM6Qb7asvXeiZltlT4U2gVfl8Gx5UAv2sMR0LWo8yhsIZh2oQ0eFdZ59aoOOMV7b47VA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@radix-ui/react-use-controllable-state@1.1.0': resolution: {integrity: sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw==} peerDependencies: @@ -601,6 +1142,15 @@ packages: '@types/react': optional: true + '@radix-ui/react-use-escape-keydown@1.0.3': + resolution: {integrity: sha512-vyL82j40hcFicA+M4Ex7hVkB9vHgSse1ZWomAqV2Je3RleKGO5iM8KMOEtfoSB0PnIelMd2lATjTGMYqN5ylTg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@radix-ui/react-use-escape-keydown@1.1.0': resolution: {integrity: sha512-L7vwWlR1kTTQ3oh7g1O0CBF3YCyyTj8NmhLR+phShpyA50HCfBFKVJTpshm9PzLiKmehsrQzTYTpX9HvmC9rhw==} peerDependencies: @@ -610,6 +1160,20 @@ packages: '@types/react': optional: true + '@radix-ui/react-use-layout-effect@1.0.0': + resolution: {integrity: sha512-6Tpkq+R6LOlmQb1R5NNETLG0B4YP0wc+klfXafpUCj6JGyaUc8il7/kUZ7m59rGbXGczE9Bs+iz2qloqsZBduQ==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + + '@radix-ui/react-use-layout-effect@1.0.1': + resolution: {integrity: sha512-v/5RegiJWYdoCvMnITBkNNx6bCj20fiaJnWtRkU18yITptraXjffz5Qbn05uOiQnOvi+dbkznkoaMltz1GnszQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@radix-ui/react-use-layout-effect@1.1.0': resolution: {integrity: sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w==} peerDependencies: @@ -619,6 +1183,15 @@ packages: '@types/react': optional: true + '@radix-ui/react-use-previous@1.1.0': + resolution: {integrity: sha512-Z/e78qg2YFnnXcW88A4JmTtm4ADckLno6F7OXotmkQfeuCVaKuYzqAATPhVzl3delXE7CxIV8shofPn3jPc5Og==} + 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-rect@1.1.0': resolution: {integrity: sha512-0Fmkebhr6PiseyZlYAOtLS+nb7jLmpqTrJyv61Pe68MKYW6OWdRE2kI70TaYY27u7H0lajqM3hSMMLFq18Z7nQ==} peerDependencies: @@ -894,9 +1467,47 @@ packages: csstype: optional: true + '@tanstack/router-generator@1.114.25': + resolution: {integrity: sha512-KfPdXm9+zGPrEjcdDkkSbZpDvx8rOSD9sS0cQn6y82jqoSeHlzC0K3bSVElsAmS1uh7WXR+PNDJra+nHUdPhaQ==} + engines: {node: '>=12'} + peerDependencies: + '@tanstack/react-router': ^1.114.25 + peerDependenciesMeta: + '@tanstack/react-router': + optional: true + + '@tanstack/router-plugin@1.114.25': + resolution: {integrity: sha512-4SIvBzgX6TzwgW5OO6Knx4/vX8AocXnfQhXW7dzsNBgzt5WnI4dzoPvp6p9p+Hqo0AjJ2WndpEYq7fMl5BhA4Q==} + engines: {node: '>=12'} + peerDependencies: + '@rsbuild/core': '>=1.0.2' + '@tanstack/react-router': ^1.114.25 + vite: '>=5.0.0 || >=6.0.0' + vite-plugin-solid: ^2.11.2 + webpack: '>=5.92.0' + peerDependenciesMeta: + '@rsbuild/core': + optional: true + '@tanstack/react-router': + optional: true + vite: + optional: true + vite-plugin-solid: + optional: true + webpack: + optional: true + + '@tanstack/router-utils@1.114.12': + resolution: {integrity: sha512-W4tltvM9FQuDEJejz/JJD3q/pVHBXBb8VmA77pZlj4IBW97RnUNy8CUwZUgSYcb9OReoO4i/VjjQCUq9ZdiDmg==} + engines: {node: '>=12'} + '@tanstack/store@0.7.0': resolution: {integrity: sha512-CNIhdoUsmD2NolYuaIs8VfWM467RK6oIBAW4nPEKZhg1smZ+/CwtCdpURgp7nxSqOaV9oKkzdWD80+bC66F/Jg==} + '@tanstack/virtual-file-routes@1.114.12': + resolution: {integrity: sha512-aR13V1kSE/kUkP4a8snmqvj82OUlR5Q/rzxICmObLCsERGfzikUc4wquOy1d/RzJgsLb8o+FiOjSWynt4T7Jhg==} + engines: {node: '>=12'} + '@testing-library/dom@10.4.0': resolution: {integrity: sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==} engines: {node: '>=18'} @@ -931,9 +1542,27 @@ packages: '@types/babel__traverse@7.20.6': resolution: {integrity: sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==} + '@types/d3-scale-chromatic@3.1.0': + resolution: {integrity: sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ==} + + '@types/d3-scale@4.0.9': + resolution: {integrity: sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==} + + '@types/d3-time@3.0.4': + resolution: {integrity: sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==} + + '@types/debug@4.1.12': + resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} + '@types/estree@1.0.6': resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} + '@types/mdast@3.0.15': + resolution: {integrity: sha512-LnwD+mUEfxWMa1QpDraczIn6k0Ee3SMicuYSSzS6ZYl2gKS09EClnJYGd8Du6rfc5r/GZEk5o1mRb8TaTj03sQ==} + + '@types/ms@2.1.0': + resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} + '@types/react-dom@19.0.4': resolution: {integrity: sha512-4fSQ8vWFkg+TGhePfUzVmat3eC14TXYSsiiDSLI0dVLsrm9gZFABjPy/Qu6TKgl1tq1Bu1yDsuQgY3A3DOjCcg==} peerDependencies: @@ -945,6 +1574,9 @@ packages: '@types/trusted-types@2.0.7': resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} + '@types/unist@2.0.11': + resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==} + '@vitejs/plugin-react@4.3.4': resolution: {integrity: sha512-SCCPBJtYLdE8PX/7ZQAs1QAZ8Jqwih+0VBLum1EGqmCCQal+MIUqLCzj3ZUy8ufbC0cAM4LRlSTm7IQJwWT4ug==} engines: {node: ^14.18.0 || >=16.0.0} @@ -980,6 +1612,21 @@ packages: '@vitest/utils@3.0.9': resolution: {integrity: sha512-ilHM5fHhZ89MCp5aAaM9uhfl1c2JdxVxl3McqsdVyVNN6JffnEen8UMCdRTzOhGXNQGo5GNL9QugHrz727Wnng==} + abstract-leveldown@6.2.3: + resolution: {integrity: sha512-BsLm5vFMRUrrLeCcRc+G0t2qOaTzpoJQLOubq2XM72eNpjF5UdU5o/5NvlNhx95XHcAvcl8OMXr4mlg/fRgUXQ==} + engines: {node: '>=6'} + deprecated: Superseded by abstract-level (https://github.com/Level/community#faq) + + abstract-leveldown@6.3.0: + resolution: {integrity: sha512-TU5nlYgta8YrBMNpc9FwQzRbiXsj49gsALsXadbGHt9CROPzX5fB0rWDR5mtdpOOKa5XqRFpbj1QroPAoPzVjQ==} + engines: {node: '>=6'} + deprecated: Superseded by abstract-level (https://github.com/Level/community#faq) + + acorn@8.14.1: + resolution: {integrity: sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==} + engines: {node: '>=0.4.0'} + hasBin: true + agent-base@7.1.3: resolution: {integrity: sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==} engines: {node: '>= 14'} @@ -1004,6 +1651,14 @@ packages: resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} engines: {node: '>=12'} + ansis@3.17.0: + resolution: {integrity: sha512-0qWUglt9JEqLFr3w1I1pbrChn1grhaiAR2ocX1PP/flRmxgtwTzPFFFnfIlD6aMOLQZgSuCRlidD70lvx8yhzg==} + engines: {node: '>=14'} + + anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + aria-hidden@1.2.4: resolution: {integrity: sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A==} engines: {node: '>=10'} @@ -1015,20 +1670,43 @@ packages: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} + async-limiter@1.0.1: + resolution: {integrity: sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==} + asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + babel-dead-code-elimination@1.0.9: + resolution: {integrity: sha512-JLIhax/xullfInZjtu13UJjaLHDeTzt3vOeomaSUdO/nAMEL/pWC/laKrSvWylXMnVWyL5bpmG9njqBZlUQOdg==} + balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + + binary-extensions@2.3.0: + resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} + engines: {node: '>=8'} + brace-expansion@2.0.1: resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + browser-fs-access@0.29.1: + resolution: {integrity: sha512-LSvVX5e21LRrXqVMhqtAwj5xPgDb+fXAIH80NsnCQ9xuZPs2xWsOREi24RKgZa1XOiQRbcmVrv87+ulOKsgjxw==} + browserslist@4.24.4: resolution: {integrity: sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true + buffer@5.7.1: + resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + cac@6.7.14: resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} engines: {node: '>=8'} @@ -1040,6 +1718,9 @@ packages: caniuse-lite@1.0.30001706: resolution: {integrity: sha512-3ZczoTApMAZwPKYWmwVbQMFpXBDds3/0VciVoUwPUbldlYyVLmRVuRs/PcUZtHpbLRpzzDvrvnFuREsGt6lUug==} + canvas-roundrect-polyfill@0.0.1: + resolution: {integrity: sha512-yWq+R3U3jE+coOeEb3a3GgE2j/0MMiDKM/QpLb6h9ihf5fGY9UXtvK9o4vNqjWXoZz7/3EaSVU3IX53TvFFUOw==} + chai@5.2.0: resolution: {integrity: sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==} engines: {node: '>=12'} @@ -1048,17 +1729,34 @@ packages: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} + character-entities@2.0.2: + resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==} + check-error@2.1.1: resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} engines: {node: '>= 16'} + chokidar@3.6.0: + resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} + engines: {node: '>= 8.10.0'} + class-variance-authority@0.7.1: resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==} + clsx@1.1.1: + resolution: {integrity: sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA==} + engines: {node: '>=6'} + clsx@2.1.1: resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} engines: {node: '>=6'} + cmdk@1.0.0: + resolution: {integrity: sha512-gDzVf0a09TvoJ5jnuPvygTB77+XdOSwEmJ88L6XPFPlv7T3RxbP9jgenfylrAMD0+Le1aO0nVjQUzl2g+vjz5Q==} + peerDependencies: + react: ^18.0.0 + react-dom: ^18.0.0 + color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} @@ -1070,6 +1768,14 @@ packages: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} + commander@7.2.0: + resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==} + engines: {node: '>= 10'} + + commander@8.3.0: + resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} + engines: {node: '>= 12'} + common-tags@1.8.2: resolution: {integrity: sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==} engines: {node: '>=4.0.0'} @@ -1077,6 +1783,18 @@ packages: convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + cose-base@1.0.3: + resolution: {integrity: sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==} + + crc-32@0.3.0: + resolution: {integrity: sha512-kucVIjOmMc1f0tv53BJ/5WIX+MGLcKuoBhnGqQrgKJNqLByb/sVMWfW/Aw6hw0jgcqjJ2pi9E5y32zOIpaUlsA==} + engines: {node: '>=0.8'} + + cross-env@7.0.3: + resolution: {integrity: sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==} + engines: {node: '>=10.14', npm: '>=6', yarn: '>=1'} + hasBin: true + cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} @@ -1088,10 +1806,164 @@ packages: csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + cytoscape-cose-bilkent@4.1.0: + resolution: {integrity: sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==} + peerDependencies: + cytoscape: ^3.2.0 + + cytoscape@3.31.1: + resolution: {integrity: sha512-Hx5Mtb1+hnmAKaZZ/7zL1Y5HTFYOjdDswZy/jD+1WINRU8KVi1B7+vlHdsTwY+VCFucTreoyu1RDzQJ9u0d2Hw==} + engines: {node: '>=0.10'} + + d3-array@2.12.1: + resolution: {integrity: sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==} + + d3-array@3.2.4: + resolution: {integrity: sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==} + engines: {node: '>=12'} + + d3-axis@3.0.0: + resolution: {integrity: sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==} + engines: {node: '>=12'} + + d3-brush@3.0.0: + resolution: {integrity: sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==} + engines: {node: '>=12'} + + d3-chord@3.0.1: + resolution: {integrity: sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==} + engines: {node: '>=12'} + + d3-color@3.1.0: + resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==} + engines: {node: '>=12'} + + d3-contour@4.0.2: + resolution: {integrity: sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==} + engines: {node: '>=12'} + + d3-delaunay@6.0.4: + resolution: {integrity: sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==} + engines: {node: '>=12'} + + d3-dispatch@3.0.1: + resolution: {integrity: sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==} + engines: {node: '>=12'} + + d3-drag@3.0.0: + resolution: {integrity: sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==} + engines: {node: '>=12'} + + d3-dsv@3.0.1: + resolution: {integrity: sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==} + engines: {node: '>=12'} + hasBin: true + + d3-ease@3.0.1: + resolution: {integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==} + engines: {node: '>=12'} + + d3-fetch@3.0.1: + resolution: {integrity: sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==} + engines: {node: '>=12'} + + d3-force@3.0.0: + resolution: {integrity: sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==} + engines: {node: '>=12'} + + d3-format@3.1.0: + resolution: {integrity: sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==} + engines: {node: '>=12'} + + d3-geo@3.1.1: + resolution: {integrity: sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==} + engines: {node: '>=12'} + + d3-hierarchy@3.1.2: + resolution: {integrity: sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==} + engines: {node: '>=12'} + + d3-interpolate@3.0.1: + resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==} + engines: {node: '>=12'} + + d3-path@1.0.9: + resolution: {integrity: sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==} + + d3-path@3.1.0: + resolution: {integrity: sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==} + engines: {node: '>=12'} + + d3-polygon@3.0.1: + resolution: {integrity: sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==} + engines: {node: '>=12'} + + d3-quadtree@3.0.1: + resolution: {integrity: sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==} + engines: {node: '>=12'} + + d3-random@3.0.1: + resolution: {integrity: sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==} + engines: {node: '>=12'} + + d3-sankey@0.12.3: + resolution: {integrity: sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ==} + + d3-scale-chromatic@3.1.0: + resolution: {integrity: sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==} + engines: {node: '>=12'} + + d3-scale@4.0.2: + resolution: {integrity: sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==} + engines: {node: '>=12'} + + d3-selection@3.0.0: + resolution: {integrity: sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==} + engines: {node: '>=12'} + + d3-shape@1.3.7: + resolution: {integrity: sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==} + + d3-shape@3.2.0: + resolution: {integrity: sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==} + engines: {node: '>=12'} + + d3-time-format@4.1.0: + resolution: {integrity: sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==} + engines: {node: '>=12'} + + d3-time@3.1.0: + resolution: {integrity: sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==} + engines: {node: '>=12'} + + d3-timer@3.0.1: + resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==} + engines: {node: '>=12'} + + d3-transition@3.0.1: + resolution: {integrity: sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==} + engines: {node: '>=12'} + peerDependencies: + d3-selection: 2 - 3 + + d3-zoom@3.0.0: + resolution: {integrity: sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==} + engines: {node: '>=12'} + + d3@7.9.0: + resolution: {integrity: sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==} + engines: {node: '>=12'} + + dagre-d3-es@7.0.10: + resolution: {integrity: sha512-qTCQmEhcynucuaZgY5/+ti3X/rnszKZhEQH/ZdWdtP1tA/y3VoHJzcVrO9pjjJCNpigfscAtoUB5ONcd2wNn0A==} + data-urls@5.0.0: resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==} engines: {node: '>=18'} + dayjs@1.11.13: + resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==} + debug@4.4.0: resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==} engines: {node: '>=6.0'} @@ -1104,10 +1976,21 @@ packages: decimal.js@10.5.0: resolution: {integrity: sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==} + decode-named-character-reference@1.1.0: + resolution: {integrity: sha512-Wy+JTSbFThEOXQIR2L6mxJvEs+veIzpmqD7ynWxMXGpnk3smkHQOp6forLdHsKpAMW9iJpaBBIxz285t1n1C3w==} + deep-eql@5.0.2: resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} engines: {node: '>=6'} + deferred-leveldown@5.3.0: + resolution: {integrity: sha512-a59VOT+oDy7vtAbLRCZwWgxu2BaCfd5Hk7wxJd48ei7I+nsg8Orlb9CLG0PMZienk9BSUKgeAqkO2+Lw+1+Ukw==} + engines: {node: '>=6'} + deprecated: Superseded by abstract-level (https://github.com/Level/community#faq) + + delaunator@5.0.1: + resolution: {integrity: sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==} + delayed-stream@1.0.0: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} @@ -1123,9 +2006,20 @@ packages: detect-node-es@1.1.0: resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} + diff@5.2.0: + resolution: {integrity: sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==} + engines: {node: '>=0.3.1'} + + diff@7.0.0: + resolution: {integrity: sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==} + engines: {node: '>=0.3.1'} + dom-accessibility-api@0.5.16: resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==} + dompurify@3.1.6: + resolution: {integrity: sha512-cTOAhc36AalkjtBpfG6O8JimdTMWNXjiePT2xQH/ppBGi/4uIpmj8eKyIkMJErXWARyINV/sB38yf8JCLF5pbQ==} + dunder-proto@1.0.1: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} @@ -1136,12 +2030,20 @@ packages: electron-to-chromium@1.5.120: resolution: {integrity: sha512-oTUp3gfX1gZI+xfD2djr2rzQdHCwHzPQrrK0CD7WpTdF0nPdQ/INcRVjWgLdCT4a9W3jFObR9DAfsuyFQnI8CQ==} + elkjs@0.9.3: + resolution: {integrity: sha512-f/ZeWvW/BCXbhGEf1Ujp29EASo/lk1FDnETgNKwJrsVvGZhUWCZyg3xLJjAsxfOmt8KjswHmI5EwCQcPMpOYhQ==} + emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + encoding-down@6.3.0: + resolution: {integrity: sha512-QKrV0iKR6MZVJV08QY0wp1e7vF6QbhnbQhb07bwpEyuz4uZiZgPlEGdkCROuFkUwdxlFaiPIhjyarH1ee/3vhw==} + engines: {node: '>=6'} + deprecated: Superseded by abstract-level (https://github.com/Level/community#faq) + enhanced-resolve@5.18.1: resolution: {integrity: sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==} engines: {node: '>=10.13.0'} @@ -1150,6 +2052,10 @@ packages: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} engines: {node: '>=0.12'} + errno@0.1.8: + resolution: {integrity: sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==} + hasBin: true + es-define-property@1.0.1: resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} engines: {node: '>= 0.4'} @@ -1169,6 +2075,10 @@ packages: resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} engines: {node: '>= 0.4'} + es6-promise-pool@2.5.0: + resolution: {integrity: sha512-VHErXfzR/6r/+yyzPKeBvO0lgjfC5cbDCQWjWwMZWSb6YU39TGIl51OUmCfWCq4ylMdJSB8zkz2vIuIeIxXApA==} + engines: {node: '>=0.10.0'} + esbuild@0.25.1: resolution: {integrity: sha512-BGO5LtrGC7vxnqucAe/rmvKdJllfGaYWdyABvyMoXQlfYMb2bbRuReWR5tEGE//4LcNJj9XrkovTqNYRFZHAMQ==} engines: {node: '>=18'} @@ -1185,6 +2095,10 @@ packages: resolution: {integrity: sha512-80F22aiJ3GLyVnS/B3HzgR6RelZVumzj9jkL0Rhz4h0xYbNW9PjlQz5h3J/SShErbXBc295vseR4/MIbVmUbeA==} engines: {node: '>=12.0.0'} + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + foreground-child@3.3.1: resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} engines: {node: '>=14'} @@ -1193,6 +2107,10 @@ packages: resolution: {integrity: sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==} engines: {node: '>= 6'} + fractional-indexing@3.2.0: + resolution: {integrity: sha512-PcOxmqwYCW7O2ovKRU8OoQQj2yqTfEB/yeTYk4gPid6dN5ODRfU1hXd9tTVZzax/0NkO7AxpHykvZnT1aYp/BQ==} + engines: {node: ^14.13.1 || >=16.0.0} + fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -1201,6 +2119,10 @@ packages: function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + fuzzy@0.1.3: + resolution: {integrity: sha512-/gZffu4ykarLrCiP3Ygsa86UAo1E5vEVlvTrpkKywXSbP9Xhln3oSp9QSV57gEq3JFFpGJ4GZ+5zdEp3FcUh4w==} + engines: {node: '>= 0.6.0'} + gensync@1.0.0-beta.2: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} engines: {node: '>=6.9.0'} @@ -1217,6 +2139,13 @@ packages: resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} engines: {node: '>= 0.4'} + get-tsconfig@4.10.0: + resolution: {integrity: sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A==} + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + glob@10.4.5: resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} hasBin: true @@ -1225,6 +2154,9 @@ packages: resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} engines: {node: '>=4'} + glur@1.1.2: + resolution: {integrity: sha512-l+8esYHTKOx2G/Aao4lEQ0bnHWg4fWtJbVoZZT9Knxi01pB8C80BR85nONLFwkkQoFRCmXY+BUcGZN3yZ2QsRA==} + goober@2.1.16: resolution: {integrity: sha512-erjk19y1U33+XAMe1VTvIONHYoSqE4iS7BYUZfHaqeohLmnC0FdxEh7rQU+6MZ4OajItzjZFSRtVANrQwNq6/g==} peerDependencies: @@ -1237,6 +2169,9 @@ packages: graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + hachure-fill@0.5.2: + resolution: {integrity: sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg==} + has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} @@ -1272,16 +2207,57 @@ packages: idb@8.0.2: resolution: {integrity: sha512-CX70rYhx7GDDQzwwQMDwF6kDRQi5vVs6khHUumDrMecBylKkwvZ8HWvKV08AGb7VbpoGCWUQ4aHzNDgoUiOIUg==} + ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + + image-blob-reduce@3.0.1: + resolution: {integrity: sha512-/VmmWgIryG/wcn4TVrV7cC4mlfUC/oyiKIfSg5eVM3Ten/c1c34RJhMYKCWTnoSMHSqXLt3tsrBR4Q2HInvN+Q==} + + immediate@3.3.0: + resolution: {integrity: sha512-HR7EVodfFUdQCTIeySw+WDRFJlPcLOJbXfwwZ7Oom6tjsvZ3bOkCDJHehQC3nxJrv7+f9XecwazynjU8e4Vw3Q==} + + immutable@4.3.7: + resolution: {integrity: sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==} + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + internmap@1.0.1: + resolution: {integrity: sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==} + + internmap@2.0.3: + resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==} + engines: {node: '>=12'} + + is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + is-fullwidth-code-point@3.0.0: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + is-potential-custom-element-name@1.0.1: resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + isomorphic.js@0.2.5: + resolution: {integrity: sha512-PIeMbHqMt4DnUP3MA/Flc0HElYjMXArsw1qwJZcm9sqR8mq3l8NYizFMty0pWwE/tzIGH3EKK5+jes5mAr85yw==} + jackspeak@3.4.3: resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} @@ -1289,6 +2265,24 @@ packages: resolution: {integrity: sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==} hasBin: true + jotai-scope@0.7.2: + resolution: {integrity: sha512-Gwed97f3dDObrO43++2lRcgOqw4O2sdr4JCjP/7eHK1oPACDJ7xKHGScpJX9XaflU+KBHXF+VhwECnzcaQiShg==} + peerDependencies: + jotai: '>=2.9.2' + react: '>=17.0.0' + + jotai@2.11.0: + resolution: {integrity: sha512-zKfoBBD1uDw3rljwHkt0fWuja1B76R7CjznuBO+mSX6jpsO1EBeWNRKpeaQho9yPI/pvCv4recGfgOXGxwPZvQ==} + engines: {node: '>=12.20.0'} + peerDependencies: + '@types/react': '>=17.0.0' + react: '>=17.0.0' + peerDependenciesMeta: + '@types/react': + optional: true + react: + optional: true + js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -1311,9 +2305,77 @@ packages: engines: {node: '>=6'} hasBin: true + katex@0.16.21: + resolution: {integrity: sha512-XvqR7FgOHtWupfMiigNzmh+MgUVmDGU2kXZm899ZkPfcuoPuFxyHmXsgATDpFZDAXCI8tvinaVcDo8PIIJSo4A==} + hasBin: true + + khroma@2.1.0: + resolution: {integrity: sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw==} + + kleur@4.1.5: + resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} + engines: {node: '>=6'} + kolorist@1.8.0: resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==} + layout-base@1.0.2: + resolution: {integrity: sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==} + + level-codec@9.0.2: + resolution: {integrity: sha512-UyIwNb1lJBChJnGfjmO0OR+ezh2iVu1Kas3nvBS/BzGnx79dv6g7unpKIDNPMhfdTEGoc7mC8uAu51XEtX+FHQ==} + engines: {node: '>=6'} + deprecated: Superseded by level-transcoder (https://github.com/Level/community#faq) + + level-concat-iterator@2.0.1: + resolution: {integrity: sha512-OTKKOqeav2QWcERMJR7IS9CUo1sHnke2C0gkSmcR7QuEtFNLLzHQAvnMw8ykvEcv0Qtkg0p7FOwP1v9e5Smdcw==} + engines: {node: '>=6'} + deprecated: Superseded by abstract-level (https://github.com/Level/community#faq) + + level-errors@2.0.1: + resolution: {integrity: sha512-UVprBJXite4gPS+3VznfgDSU8PTRuVX0NXwoWW50KLxd2yw4Y1t2JUR5In1itQnudZqRMT9DlAM3Q//9NCjCFw==} + engines: {node: '>=6'} + deprecated: Superseded by abstract-level (https://github.com/Level/community#faq) + + level-iterator-stream@4.0.2: + resolution: {integrity: sha512-ZSthfEqzGSOMWoUGhTXdX9jv26d32XJuHz/5YnuHZzH6wldfWMOVwI9TBtKcya4BKTyTt3XVA0A3cF3q5CY30Q==} + engines: {node: '>=6'} + + level-js@5.0.2: + resolution: {integrity: sha512-SnBIDo2pdO5VXh02ZmtAyPP6/+6YTJg2ibLtl9C34pWvmtMEmRTWpra+qO/hifkUtBTOtfx6S9vLDjBsBK4gRg==} + deprecated: Superseded by browser-level (https://github.com/Level/community#faq) + + level-packager@5.1.1: + resolution: {integrity: sha512-HMwMaQPlTC1IlcwT3+swhqf/NUO+ZhXVz6TY1zZIIZlIR0YSn8GtAAWmIvKjNY16ZkEg/JcpAuQskxsXqC0yOQ==} + engines: {node: '>=6'} + deprecated: Superseded by abstract-level (https://github.com/Level/community#faq) + + level-supports@1.0.1: + resolution: {integrity: sha512-rXM7GYnW8gsl1vedTJIbzOrRv85c/2uCMpiiCzO2fndd06U/kUXEEU9evYn4zFggBOg36IsBW8LzqIpETwwQzg==} + engines: {node: '>=6'} + + level@6.0.1: + resolution: {integrity: sha512-psRSqJZCsC/irNhfHzrVZbmPYXDcEYhA5TVNwr+V92jF44rbf86hqGp8fiT702FyiArScYIlPSBTDUASCVNSpw==} + engines: {node: '>=8.6.0'} + + leveldown@5.6.0: + resolution: {integrity: sha512-iB8O/7Db9lPaITU1aA2txU/cBEXAt4vWwKQRrrWuS6XDgbP4QZGj9BL2aNbwb002atoQ/lIotJkfyzz+ygQnUQ==} + engines: {node: '>=8.6.0'} + deprecated: Superseded by classic-level (https://github.com/Level/community#faq) + + levelup@4.4.0: + resolution: {integrity: sha512-94++VFO3qN95cM/d6eBXvd894oJE0w3cInq9USsyQzzoJxmiYzPAocNcuGCPGGjoXqDVJcr3C1jzt1TSjyaiLQ==} + engines: {node: '>=6'} + deprecated: Superseded by abstract-level (https://github.com/Level/community#faq) + + lexical@0.28.0: + resolution: {integrity: sha512-dLE3O1PZg0TlZxRQo9YDpjCjDUj8zluGyBO9MHdjo21qZmMUNrxQPeCRt8fn2s5l4HKYFQ1YNgl7k1pOJB/vZQ==} + + lib0@0.2.99: + resolution: {integrity: sha512-vwztYuUf1uf/1zQxfzRfO5yzfNKhTtgOByCruuiQQxWQXnPb8Itaube5ylofcV0oM0aKal9Mv+S1s1Ky0UYP1w==} + engines: {node: '>=16'} + hasBin: true + lightningcss-darwin-arm64@1.29.2: resolution: {integrity: sha512-cK/eMabSViKn/PG8U/a7aCorpeKLMlK0bQeNHmdb7qUnBkNPnL+oV5DjJUo0kqWsJUapZsM4jCfYItbqBDvlcA==} engines: {node: '>= 12.0.0'} @@ -1378,9 +2440,22 @@ packages: resolution: {integrity: sha512-6b6gd/RUXKaw5keVdSEtqFVdzWnU5jMxTUjA2bVcMNPLwSQ08Sv/UodBVtETLCn7k4S1Ibxwh7k68IwLZPgKaA==} engines: {node: '>= 12.0.0'} + lodash-es@4.17.21: + resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==} + + lodash.debounce@4.0.8: + resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} + lodash.sortby@4.7.0: resolution: {integrity: sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==} + lodash.throttle@4.1.1: + resolution: {integrity: sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==} + + loose-envify@1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + loupe@3.1.3: resolution: {integrity: sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==} @@ -1390,6 +2465,9 @@ packages: lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + ltgt@2.2.1: + resolution: {integrity: sha512-AI2r85+4MquTw9ZYqabu4nMwy9Oftlfa/e/52t9IjtfG+mGBbTNdAoZ3RQKLHR6r0wQnwZnPIEh/Ya6XTWAKNA==} + lucide-react@0.483.0: resolution: {integrity: sha512-WldsY17Qb/T3VZdMnVQ9C3DDIP7h1ViDTHVdVGnLZcvHNg30zH/MTQ04RTORjexoGmpsXroiQXZ4QyR0kBy0FA==} peerDependencies: @@ -1406,6 +2484,78 @@ packages: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} + mdast-util-from-markdown@1.3.1: + resolution: {integrity: sha512-4xTO/M8c82qBcnQc1tgpNtubGUW/Y1tBQ1B0i5CtSoelOLKFYlElIr3bvgREYYO5iRqbMY1YuqZng0GVOI8Qww==} + + mdast-util-to-string@3.2.0: + resolution: {integrity: sha512-V4Zn/ncyN1QNSqSBxTrMOLpjr+IKdHl2v3KVLoWmDPscP4r9GcCi71gjgvUV1SFSKh92AjAG4peFuBl2/YgCJg==} + + mermaid@10.9.3: + resolution: {integrity: sha512-V80X1isSEvAewIL3xhmz/rVmc27CVljcsbWxkxlWJWY/1kQa4XOABqpDl2qQLGKzpKm6WbTfUEKImBlUfFYArw==} + + micromark-core-commonmark@1.1.0: + resolution: {integrity: sha512-BgHO1aRbolh2hcrzL2d1La37V0Aoz73ymF8rAcKnohLy93titmv62E0gP8Hrx9PKcKrqCZ1BbLGbP3bEhoXYlw==} + + micromark-factory-destination@1.1.0: + resolution: {integrity: sha512-XaNDROBgx9SgSChd69pjiGKbV+nfHGDPVYFs5dOoDd7ZnMAE+Cuu91BCpsY8RT2NP9vo/B8pds2VQNCLiu0zhg==} + + micromark-factory-label@1.1.0: + resolution: {integrity: sha512-OLtyez4vZo/1NjxGhcpDSbHQ+m0IIGnT8BoPamh+7jVlzLJBH98zzuCoUeMxvM6WsNeh8wx8cKvqLiPHEACn0w==} + + micromark-factory-space@1.1.0: + resolution: {integrity: sha512-cRzEj7c0OL4Mw2v6nwzttyOZe8XY/Z8G0rzmWQZTBi/jjwyw/U4uqKtUORXQrR5bAZZnbTI/feRV/R7hc4jQYQ==} + + micromark-factory-title@1.1.0: + resolution: {integrity: sha512-J7n9R3vMmgjDOCY8NPw55jiyaQnH5kBdV2/UXCtZIpnHH3P6nHUKaH7XXEYuWwx/xUJcawa8plLBEjMPU24HzQ==} + + micromark-factory-whitespace@1.1.0: + resolution: {integrity: sha512-v2WlmiymVSp5oMg+1Q0N1Lxmt6pMhIHD457whWM7/GUlEks1hI9xj5w3zbc4uuMKXGisksZk8DzP2UyGbGqNsQ==} + + micromark-util-character@1.2.0: + resolution: {integrity: sha512-lXraTwcX3yH/vMDaFWCQJP1uIszLVebzUa3ZHdrgxr7KEU/9mL4mVgCpGbyhvNLNlauROiNUq7WN5u7ndbY6xg==} + + micromark-util-chunked@1.1.0: + resolution: {integrity: sha512-Ye01HXpkZPNcV6FiyoW2fGZDUw4Yc7vT0E9Sad83+bEDiCJ1uXu0S3mr8WLpsz3HaG3x2q0HM6CTuPdcZcluFQ==} + + micromark-util-classify-character@1.1.0: + resolution: {integrity: sha512-SL0wLxtKSnklKSUplok1WQFoGhUdWYKggKUiqhX+Swala+BtptGCu5iPRc+xvzJ4PXE/hwM3FNXsfEVgoZsWbw==} + + micromark-util-combine-extensions@1.1.0: + resolution: {integrity: sha512-Q20sp4mfNf9yEqDL50WwuWZHUrCO4fEyeDCnMGmG5Pr0Cz15Uo7KBs6jq+dq0EgX4DPwwrh9m0X+zPV1ypFvUA==} + + micromark-util-decode-numeric-character-reference@1.1.0: + resolution: {integrity: sha512-m9V0ExGv0jB1OT21mrWcuf4QhP46pH1KkfWy9ZEezqHKAxkj4mPCy3nIH1rkbdMlChLHX531eOrymlwyZIf2iw==} + + micromark-util-decode-string@1.1.0: + resolution: {integrity: sha512-YphLGCK8gM1tG1bd54azwyrQRjCFcmgj2S2GoJDNnh4vYtnL38JS8M4gpxzOPNyHdNEpheyWXCTnnTDY3N+NVQ==} + + micromark-util-encode@1.1.0: + resolution: {integrity: sha512-EuEzTWSTAj9PA5GOAs992GzNh2dGQO52UvAbtSOMvXTxv3Criqb6IOzJUBCmEqrrXSblJIJBbFFv6zPxpreiJw==} + + micromark-util-html-tag-name@1.2.0: + resolution: {integrity: sha512-VTQzcuQgFUD7yYztuQFKXT49KghjtETQ+Wv/zUjGSGBioZnkA4P1XXZPT1FHeJA6RwRXSF47yvJ1tsJdoxwO+Q==} + + micromark-util-normalize-identifier@1.1.0: + resolution: {integrity: sha512-N+w5vhqrBihhjdpM8+5Xsxy71QWqGn7HYNUvch71iV2PM7+E3uWGox1Qp90loa1ephtCxG2ftRV/Conitc6P2Q==} + + micromark-util-resolve-all@1.1.0: + resolution: {integrity: sha512-b/G6BTMSg+bX+xVCshPTPyAu2tmA0E4X98NSR7eIbeC6ycCqCeE7wjfDIgzEbkzdEVJXRtOG4FbEm/uGbCRouA==} + + micromark-util-sanitize-uri@1.2.0: + resolution: {integrity: sha512-QO4GXv0XZfWey4pYFndLUKEAktKkG5kZTdUNaTAkzbuJxn2tNBOr+QtxR2XpWaMhbImT2dPzyLrPXLlPhph34A==} + + micromark-util-subtokenize@1.1.0: + resolution: {integrity: sha512-kUQHyzRoxvZO2PuLzMt2P/dwVsTiivCK8icYTeR+3WgbuPqfHgPPy7nFKbeqRivBvn/3N3GBiNC+JRTMSxEC7A==} + + micromark-util-symbol@1.1.0: + resolution: {integrity: sha512-uEjpEYY6KMs1g7QfJ2eX1SQEV+ZT4rUD3UcF6l57acZvLNK7PBZL+ty82Z1qhK1/yXIY4bdx04FKMgR0g4IAag==} + + micromark-util-types@1.1.0: + resolution: {integrity: sha512-ukRBgie8TIAcacscVHSiddHjO4k/q3pnedmzMQ4iwDcK0FtFCohKOlFbaOL/mPgfnPsL3C1ZyxJa4sbWrBl3jg==} + + micromark@3.2.0: + resolution: {integrity: sha512-uD66tJj54JLYq0De10AhWycZWGQNUvDI55xPgk2sQM5kn1JYlhbCMTtEeT27+vAhW2FBQxLlOmS3pmA7/2z4aA==} + mime-db@1.52.0: resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} engines: {node: '>= 0.6'} @@ -1422,26 +2572,70 @@ packages: resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} engines: {node: '>=16 || 14 >=14.17'} + mri@1.2.0: + resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} + engines: {node: '>=4'} + ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + multimath@2.0.0: + resolution: {integrity: sha512-toRx66cAMJ+Ccz7pMIg38xSIrtnbozk0dchXezwQDMgQmbGpfxjtv68H+L00iFL8hxDaVjrmwAFSb3I6bg8Q2g==} + nanoid@3.3.11: resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true + nanoid@3.3.3: + resolution: {integrity: sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + nanoid@4.0.2: + resolution: {integrity: sha512-7ZtY5KTCNheRGfEFxnedV5zFiORN1+Y1N6zvPTnHQd8ENUvfaDBeuJDZb2bN/oXwXxu3qkTXDzy57W5vAmDTBw==} + engines: {node: ^14 || ^16 || >=18} + hasBin: true + + napi-macros@2.0.0: + resolution: {integrity: sha512-A0xLykHtARfueITVDernsAWdtIMbOJgKgcluwENp3AlsKN/PloyO10HtmoqnFAQAcxPkgZN7wdfPfEd0zNGxbg==} + + node-gyp-build@4.1.1: + resolution: {integrity: sha512-dSq1xmcPDKPZ2EED2S6zw/b9NKsqzXRE6dVr8TVQnI3FJOTteUMuqF3Qqs6LZg+mLGYJWqQzMbIjMtJqTv87nQ==} + hasBin: true + node-releases@2.0.19: resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==} + non-layered-tidy-tree-layout@2.0.2: + resolution: {integrity: sha512-gkXMxRzUH+PB0ax9dUN0yYF0S25BqeAYqhgMaLUFmpXLEk7Fcu8f4emJuOAY0V8kjDICxROIKsTAKsV/v355xw==} + + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + nwsapi@2.2.19: resolution: {integrity: sha512-94bcyI3RsqiZufXjkr3ltkI86iEl+I7uiHVDtcq9wJUTwYQJ5odHDeSzkkrRzi80jJ8MaeZgqKjH1bAWAFw9bA==} + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + open-color@1.9.1: + resolution: {integrity: sha512-vCseG/EQ6/RcvxhUcGJiHViOgrtz4x0XbZepXvKik66TMGkvbmjeJrKFyBEx6daG5rNyyd14zYXhz0hZVwQFOw==} + package-json-from-dist@1.0.1: resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} + pako@2.0.3: + resolution: {integrity: sha512-WjR1hOeg+kki3ZIOjaf4b5WVcay1jaliKSYiEaB1XzwhMQZJxRdQRv0V31EKBYlxb4T7SK3hjfc/jxyU64BoSw==} + parse5@7.2.1: resolution: {integrity: sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==} + path-data-parser@0.1.0: + resolution: {integrity: sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w==} + path-key@3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} @@ -1457,13 +2651,46 @@ packages: resolution: {integrity: sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==} engines: {node: '>= 14.16'} + perfect-freehand@1.2.0: + resolution: {integrity: sha512-h/0ikF1M3phW7CwpZ5MMvKnfpHficWoOEyr//KVNTxV4F6deRK1eYMtHyBKEAKFK0aXIEUK9oBvlF6PNXMDsAw==} + + pica@7.1.1: + resolution: {integrity: sha512-WY73tMvNzXWEld2LicT9Y260L43isrZ85tPuqRyvtkljSDLmnNFQmZICt4xUJMVulmcc6L9O7jbBrtx3DOz/YQ==} + picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + png-chunk-text@1.0.0: + resolution: {integrity: sha512-DEROKU3SkkLGWNMzru3xPVgxyd48UGuMSZvioErCure6yhOc/pRH2ZV+SEn7nmaf7WNf3NdIpH+UTrRdKyq9Lw==} + + png-chunks-encode@1.0.0: + resolution: {integrity: sha512-J1jcHgbQRsIIgx5wxW9UmCymV3wwn4qCCJl6KYgEU/yHCh/L2Mwq/nMOkRPtmV79TLxRZj5w3tH69pvygFkDqA==} + + png-chunks-extract@1.0.0: + resolution: {integrity: sha512-ZiVwF5EJ0DNZyzAqld8BP1qyJBaGOFaq9zl579qfbkcmOwWLLO4I9L8i2O4j3HkI6/35i0nKG2n+dZplxiT89Q==} + + points-on-curve@0.2.0: + resolution: {integrity: sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A==} + + points-on-curve@1.0.1: + resolution: {integrity: sha512-3nmX4/LIiyuwGLwuUrfhTlDeQFlAhi7lyK/zcRNGhalwapDWgAGR82bUpmn2mA03vII3fvNCG8jAONzKXwpxAg==} + + points-on-path@0.2.1: + resolution: {integrity: sha512-25ClnWWuw7JbWZcgqY/gJ4FQWadKxGWk+3kR/7kD0tCaDtPPMj7oHu2ToLaVhfpnHrZzYby2w6tUA0eOIuUg8g==} + postcss@8.5.3: resolution: {integrity: sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==} engines: {node: ^10 || ^12 || >=14} + prettier@3.5.3: + resolution: {integrity: sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==} + engines: {node: '>=14'} + hasBin: true + pretty-bytes@6.1.1: resolution: {integrity: sha512-mQUvGU6aUFQ+rNvTIAcZuWGRT9a6f6Yrg9bHs4ImKF+HZCEK+plBvnAZYSIQztknZF2qnzNtr6F8s0+IuptdlQ==} engines: {node: ^14.13.1 || >=16.0.0} @@ -1472,15 +2699,37 @@ packages: resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + prismjs@1.30.0: + resolution: {integrity: sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==} + engines: {node: '>=6'} + + prop-types@15.8.1: + resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + + prr@1.0.1: + resolution: {integrity: sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==} + punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} + pwacompat@2.0.17: + resolution: {integrity: sha512-6Du7IZdIy7cHiv7AhtDy4X2QRM8IAD5DII69mt5qWibC2d15ZU8DmBG1WdZKekG11cChSu4zkSUGPF9sweOl6w==} + react-dom@19.0.0: resolution: {integrity: sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ==} peerDependencies: react: ^19.0.0 + react-error-boundary@3.1.4: + resolution: {integrity: sha512-uM9uPzZJTF6wRQORmSrvOIgt4lJ9MC1sNgEOj2XGsDTRE4kmpWxg7ENK9EWNKJRMAOY9z0MuF4yIfl6gp4sotA==} + engines: {node: '>=10', npm: '>=6'} + peerDependencies: + react: '>=16.13.1' + + 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==} @@ -1498,6 +2747,16 @@ packages: '@types/react': optional: true + react-remove-scroll@2.5.5: + resolution: {integrity: sha512-ImKhrzJJsyXJfBZ4bzu8Bwpka14c/fQt0k+cyFp/PBhTfyDnU5hjOtM4AG/0AMyy8oKzOTR0lDgJIM7pYXI0kw==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + react-remove-scroll@2.6.3: resolution: {integrity: sha512-pnAi91oOk8g8ABQKGF5/M9qxmmOPxaAnopyTHYfqYEwJhyFrbbBtHuSgtKEoH0jpcxx5o3hXqH1mNd9/Oi+8iQ==} engines: {node: '>=10'} @@ -1522,20 +2781,52 @@ packages: resolution: {integrity: sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==} engines: {node: '>=0.10.0'} + readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + + readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + regenerator-runtime@0.14.1: resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + + robust-predicates@3.0.2: + resolution: {integrity: sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==} + rollup@4.36.0: resolution: {integrity: sha512-zwATAXNQxUcd40zgtQG0ZafcRK4g004WtEl7kbuhTWPvf07PsfohXl39jVUvPF7jvNAIkKPQ2XrsDlWuxBd++Q==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true + roughjs@4.6.4: + resolution: {integrity: sha512-s6EZ0BntezkFYMf/9mGn7M8XGIoaav9QQBCnJROWB3brUWQ683Q2LbRD/hq0Z3bAJ/9NVpU/5LpiTWvQMyLDhw==} + rrweb-cssom@0.8.0: resolution: {integrity: sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==} + rw@1.3.3: + resolution: {integrity: sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==} + + sade@1.8.1: + resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==} + engines: {node: '>=6'} + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + sass@1.51.0: + resolution: {integrity: sha512-haGdpTgywJTvHC2b91GSq+clTKGbtkkZmVAb82jZQN/wTy6qs8DdFm2lhEQbEwrY0QDRgSQ3xDurqM977C3noA==} + engines: {node: '>=12.0.0'} + hasBin: true + saxes@6.0.0: resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} engines: {node: '>=v12.22.7'} @@ -1580,6 +2871,9 @@ packages: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} + sliced@1.0.1: + resolution: {integrity: sha512-VZBmZP8WU3sMOZm1bdgTadsQbcscK0UM8oKxKVBs4XAhUo2Xxzm/OFMGBkPusxw9xL3Uy8LrzEqGqJhclsr0yA==} + solid-js@1.9.5: resolution: {integrity: sha512-ogI3DaFcyn6UhYhrgcyRAMbu/buBJitYQASZz5WzfQVPP10RD2AbCoRZ517psnezrasyCbWzIxZ6kVqet768xw==} @@ -1605,6 +2899,9 @@ packages: resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} engines: {node: '>=12'} + string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + strip-ansi@6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} @@ -1613,6 +2910,9 @@ packages: resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} engines: {node: '>=12'} + stylis@4.3.6: + resolution: {integrity: sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==} + supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} @@ -1661,6 +2961,10 @@ packages: resolution: {integrity: sha512-aRGIbCIF3teodtUFAYSdQONVmDRy21REM3o6JnqWn5ZkQBJJ4gHxhw6OfwQ+WkSAi3ASamrS4N4nyazWx6uTYg==} hasBin: true + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + tough-cookie@5.1.2: resolution: {integrity: sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==} engines: {node: '>=16'} @@ -1672,9 +2976,21 @@ packages: resolution: {integrity: sha512-IUWnUK7ADYR5Sl1fZlO1INDUhVhatWl7BtJWsIhwJ0UAK7ilzzIa8uIqOO/aYVWHZPJkKbEL+362wrzoeRF7bw==} engines: {node: '>=18'} + ts-dedent@2.2.0: + resolution: {integrity: sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==} + engines: {node: '>=6.10'} + tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + tsx@4.19.3: + resolution: {integrity: sha512-4H8vUNGNjQ4V2EOoGw005+c+dGuPSnhpPBPHBtsZdGZBk/iJb4kguGlPWaZTZ3q5nMtFOEsY0nRDlh9PJyd6SQ==} + engines: {node: '>=18.0.0'} + hasBin: true + + tunnel-rat@0.1.2: + resolution: {integrity: sha512-lR5VHmkPhzdhrM092lI2nACsLO4QubF0/yoOhzX7c+wIpbN1GjHNzCc91QlpxBi+cnx8vVJ+Ur6vL5cEoQPFpQ==} + tw-animate-css@1.2.4: resolution: {integrity: sha512-yt+HkJB41NAvOffe4NweJU6fLqAlVx/mBX6XmHRp15kq0JxTtOKaIw8pVSWM1Z+n2nXtyi7cW6C9f0WG/F/QAQ==} @@ -1683,6 +2999,13 @@ packages: engines: {node: '>=14.17'} hasBin: true + unist-util-stringify-position@3.0.3: + resolution: {integrity: sha512-k5GzIBZ/QatR8N5X2y+drfpWG8IDBzdnVj6OInRNWm1oXrzydiaAT2OQiA8DPRRZyAKb9b6I2a6PxYklZD0gKg==} + + unplugin@2.2.0: + resolution: {integrity: sha512-m1ekpSwuOT5hxkJeZGRxO7gXbXT3gF26NjQ7GdVHoLoF8/nopLcd/QfPigpCy7i51oFHiRJg/CyHhj4vs2+KGw==} + engines: {node: '>=18.12.0'} + update-browserslist-db@1.1.3: resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==} hasBin: true @@ -1714,6 +3037,18 @@ packages: peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + uuid@9.0.1: + resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} + hasBin: true + + uvu@0.5.6: + resolution: {integrity: sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA==} + engines: {node: '>=8'} + hasBin: true + vite-node@3.0.9: resolution: {integrity: sha512-w3Gdx7jDcuT9cNn9jExXgOyKmf5UOTb6WMHz8LGAm54eS1Elf5OuBhCxl6zJxGhEeIkgsE1WbHuoL0mj/UXqXg==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} @@ -1794,6 +3129,9 @@ packages: web-vitals@4.2.4: resolution: {integrity: sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw==} + web-worker@1.5.0: + resolution: {integrity: sha512-RiMReJrTAiA+mBjGONMnjVDP2u3p9R1vkcGz6gDIrOMT3oGuYwX2WRMYI9ipkphSuE5XKEhydbhNEJh4NY9mlw==} + webidl-conversions@4.0.2: resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==} @@ -1801,6 +3139,12 @@ packages: resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} engines: {node: '>=12'} + webpack-virtual-modules@0.6.2: + resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==} + + webworkify@1.5.0: + resolution: {integrity: sha512-AMcUeyXAhbACL8S2hqqdqOLqvJ8ylmIbNwUIqQujRSouf4+eUFaXbG6F1Rbu+srlJMmxQWsiU7mOJi0nMBfM1g==} + whatwg-encoding@3.1.1: resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} engines: {node: '>=18'} @@ -1834,6 +3178,17 @@ packages: resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} engines: {node: '>=12'} + ws@6.2.3: + resolution: {integrity: sha512-jmTjYU0j60B+vHey6TfR3Z7RD61z/hmxBS3VMSGIrroOWXQEneK1zNuotOUrGyBHQj0yrpsLHPWtigEFd13ndA==} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ^5.0.2 + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + ws@8.18.1: resolution: {integrity: sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==} engines: {node: '>=10.0.0'} @@ -1853,12 +3208,65 @@ packages: xmlchars@2.2.0: resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} + xtend@4.0.2: + resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} + engines: {node: '>=0.4'} + + y-excalidraw@2.0.12: + resolution: {integrity: sha512-/dp0MUSD7WC4TFXsv9DyXxeg+CQoSM4iwh9UpLx8+VFwAg47F3O9KW62C/Jkuq7Rt3y9MAmKC2rE8beZOHCGaw==} + peerDependencies: + '@excalidraw/excalidraw': ^0.17.6 + yjs: ^13.6.19 + + y-indexeddb@9.0.12: + resolution: {integrity: sha512-9oCFRSPPzBK7/w5vOkJBaVCQZKHXB/v6SIT+WYhnJxlEC61juqG0hBrAf+y3gmSMLFLwICNH9nQ53uscuse6Hg==} + engines: {node: '>=16.0.0', npm: '>=8.0.0'} + peerDependencies: + yjs: ^13.0.0 + + y-leveldb@0.1.2: + resolution: {integrity: sha512-6ulEn5AXfXJYi89rXPEg2mMHAyyw8+ZfeMMdOtBbV8FJpQ1NOrcgi6DTAcXof0dap84NjHPT2+9d0rb6cFsjEg==} + peerDependencies: + yjs: ^13.0.0 + + y-protocols@1.0.6: + resolution: {integrity: sha512-vHRF2L6iT3rwj1jub/K5tYcTT/mEYDUppgNPXwp8fmLpui9f7Yeq3OEtTLVF012j39QnV+KEQpNqoN7CWU7Y9Q==} + engines: {node: '>=16.0.0', npm: '>=8.0.0'} + peerDependencies: + yjs: ^13.0.0 + + y-websocket@2.1.0: + resolution: {integrity: sha512-WHYDRqomaGkkaujtowCDwL8KYk+t1zQCGIgKyvxvchhjTQlMgWXRHJK+FDEcWmHA7I7o/4fy0eniOrtmz0e4mA==} + engines: {node: '>=16.0.0', npm: '>=8.0.0'} + hasBin: true + peerDependencies: + yjs: ^13.5.6 + yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + yjs@13.6.24: + resolution: {integrity: sha512-xn/pYLTZa3uD1uDG8lpxfLRo5SR/rp0frdASOl2a71aYNvUXdWcLtVL91s2y7j+Q8ppmjZ9H3jsGVgoFMbT2VA==} + engines: {node: '>=16.0.0', npm: '>=8.0.0'} + zod@3.24.2: resolution: {integrity: sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ==} + zustand@4.5.6: + resolution: {integrity: sha512-ibr/n1hBzLLj5Y+yUcU7dYw8p6WnIVzdJbnX+1YpaScvZVF2ziugqHs+LAmHw4lWO9c/zRj+K1ncgWDQuthEdQ==} + engines: {node: '>=12.7.0'} + peerDependencies: + '@types/react': '>=16.8' + immer: '>=9.0.6' + react: '>=16.8' + peerDependenciesMeta: + '@types/react': + optional: true + immer: + optional: true + react: + optional: true + snapshots: '@ampproject/remapping@2.3.0': @@ -1951,6 +3359,16 @@ snapshots: dependencies: '@babel/types': 7.26.10 + '@babel/plugin-syntax-jsx@7.25.9(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.26.5 + + '@babel/plugin-syntax-typescript@7.25.9(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.26.5 + '@babel/plugin-transform-react-jsx-self@7.25.9(@babel/core@7.26.10)': dependencies: '@babel/core': 7.26.10 @@ -1988,6 +3406,8 @@ snapshots: '@babel/helper-string-parser': 7.25.9 '@babel/helper-validator-identifier': 7.25.9 + '@braintree/sanitize-url@6.0.2': {} + '@csstools/color-helpers@5.0.2': {} '@csstools/css-calc@2.1.2(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3)': @@ -2083,6 +3503,61 @@ snapshots: '@esbuild/win32-x64@0.25.1': optional: true + '@excalidraw/excalidraw@0.18.0(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@braintree/sanitize-url': 6.0.2 + '@excalidraw/laser-pointer': 1.3.1 + '@excalidraw/mermaid-to-excalidraw': 1.1.2 + '@excalidraw/random-username': 1.1.0 + '@radix-ui/react-popover': 1.1.6(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-tabs': 1.0.2(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + browser-fs-access: 0.29.1 + canvas-roundrect-polyfill: 0.0.1 + clsx: 1.1.1 + cross-env: 7.0.3 + es6-promise-pool: 2.5.0 + fractional-indexing: 3.2.0 + fuzzy: 0.1.3 + image-blob-reduce: 3.0.1 + jotai: 2.11.0(@types/react@19.0.11)(react@19.0.0) + jotai-scope: 0.7.2(jotai@2.11.0(@types/react@19.0.11)(react@19.0.0))(react@19.0.0) + lodash.debounce: 4.0.8 + lodash.throttle: 4.1.1 + nanoid: 3.3.3 + open-color: 1.9.1 + pako: 2.0.3 + perfect-freehand: 1.2.0 + pica: 7.1.1 + png-chunk-text: 1.0.0 + png-chunks-encode: 1.0.0 + png-chunks-extract: 1.0.0 + points-on-curve: 1.0.1 + pwacompat: 2.0.17 + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + roughjs: 4.6.4 + sass: 1.51.0 + tunnel-rat: 0.1.2(@types/react@19.0.11)(react@19.0.0) + transitivePeerDependencies: + - '@types/react' + - '@types/react-dom' + - immer + - supports-color + + '@excalidraw/laser-pointer@1.3.1': {} + + '@excalidraw/markdown-to-text@0.1.2': {} + + '@excalidraw/mermaid-to-excalidraw@1.1.2': + dependencies: + '@excalidraw/markdown-to-text': 0.1.2 + mermaid: 10.9.3 + nanoid: 4.0.2 + transitivePeerDependencies: + - supports-color + + '@excalidraw/random-username@1.1.0': {} + '@floating-ui/core@1.6.9': dependencies: '@floating-ui/utils': 0.2.9 @@ -2126,11 +3601,184 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.0 + '@lexical/clipboard@0.28.0': + dependencies: + '@lexical/html': 0.28.0 + '@lexical/list': 0.28.0 + '@lexical/selection': 0.28.0 + '@lexical/utils': 0.28.0 + lexical: 0.28.0 + + '@lexical/code@0.28.0': + dependencies: + '@lexical/utils': 0.28.0 + lexical: 0.28.0 + prismjs: 1.30.0 + + '@lexical/devtools-core@0.28.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@lexical/html': 0.28.0 + '@lexical/link': 0.28.0 + '@lexical/mark': 0.28.0 + '@lexical/table': 0.28.0 + '@lexical/utils': 0.28.0 + lexical: 0.28.0 + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + + '@lexical/dragon@0.28.0': + dependencies: + lexical: 0.28.0 + + '@lexical/hashtag@0.28.0': + dependencies: + '@lexical/utils': 0.28.0 + lexical: 0.28.0 + + '@lexical/history@0.28.0': + dependencies: + '@lexical/utils': 0.28.0 + lexical: 0.28.0 + + '@lexical/html@0.28.0': + dependencies: + '@lexical/selection': 0.28.0 + '@lexical/utils': 0.28.0 + lexical: 0.28.0 + + '@lexical/link@0.28.0': + dependencies: + '@lexical/utils': 0.28.0 + lexical: 0.28.0 + + '@lexical/list@0.28.0': + dependencies: + '@lexical/selection': 0.28.0 + '@lexical/utils': 0.28.0 + lexical: 0.28.0 + + '@lexical/mark@0.28.0': + dependencies: + '@lexical/utils': 0.28.0 + lexical: 0.28.0 + + '@lexical/markdown@0.28.0': + dependencies: + '@lexical/code': 0.28.0 + '@lexical/link': 0.28.0 + '@lexical/list': 0.28.0 + '@lexical/rich-text': 0.28.0 + '@lexical/text': 0.28.0 + '@lexical/utils': 0.28.0 + lexical: 0.28.0 + + '@lexical/offset@0.28.0': + dependencies: + lexical: 0.28.0 + + '@lexical/overflow@0.28.0': + dependencies: + lexical: 0.28.0 + + '@lexical/plain-text@0.28.0': + dependencies: + '@lexical/clipboard': 0.28.0 + '@lexical/selection': 0.28.0 + '@lexical/utils': 0.28.0 + lexical: 0.28.0 + + '@lexical/react@0.28.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(yjs@13.6.24)': + dependencies: + '@lexical/devtools-core': 0.28.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@lexical/dragon': 0.28.0 + '@lexical/hashtag': 0.28.0 + '@lexical/history': 0.28.0 + '@lexical/link': 0.28.0 + '@lexical/list': 0.28.0 + '@lexical/mark': 0.28.0 + '@lexical/markdown': 0.28.0 + '@lexical/overflow': 0.28.0 + '@lexical/plain-text': 0.28.0 + '@lexical/rich-text': 0.28.0 + '@lexical/table': 0.28.0 + '@lexical/text': 0.28.0 + '@lexical/utils': 0.28.0 + '@lexical/yjs': 0.28.0(yjs@13.6.24) + lexical: 0.28.0 + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + react-error-boundary: 3.1.4(react@19.0.0) + transitivePeerDependencies: + - yjs + + '@lexical/rich-text@0.28.0': + dependencies: + '@lexical/clipboard': 0.28.0 + '@lexical/selection': 0.28.0 + '@lexical/utils': 0.28.0 + lexical: 0.28.0 + + '@lexical/selection@0.28.0': + dependencies: + lexical: 0.28.0 + + '@lexical/table@0.28.0': + dependencies: + '@lexical/clipboard': 0.28.0 + '@lexical/utils': 0.28.0 + lexical: 0.28.0 + + '@lexical/text@0.28.0': + dependencies: + lexical: 0.28.0 + + '@lexical/utils@0.28.0': + dependencies: + '@lexical/list': 0.28.0 + '@lexical/selection': 0.28.0 + '@lexical/table': 0.28.0 + lexical: 0.28.0 + + '@lexical/yjs@0.28.0(yjs@13.6.24)': + dependencies: + '@lexical/offset': 0.28.0 + '@lexical/selection': 0.28.0 + lexical: 0.28.0 + yjs: 13.6.24 + + '@mdi/js@7.4.47': {} + + '@mdi/react@1.6.1': + dependencies: + prop-types: 15.8.1 + '@pkgjs/parseargs@0.11.0': optional: true + '@radix-ui/primitive@1.0.0': + dependencies: + '@babel/runtime': 7.26.10 + + '@radix-ui/primitive@1.0.1': + dependencies: + '@babel/runtime': 7.26.10 + '@radix-ui/primitive@1.1.1': {} + '@radix-ui/react-alert-dialog@1.1.6(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@radix-ui/primitive': 1.1.1 + '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.11)(react@19.0.0) + '@radix-ui/react-context': 1.1.1(@types/react@19.0.11)(react@19.0.0) + '@radix-ui/react-dialog': 1.1.6(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-slot': 1.1.2(@types/react@19.0.11)(react@19.0.0) + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + optionalDependencies: + '@types/react': 19.0.11 + '@types/react-dom': 19.0.4(@types/react@19.0.11) + '@radix-ui/react-arrow@1.1.2(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': dependencies: '@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) @@ -2140,18 +3788,99 @@ snapshots: '@types/react': 19.0.11 '@types/react-dom': 19.0.4(@types/react@19.0.11) + '@radix-ui/react-avatar@1.1.3(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@radix-ui/react-context': 1.1.1(@types/react@19.0.11)(react@19.0.0) + '@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.11)(react@19.0.0) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.0.11)(react@19.0.0) + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + optionalDependencies: + '@types/react': 19.0.11 + '@types/react-dom': 19.0.4(@types/react@19.0.11) + + '@radix-ui/react-collection@1.0.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@babel/runtime': 7.26.10 + '@radix-ui/react-compose-refs': 1.0.0(react@19.0.0) + '@radix-ui/react-context': 1.0.0(react@19.0.0) + '@radix-ui/react-primitive': 1.0.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-slot': 1.0.1(react@19.0.0) + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + + '@radix-ui/react-collection@1.1.2(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.11)(react@19.0.0) + '@radix-ui/react-context': 1.1.1(@types/react@19.0.11)(react@19.0.0) + '@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-slot': 1.1.2(@types/react@19.0.11)(react@19.0.0) + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + optionalDependencies: + '@types/react': 19.0.11 + '@types/react-dom': 19.0.4(@types/react@19.0.11) + + '@radix-ui/react-compose-refs@1.0.0(react@19.0.0)': + dependencies: + '@babel/runtime': 7.26.10 + react: 19.0.0 + + '@radix-ui/react-compose-refs@1.0.1(@types/react@19.0.11)(react@19.0.0)': + dependencies: + '@babel/runtime': 7.26.10 + react: 19.0.0 + optionalDependencies: + '@types/react': 19.0.11 + '@radix-ui/react-compose-refs@1.1.1(@types/react@19.0.11)(react@19.0.0)': dependencies: react: 19.0.0 optionalDependencies: '@types/react': 19.0.11 + '@radix-ui/react-context@1.0.0(react@19.0.0)': + dependencies: + '@babel/runtime': 7.26.10 + react: 19.0.0 + + '@radix-ui/react-context@1.0.1(@types/react@19.0.11)(react@19.0.0)': + dependencies: + '@babel/runtime': 7.26.10 + react: 19.0.0 + optionalDependencies: + '@types/react': 19.0.11 + '@radix-ui/react-context@1.1.1(@types/react@19.0.11)(react@19.0.0)': dependencies: react: 19.0.0 optionalDependencies: '@types/react': 19.0.11 + '@radix-ui/react-dialog@1.0.5(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@babel/runtime': 7.26.10 + '@radix-ui/primitive': 1.0.1 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@19.0.11)(react@19.0.0) + '@radix-ui/react-context': 1.0.1(@types/react@19.0.11)(react@19.0.0) + '@radix-ui/react-dismissable-layer': 1.0.5(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-focus-guards': 1.0.1(@types/react@19.0.11)(react@19.0.0) + '@radix-ui/react-focus-scope': 1.0.4(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-id': 1.0.1(@types/react@19.0.11)(react@19.0.0) + '@radix-ui/react-portal': 1.0.4(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-presence': 1.0.1(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-slot': 1.0.2(@types/react@19.0.11)(react@19.0.0) + '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@19.0.11)(react@19.0.0) + aria-hidden: 1.2.4 + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + react-remove-scroll: 2.5.5(@types/react@19.0.11)(react@19.0.0) + optionalDependencies: + '@types/react': 19.0.11 + '@types/react-dom': 19.0.4(@types/react@19.0.11) + '@radix-ui/react-dialog@1.1.6(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': dependencies: '@radix-ui/primitive': 1.1.1 @@ -2174,6 +3903,31 @@ snapshots: '@types/react': 19.0.11 '@types/react-dom': 19.0.4(@types/react@19.0.11) + '@radix-ui/react-direction@1.0.0(react@19.0.0)': + dependencies: + '@babel/runtime': 7.26.10 + react: 19.0.0 + + '@radix-ui/react-direction@1.1.0(@types/react@19.0.11)(react@19.0.0)': + dependencies: + react: 19.0.0 + optionalDependencies: + '@types/react': 19.0.11 + + '@radix-ui/react-dismissable-layer@1.0.5(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@babel/runtime': 7.26.10 + '@radix-ui/primitive': 1.0.1 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@19.0.11)(react@19.0.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@19.0.11)(react@19.0.0) + '@radix-ui/react-use-escape-keydown': 1.0.3(@types/react@19.0.11)(react@19.0.0) + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + optionalDependencies: + '@types/react': 19.0.11 + '@types/react-dom': 19.0.4(@types/react@19.0.11) + '@radix-ui/react-dismissable-layer@1.1.5(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': dependencies: '@radix-ui/primitive': 1.1.1 @@ -2187,12 +3941,46 @@ snapshots: '@types/react': 19.0.11 '@types/react-dom': 19.0.4(@types/react@19.0.11) + '@radix-ui/react-dropdown-menu@2.1.6(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@radix-ui/primitive': 1.1.1 + '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.11)(react@19.0.0) + '@radix-ui/react-context': 1.1.1(@types/react@19.0.11)(react@19.0.0) + '@radix-ui/react-id': 1.1.0(@types/react@19.0.11)(react@19.0.0) + '@radix-ui/react-menu': 2.1.6(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.0.11)(react@19.0.0) + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + optionalDependencies: + '@types/react': 19.0.11 + '@types/react-dom': 19.0.4(@types/react@19.0.11) + + '@radix-ui/react-focus-guards@1.0.1(@types/react@19.0.11)(react@19.0.0)': + dependencies: + '@babel/runtime': 7.26.10 + react: 19.0.0 + optionalDependencies: + '@types/react': 19.0.11 + '@radix-ui/react-focus-guards@1.1.1(@types/react@19.0.11)(react@19.0.0)': dependencies: react: 19.0.0 optionalDependencies: '@types/react': 19.0.11 + '@radix-ui/react-focus-scope@1.0.4(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@babel/runtime': 7.26.10 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@19.0.11)(react@19.0.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@19.0.11)(react@19.0.0) + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + optionalDependencies: + '@types/react': 19.0.11 + '@types/react-dom': 19.0.4(@types/react@19.0.11) + '@radix-ui/react-focus-scope@1.1.2(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': dependencies: '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.11)(react@19.0.0) @@ -2204,6 +3992,20 @@ snapshots: '@types/react': 19.0.11 '@types/react-dom': 19.0.4(@types/react@19.0.11) + '@radix-ui/react-id@1.0.0(react@19.0.0)': + dependencies: + '@babel/runtime': 7.26.10 + '@radix-ui/react-use-layout-effect': 1.0.0(react@19.0.0) + react: 19.0.0 + + '@radix-ui/react-id@1.0.1(@types/react@19.0.11)(react@19.0.0)': + dependencies: + '@babel/runtime': 7.26.10 + '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@19.0.11)(react@19.0.0) + react: 19.0.0 + optionalDependencies: + '@types/react': 19.0.11 + '@radix-ui/react-id@1.1.0(@types/react@19.0.11)(react@19.0.0)': dependencies: '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.0.11)(react@19.0.0) @@ -2211,6 +4013,64 @@ snapshots: optionalDependencies: '@types/react': 19.0.11 + '@radix-ui/react-label@2.1.2(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + optionalDependencies: + '@types/react': 19.0.11 + '@types/react-dom': 19.0.4(@types/react@19.0.11) + + '@radix-ui/react-menu@2.1.6(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@radix-ui/primitive': 1.1.1 + '@radix-ui/react-collection': 1.1.2(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.11)(react@19.0.0) + '@radix-ui/react-context': 1.1.1(@types/react@19.0.11)(react@19.0.0) + '@radix-ui/react-direction': 1.1.0(@types/react@19.0.11)(react@19.0.0) + '@radix-ui/react-dismissable-layer': 1.1.5(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-focus-guards': 1.1.1(@types/react@19.0.11)(react@19.0.0) + '@radix-ui/react-focus-scope': 1.1.2(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-id': 1.1.0(@types/react@19.0.11)(react@19.0.0) + '@radix-ui/react-popper': 1.2.2(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-portal': 1.1.4(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-presence': 1.1.2(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-roving-focus': 1.1.2(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-slot': 1.1.2(@types/react@19.0.11)(react@19.0.0) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.11)(react@19.0.0) + aria-hidden: 1.2.4 + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + react-remove-scroll: 2.6.3(@types/react@19.0.11)(react@19.0.0) + optionalDependencies: + '@types/react': 19.0.11 + '@types/react-dom': 19.0.4(@types/react@19.0.11) + + '@radix-ui/react-popover@1.1.6(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@radix-ui/primitive': 1.1.1 + '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.11)(react@19.0.0) + '@radix-ui/react-context': 1.1.1(@types/react@19.0.11)(react@19.0.0) + '@radix-ui/react-dismissable-layer': 1.1.5(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-focus-guards': 1.1.1(@types/react@19.0.11)(react@19.0.0) + '@radix-ui/react-focus-scope': 1.1.2(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-id': 1.1.0(@types/react@19.0.11)(react@19.0.0) + '@radix-ui/react-popper': 1.2.2(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-portal': 1.1.4(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-presence': 1.1.2(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-slot': 1.1.2(@types/react@19.0.11)(react@19.0.0) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.0.11)(react@19.0.0) + aria-hidden: 1.2.4 + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + react-remove-scroll: 2.6.3(@types/react@19.0.11)(react@19.0.0) + optionalDependencies: + '@types/react': 19.0.11 + '@types/react-dom': 19.0.4(@types/react@19.0.11) + '@radix-ui/react-popper@1.2.2(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': dependencies: '@floating-ui/react-dom': 2.1.2(react-dom@19.0.0(react@19.0.0))(react@19.0.0) @@ -2229,6 +4089,16 @@ snapshots: '@types/react': 19.0.11 '@types/react-dom': 19.0.4(@types/react@19.0.11) + '@radix-ui/react-portal@1.0.4(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@babel/runtime': 7.26.10 + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + optionalDependencies: + '@types/react': 19.0.11 + '@types/react-dom': 19.0.4(@types/react@19.0.11) + '@radix-ui/react-portal@1.1.4(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': dependencies: '@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) @@ -2239,6 +4109,25 @@ snapshots: '@types/react': 19.0.11 '@types/react-dom': 19.0.4(@types/react@19.0.11) + '@radix-ui/react-presence@1.0.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@babel/runtime': 7.26.10 + '@radix-ui/react-compose-refs': 1.0.0(react@19.0.0) + '@radix-ui/react-use-layout-effect': 1.0.0(react@19.0.0) + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + + '@radix-ui/react-presence@1.0.1(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@babel/runtime': 7.26.10 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@19.0.11)(react@19.0.0) + '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@19.0.11)(react@19.0.0) + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + optionalDependencies: + '@types/react': 19.0.11 + '@types/react-dom': 19.0.4(@types/react@19.0.11) + '@radix-ui/react-presence@1.1.2(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': dependencies: '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.11)(react@19.0.0) @@ -2249,6 +4138,23 @@ snapshots: '@types/react': 19.0.11 '@types/react-dom': 19.0.4(@types/react@19.0.11) + '@radix-ui/react-primitive@1.0.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@babel/runtime': 7.26.10 + '@radix-ui/react-slot': 1.0.1(react@19.0.0) + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + + '@radix-ui/react-primitive@1.0.3(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@babel/runtime': 7.26.10 + '@radix-ui/react-slot': 1.0.2(@types/react@19.0.11)(react@19.0.0) + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + optionalDependencies: + '@types/react': 19.0.11 + '@types/react-dom': 19.0.4(@types/react@19.0.11) + '@radix-ui/react-primitive@2.0.2(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': dependencies: '@radix-ui/react-slot': 1.1.2(@types/react@19.0.11)(react@19.0.0) @@ -2258,6 +4164,56 @@ snapshots: '@types/react': 19.0.11 '@types/react-dom': 19.0.4(@types/react@19.0.11) + '@radix-ui/react-radio-group@1.2.3(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@radix-ui/primitive': 1.1.1 + '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.11)(react@19.0.0) + '@radix-ui/react-context': 1.1.1(@types/react@19.0.11)(react@19.0.0) + '@radix-ui/react-direction': 1.1.0(@types/react@19.0.11)(react@19.0.0) + '@radix-ui/react-presence': 1.1.2(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-roving-focus': 1.1.2(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.0.11)(react@19.0.0) + '@radix-ui/react-use-previous': 1.1.0(@types/react@19.0.11)(react@19.0.0) + '@radix-ui/react-use-size': 1.1.0(@types/react@19.0.11)(react@19.0.0) + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + optionalDependencies: + '@types/react': 19.0.11 + '@types/react-dom': 19.0.4(@types/react@19.0.11) + + '@radix-ui/react-roving-focus@1.0.2(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@babel/runtime': 7.26.10 + '@radix-ui/primitive': 1.0.0 + '@radix-ui/react-collection': 1.0.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-compose-refs': 1.0.0(react@19.0.0) + '@radix-ui/react-context': 1.0.0(react@19.0.0) + '@radix-ui/react-direction': 1.0.0(react@19.0.0) + '@radix-ui/react-id': 1.0.0(react@19.0.0) + '@radix-ui/react-primitive': 1.0.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-use-callback-ref': 1.0.0(react@19.0.0) + '@radix-ui/react-use-controllable-state': 1.0.0(react@19.0.0) + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + + '@radix-ui/react-roving-focus@1.1.2(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@radix-ui/primitive': 1.1.1 + '@radix-ui/react-collection': 1.1.2(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.11)(react@19.0.0) + '@radix-ui/react-context': 1.1.1(@types/react@19.0.11)(react@19.0.0) + '@radix-ui/react-direction': 1.1.0(@types/react@19.0.11)(react@19.0.0) + '@radix-ui/react-id': 1.1.0(@types/react@19.0.11)(react@19.0.0) + '@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.11)(react@19.0.0) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.0.11)(react@19.0.0) + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + optionalDependencies: + '@types/react': 19.0.11 + '@types/react-dom': 19.0.4(@types/react@19.0.11) + '@radix-ui/react-separator@1.1.2(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': dependencies: '@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) @@ -2267,6 +4223,20 @@ snapshots: '@types/react': 19.0.11 '@types/react-dom': 19.0.4(@types/react@19.0.11) + '@radix-ui/react-slot@1.0.1(react@19.0.0)': + dependencies: + '@babel/runtime': 7.26.10 + '@radix-ui/react-compose-refs': 1.0.0(react@19.0.0) + react: 19.0.0 + + '@radix-ui/react-slot@1.0.2(@types/react@19.0.11)(react@19.0.0)': + dependencies: + '@babel/runtime': 7.26.10 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@19.0.11)(react@19.0.0) + react: 19.0.0 + optionalDependencies: + '@types/react': 19.0.11 + '@radix-ui/react-slot@1.1.2(@types/react@19.0.11)(react@19.0.0)': dependencies: '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.11)(react@19.0.0) @@ -2274,6 +4244,46 @@ snapshots: optionalDependencies: '@types/react': 19.0.11 + '@radix-ui/react-tabs@1.0.2(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@babel/runtime': 7.26.10 + '@radix-ui/primitive': 1.0.0 + '@radix-ui/react-context': 1.0.0(react@19.0.0) + '@radix-ui/react-direction': 1.0.0(react@19.0.0) + '@radix-ui/react-id': 1.0.0(react@19.0.0) + '@radix-ui/react-presence': 1.0.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-primitive': 1.0.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-roving-focus': 1.0.2(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-use-controllable-state': 1.0.0(react@19.0.0) + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + + '@radix-ui/react-toggle-group@1.1.2(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@radix-ui/primitive': 1.1.1 + '@radix-ui/react-context': 1.1.1(@types/react@19.0.11)(react@19.0.0) + '@radix-ui/react-direction': 1.1.0(@types/react@19.0.11)(react@19.0.0) + '@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-roving-focus': 1.1.2(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-toggle': 1.1.2(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.0.11)(react@19.0.0) + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + optionalDependencies: + '@types/react': 19.0.11 + '@types/react-dom': 19.0.4(@types/react@19.0.11) + + '@radix-ui/react-toggle@1.1.2(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@radix-ui/primitive': 1.1.1 + '@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.0.11)(react@19.0.0) + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + optionalDependencies: + '@types/react': 19.0.11 + '@types/react-dom': 19.0.4(@types/react@19.0.11) + '@radix-ui/react-tooltip@1.1.8(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': dependencies: '@radix-ui/primitive': 1.1.1 @@ -2294,12 +4304,38 @@ snapshots: '@types/react': 19.0.11 '@types/react-dom': 19.0.4(@types/react@19.0.11) + '@radix-ui/react-use-callback-ref@1.0.0(react@19.0.0)': + dependencies: + '@babel/runtime': 7.26.10 + react: 19.0.0 + + '@radix-ui/react-use-callback-ref@1.0.1(@types/react@19.0.11)(react@19.0.0)': + dependencies: + '@babel/runtime': 7.26.10 + react: 19.0.0 + optionalDependencies: + '@types/react': 19.0.11 + '@radix-ui/react-use-callback-ref@1.1.0(@types/react@19.0.11)(react@19.0.0)': dependencies: react: 19.0.0 optionalDependencies: '@types/react': 19.0.11 + '@radix-ui/react-use-controllable-state@1.0.0(react@19.0.0)': + dependencies: + '@babel/runtime': 7.26.10 + '@radix-ui/react-use-callback-ref': 1.0.0(react@19.0.0) + react: 19.0.0 + + '@radix-ui/react-use-controllable-state@1.0.1(@types/react@19.0.11)(react@19.0.0)': + dependencies: + '@babel/runtime': 7.26.10 + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@19.0.11)(react@19.0.0) + react: 19.0.0 + optionalDependencies: + '@types/react': 19.0.11 + '@radix-ui/react-use-controllable-state@1.1.0(@types/react@19.0.11)(react@19.0.0)': dependencies: '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.11)(react@19.0.0) @@ -2307,6 +4343,14 @@ snapshots: optionalDependencies: '@types/react': 19.0.11 + '@radix-ui/react-use-escape-keydown@1.0.3(@types/react@19.0.11)(react@19.0.0)': + dependencies: + '@babel/runtime': 7.26.10 + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@19.0.11)(react@19.0.0) + react: 19.0.0 + optionalDependencies: + '@types/react': 19.0.11 + '@radix-ui/react-use-escape-keydown@1.1.0(@types/react@19.0.11)(react@19.0.0)': dependencies: '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.11)(react@19.0.0) @@ -2314,12 +4358,30 @@ snapshots: optionalDependencies: '@types/react': 19.0.11 + '@radix-ui/react-use-layout-effect@1.0.0(react@19.0.0)': + dependencies: + '@babel/runtime': 7.26.10 + react: 19.0.0 + + '@radix-ui/react-use-layout-effect@1.0.1(@types/react@19.0.11)(react@19.0.0)': + dependencies: + '@babel/runtime': 7.26.10 + react: 19.0.0 + optionalDependencies: + '@types/react': 19.0.11 + '@radix-ui/react-use-layout-effect@1.1.0(@types/react@19.0.11)(react@19.0.0)': dependencies: react: 19.0.0 optionalDependencies: '@types/react': 19.0.11 + '@radix-ui/react-use-previous@1.1.0(@types/react@19.0.11)(react@19.0.0)': + dependencies: + react: 19.0.0 + optionalDependencies: + '@types/react': 19.0.11 + '@radix-ui/react-use-rect@1.1.0(@types/react@19.0.11)(react@19.0.0)': dependencies: '@radix-ui/rect': 1.1.0 @@ -2412,13 +4474,13 @@ snapshots: optionalDependencies: typescript: 5.8.2 - '@serwist/vite@9.0.12(typescript@5.8.2)(vite@6.2.2(jiti@2.4.2)(lightningcss@1.29.2))': + '@serwist/vite@9.0.12(typescript@5.8.2)(vite@6.2.2(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.51.0)(tsx@4.19.3))': dependencies: '@serwist/build': 9.0.12(typescript@5.8.2) glob: 10.4.5 kolorist: 1.8.0 serwist: 9.0.12(typescript@5.8.2) - vite: 6.2.2(jiti@2.4.2)(lightningcss@1.29.2) + vite: 6.2.2(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.51.0)(tsx@4.19.3) zod: 3.24.2 optionalDependencies: typescript: 5.8.2 @@ -2483,13 +4545,13 @@ snapshots: '@tailwindcss/oxide-win32-arm64-msvc': 4.0.14 '@tailwindcss/oxide-win32-x64-msvc': 4.0.14 - '@tailwindcss/vite@4.0.14(vite@6.2.2(jiti@2.4.2)(lightningcss@1.29.2))': + '@tailwindcss/vite@4.0.14(vite@6.2.2(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.51.0)(tsx@4.19.3))': dependencies: '@tailwindcss/node': 4.0.14 '@tailwindcss/oxide': 4.0.14 lightningcss: 1.29.2 tailwindcss: 4.0.14 - vite: 6.2.2(jiti@2.4.2)(lightningcss@1.29.2) + vite: 6.2.2(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.51.0)(tsx@4.19.3) '@tanstack/history@1.114.22': {} @@ -2539,8 +4601,51 @@ snapshots: optionalDependencies: csstype: 3.1.3 + '@tanstack/router-generator@1.114.25(@tanstack/react-router@1.114.25(react-dom@19.0.0(react@19.0.0))(react@19.0.0))': + dependencies: + '@tanstack/virtual-file-routes': 1.114.12 + prettier: 3.5.3 + tsx: 4.19.3 + zod: 3.24.2 + optionalDependencies: + '@tanstack/react-router': 1.114.25(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + + '@tanstack/router-plugin@1.114.25(@tanstack/react-router@1.114.25(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(vite@6.2.2(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.51.0)(tsx@4.19.3))': + dependencies: + '@babel/core': 7.26.10 + '@babel/plugin-syntax-jsx': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-syntax-typescript': 7.25.9(@babel/core@7.26.10) + '@babel/template': 7.26.9 + '@babel/traverse': 7.26.10 + '@babel/types': 7.26.10 + '@tanstack/router-core': 1.114.25 + '@tanstack/router-generator': 1.114.25(@tanstack/react-router@1.114.25(react-dom@19.0.0(react@19.0.0))(react@19.0.0)) + '@tanstack/router-utils': 1.114.12 + '@tanstack/virtual-file-routes': 1.114.12 + '@types/babel__core': 7.20.5 + '@types/babel__template': 7.4.4 + '@types/babel__traverse': 7.20.6 + babel-dead-code-elimination: 1.0.9 + chokidar: 3.6.0 + unplugin: 2.2.0 + zod: 3.24.2 + optionalDependencies: + '@tanstack/react-router': 1.114.25(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + vite: 6.2.2(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.51.0)(tsx@4.19.3) + transitivePeerDependencies: + - supports-color + + '@tanstack/router-utils@1.114.12': + dependencies: + '@babel/generator': 7.26.10 + '@babel/parser': 7.26.10 + ansis: 3.17.0 + diff: 7.0.0 + '@tanstack/store@0.7.0': {} + '@tanstack/virtual-file-routes@1.114.12': {} + '@testing-library/dom@10.4.0': dependencies: '@babel/code-frame': 7.26.2 @@ -2585,8 +4690,26 @@ snapshots: dependencies: '@babel/types': 7.26.10 + '@types/d3-scale-chromatic@3.1.0': {} + + '@types/d3-scale@4.0.9': + dependencies: + '@types/d3-time': 3.0.4 + + '@types/d3-time@3.0.4': {} + + '@types/debug@4.1.12': + dependencies: + '@types/ms': 2.1.0 + '@types/estree@1.0.6': {} + '@types/mdast@3.0.15': + dependencies: + '@types/unist': 2.0.11 + + '@types/ms@2.1.0': {} + '@types/react-dom@19.0.4(@types/react@19.0.11)': dependencies: '@types/react': 19.0.11 @@ -2597,14 +4720,16 @@ snapshots: '@types/trusted-types@2.0.7': {} - '@vitejs/plugin-react@4.3.4(vite@6.2.2(jiti@2.4.2)(lightningcss@1.29.2))': + '@types/unist@2.0.11': {} + + '@vitejs/plugin-react@4.3.4(vite@6.2.2(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.51.0)(tsx@4.19.3))': dependencies: '@babel/core': 7.26.10 '@babel/plugin-transform-react-jsx-self': 7.25.9(@babel/core@7.26.10) '@babel/plugin-transform-react-jsx-source': 7.25.9(@babel/core@7.26.10) '@types/babel__core': 7.20.5 react-refresh: 0.14.2 - vite: 6.2.2(jiti@2.4.2)(lightningcss@1.29.2) + vite: 6.2.2(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.51.0)(tsx@4.19.3) transitivePeerDependencies: - supports-color @@ -2615,13 +4740,13 @@ snapshots: chai: 5.2.0 tinyrainbow: 2.0.0 - '@vitest/mocker@3.0.9(vite@6.2.2(jiti@2.4.2)(lightningcss@1.29.2))': + '@vitest/mocker@3.0.9(vite@6.2.2(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.51.0)(tsx@4.19.3))': dependencies: '@vitest/spy': 3.0.9 estree-walker: 3.0.3 magic-string: 0.30.17 optionalDependencies: - vite: 6.2.2(jiti@2.4.2)(lightningcss@1.29.2) + vite: 6.2.2(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.51.0)(tsx@4.19.3) '@vitest/pretty-format@3.0.9': dependencies: @@ -2648,6 +4773,26 @@ snapshots: loupe: 3.1.3 tinyrainbow: 2.0.0 + abstract-leveldown@6.2.3: + dependencies: + buffer: 5.7.1 + immediate: 3.3.0 + level-concat-iterator: 2.0.1 + level-supports: 1.0.1 + xtend: 4.0.2 + optional: true + + abstract-leveldown@6.3.0: + dependencies: + buffer: 5.7.1 + immediate: 3.3.0 + level-concat-iterator: 2.0.1 + level-supports: 1.0.1 + xtend: 4.0.2 + optional: true + + acorn@8.14.1: {} + agent-base@7.1.3: {} ansi-regex@5.0.1: {} @@ -2662,6 +4807,13 @@ snapshots: ansi-styles@6.2.1: {} + ansis@3.17.0: {} + + anymatch@3.1.3: + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + aria-hidden@1.2.4: dependencies: tslib: 2.8.1 @@ -2672,14 +4824,37 @@ snapshots: assertion-error@2.0.1: {} + async-limiter@1.0.1: + optional: true + asynckit@0.4.0: {} + babel-dead-code-elimination@1.0.9: + dependencies: + '@babel/core': 7.26.10 + '@babel/parser': 7.26.10 + '@babel/traverse': 7.26.10 + '@babel/types': 7.26.10 + transitivePeerDependencies: + - supports-color + balanced-match@1.0.2: {} + base64-js@1.5.1: + optional: true + + binary-extensions@2.3.0: {} + brace-expansion@2.0.1: dependencies: balanced-match: 1.0.2 + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + browser-fs-access@0.29.1: {} + browserslist@4.24.4: dependencies: caniuse-lite: 1.0.30001706 @@ -2687,6 +4862,12 @@ snapshots: node-releases: 2.0.19 update-browserslist-db: 1.1.3(browserslist@4.24.4) + buffer@5.7.1: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + optional: true + cac@6.7.14: {} call-bind-apply-helpers@1.0.2: @@ -2696,6 +4877,8 @@ snapshots: caniuse-lite@1.0.30001706: {} + canvas-roundrect-polyfill@0.0.1: {} + chai@5.2.0: dependencies: assertion-error: 2.0.1 @@ -2709,14 +4892,40 @@ snapshots: ansi-styles: 4.3.0 supports-color: 7.2.0 + character-entities@2.0.2: {} + check-error@2.1.1: {} + chokidar@3.6.0: + dependencies: + anymatch: 3.1.3 + braces: 3.0.3 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.3 + class-variance-authority@0.7.1: dependencies: clsx: 2.1.1 + clsx@1.1.1: {} + clsx@2.1.1: {} + cmdk@1.0.0(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0): + dependencies: + '@radix-ui/react-dialog': 1.0.5(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + transitivePeerDependencies: + - '@types/react' + - '@types/react-dom' + color-convert@2.0.1: dependencies: color-name: 1.1.4 @@ -2727,10 +4936,24 @@ snapshots: dependencies: delayed-stream: 1.0.0 + commander@7.2.0: {} + + commander@8.3.0: {} + common-tags@1.8.2: {} convert-source-map@2.0.0: {} + cose-base@1.0.3: + dependencies: + layout-base: 1.0.2 + + crc-32@0.3.0: {} + + cross-env@7.0.3: + dependencies: + cross-spawn: 7.0.6 + cross-spawn@7.0.6: dependencies: path-key: 3.1.1 @@ -2744,19 +4967,214 @@ snapshots: csstype@3.1.3: {} + cytoscape-cose-bilkent@4.1.0(cytoscape@3.31.1): + dependencies: + cose-base: 1.0.3 + cytoscape: 3.31.1 + + cytoscape@3.31.1: {} + + d3-array@2.12.1: + dependencies: + internmap: 1.0.1 + + d3-array@3.2.4: + dependencies: + internmap: 2.0.3 + + d3-axis@3.0.0: {} + + d3-brush@3.0.0: + dependencies: + d3-dispatch: 3.0.1 + d3-drag: 3.0.0 + d3-interpolate: 3.0.1 + d3-selection: 3.0.0 + d3-transition: 3.0.1(d3-selection@3.0.0) + + d3-chord@3.0.1: + dependencies: + d3-path: 3.1.0 + + d3-color@3.1.0: {} + + d3-contour@4.0.2: + dependencies: + d3-array: 3.2.4 + + d3-delaunay@6.0.4: + dependencies: + delaunator: 5.0.1 + + d3-dispatch@3.0.1: {} + + d3-drag@3.0.0: + dependencies: + d3-dispatch: 3.0.1 + d3-selection: 3.0.0 + + d3-dsv@3.0.1: + dependencies: + commander: 7.2.0 + iconv-lite: 0.6.3 + rw: 1.3.3 + + d3-ease@3.0.1: {} + + d3-fetch@3.0.1: + dependencies: + d3-dsv: 3.0.1 + + d3-force@3.0.0: + dependencies: + d3-dispatch: 3.0.1 + d3-quadtree: 3.0.1 + d3-timer: 3.0.1 + + d3-format@3.1.0: {} + + d3-geo@3.1.1: + dependencies: + d3-array: 3.2.4 + + d3-hierarchy@3.1.2: {} + + d3-interpolate@3.0.1: + dependencies: + d3-color: 3.1.0 + + d3-path@1.0.9: {} + + d3-path@3.1.0: {} + + d3-polygon@3.0.1: {} + + d3-quadtree@3.0.1: {} + + d3-random@3.0.1: {} + + d3-sankey@0.12.3: + dependencies: + d3-array: 2.12.1 + d3-shape: 1.3.7 + + d3-scale-chromatic@3.1.0: + dependencies: + d3-color: 3.1.0 + d3-interpolate: 3.0.1 + + d3-scale@4.0.2: + dependencies: + d3-array: 3.2.4 + d3-format: 3.1.0 + d3-interpolate: 3.0.1 + d3-time: 3.1.0 + d3-time-format: 4.1.0 + + d3-selection@3.0.0: {} + + d3-shape@1.3.7: + dependencies: + d3-path: 1.0.9 + + d3-shape@3.2.0: + dependencies: + d3-path: 3.1.0 + + d3-time-format@4.1.0: + dependencies: + d3-time: 3.1.0 + + d3-time@3.1.0: + dependencies: + d3-array: 3.2.4 + + d3-timer@3.0.1: {} + + d3-transition@3.0.1(d3-selection@3.0.0): + dependencies: + d3-color: 3.1.0 + d3-dispatch: 3.0.1 + d3-ease: 3.0.1 + d3-interpolate: 3.0.1 + d3-selection: 3.0.0 + d3-timer: 3.0.1 + + d3-zoom@3.0.0: + dependencies: + d3-dispatch: 3.0.1 + d3-drag: 3.0.0 + d3-interpolate: 3.0.1 + d3-selection: 3.0.0 + d3-transition: 3.0.1(d3-selection@3.0.0) + + d3@7.9.0: + dependencies: + d3-array: 3.2.4 + d3-axis: 3.0.0 + d3-brush: 3.0.0 + d3-chord: 3.0.1 + d3-color: 3.1.0 + d3-contour: 4.0.2 + d3-delaunay: 6.0.4 + d3-dispatch: 3.0.1 + d3-drag: 3.0.0 + d3-dsv: 3.0.1 + d3-ease: 3.0.1 + d3-fetch: 3.0.1 + d3-force: 3.0.0 + d3-format: 3.1.0 + d3-geo: 3.1.1 + d3-hierarchy: 3.1.2 + d3-interpolate: 3.0.1 + d3-path: 3.1.0 + d3-polygon: 3.0.1 + d3-quadtree: 3.0.1 + d3-random: 3.0.1 + d3-scale: 4.0.2 + d3-scale-chromatic: 3.1.0 + d3-selection: 3.0.0 + d3-shape: 3.2.0 + d3-time: 3.1.0 + d3-time-format: 4.1.0 + d3-timer: 3.0.1 + d3-transition: 3.0.1(d3-selection@3.0.0) + d3-zoom: 3.0.0 + + dagre-d3-es@7.0.10: + dependencies: + d3: 7.9.0 + lodash-es: 4.17.21 + data-urls@5.0.0: dependencies: whatwg-mimetype: 4.0.0 whatwg-url: 14.2.0 + dayjs@1.11.13: {} + debug@4.4.0: dependencies: ms: 2.1.3 decimal.js@10.5.0: {} + decode-named-character-reference@1.1.0: + dependencies: + character-entities: 2.0.2 + deep-eql@5.0.2: {} + deferred-leveldown@5.3.0: + dependencies: + abstract-leveldown: 6.2.3 + inherits: 2.0.4 + optional: true + + delaunator@5.0.1: + dependencies: + robust-predicates: 3.0.2 + delayed-stream@1.0.0: {} dequal@2.0.3: {} @@ -2765,8 +5183,14 @@ snapshots: detect-node-es@1.1.0: {} + diff@5.2.0: {} + + diff@7.0.0: {} + dom-accessibility-api@0.5.16: {} + dompurify@3.1.6: {} + dunder-proto@1.0.1: dependencies: call-bind-apply-helpers: 1.0.2 @@ -2777,10 +5201,20 @@ snapshots: electron-to-chromium@1.5.120: {} + elkjs@0.9.3: {} + emoji-regex@8.0.0: {} emoji-regex@9.2.2: {} + encoding-down@6.3.0: + dependencies: + abstract-leveldown: 6.3.0 + inherits: 2.0.4 + level-codec: 9.0.2 + level-errors: 2.0.1 + optional: true + enhanced-resolve@5.18.1: dependencies: graceful-fs: 4.2.11 @@ -2788,6 +5222,11 @@ snapshots: entities@4.5.0: {} + errno@0.1.8: + dependencies: + prr: 1.0.1 + optional: true + es-define-property@1.0.1: {} es-errors@1.3.0: {} @@ -2805,6 +5244,8 @@ snapshots: has-tostringtag: 1.0.2 hasown: 2.0.2 + es6-promise-pool@2.5.0: {} + esbuild@0.25.1: optionalDependencies: '@esbuild/aix-ppc64': 0.25.1 @@ -2841,6 +5282,10 @@ snapshots: expect-type@1.2.0: {} + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + foreground-child@3.3.1: dependencies: cross-spawn: 7.0.6 @@ -2853,11 +5298,15 @@ snapshots: es-set-tostringtag: 2.1.0 mime-types: 2.1.35 + fractional-indexing@3.2.0: {} + fsevents@2.3.3: optional: true function-bind@1.1.2: {} + fuzzy@0.1.3: {} + gensync@1.0.0-beta.2: {} get-intrinsic@1.3.0: @@ -2880,6 +5329,14 @@ snapshots: dunder-proto: 1.0.1 es-object-atoms: 1.1.1 + get-tsconfig@4.10.0: + dependencies: + resolve-pkg-maps: 1.0.0 + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + glob@10.4.5: dependencies: foreground-child: 3.3.1 @@ -2891,6 +5348,8 @@ snapshots: globals@11.12.0: {} + glur@1.1.2: {} + goober@2.1.16(csstype@3.1.3): dependencies: csstype: 3.1.3 @@ -2899,6 +5358,8 @@ snapshots: graceful-fs@4.2.11: {} + hachure-fill@0.5.2: {} + has-flag@4.0.0: {} has-symbols@1.1.0: {} @@ -2935,12 +5396,44 @@ snapshots: idb@8.0.2: {} + ieee754@1.2.1: + optional: true + + image-blob-reduce@3.0.1: + dependencies: + pica: 7.1.1 + + immediate@3.3.0: + optional: true + + immutable@4.3.7: {} + + inherits@2.0.4: {} + + internmap@1.0.1: {} + + internmap@2.0.3: {} + + is-binary-path@2.1.0: + dependencies: + binary-extensions: 2.3.0 + + is-extglob@2.1.1: {} + is-fullwidth-code-point@3.0.0: {} + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-number@7.0.0: {} + is-potential-custom-element-name@1.0.1: {} isexe@2.0.0: {} + isomorphic.js@0.2.5: {} + jackspeak@3.4.3: dependencies: '@isaacs/cliui': 8.0.2 @@ -2949,6 +5442,16 @@ snapshots: jiti@2.4.2: {} + jotai-scope@0.7.2(jotai@2.11.0(@types/react@19.0.11)(react@19.0.0))(react@19.0.0): + dependencies: + jotai: 2.11.0(@types/react@19.0.11)(react@19.0.0) + react: 19.0.0 + + jotai@2.11.0(@types/react@19.0.11)(react@19.0.0): + optionalDependencies: + '@types/react': 19.0.11 + react: 19.0.0 + js-tokens@4.0.0: {} jsdom@26.0.0: @@ -2983,8 +5486,86 @@ snapshots: json5@2.2.3: {} + katex@0.16.21: + dependencies: + commander: 8.3.0 + + khroma@2.1.0: {} + + kleur@4.1.5: {} + kolorist@1.8.0: {} + layout-base@1.0.2: {} + + level-codec@9.0.2: + dependencies: + buffer: 5.7.1 + optional: true + + level-concat-iterator@2.0.1: + optional: true + + level-errors@2.0.1: + dependencies: + errno: 0.1.8 + optional: true + + level-iterator-stream@4.0.2: + dependencies: + inherits: 2.0.4 + readable-stream: 3.6.2 + xtend: 4.0.2 + optional: true + + level-js@5.0.2: + dependencies: + abstract-leveldown: 6.2.3 + buffer: 5.7.1 + inherits: 2.0.4 + ltgt: 2.2.1 + optional: true + + level-packager@5.1.1: + dependencies: + encoding-down: 6.3.0 + levelup: 4.4.0 + optional: true + + level-supports@1.0.1: + dependencies: + xtend: 4.0.2 + optional: true + + level@6.0.1: + dependencies: + level-js: 5.0.2 + level-packager: 5.1.1 + leveldown: 5.6.0 + optional: true + + leveldown@5.6.0: + dependencies: + abstract-leveldown: 6.2.3 + napi-macros: 2.0.0 + node-gyp-build: 4.1.1 + optional: true + + levelup@4.4.0: + dependencies: + deferred-leveldown: 5.3.0 + level-errors: 2.0.1 + level-iterator-stream: 4.0.2 + level-supports: 1.0.1 + xtend: 4.0.2 + optional: true + + lexical@0.28.0: {} + + lib0@0.2.99: + dependencies: + isomorphic.js: 0.2.5 + lightningcss-darwin-arm64@1.29.2: optional: true @@ -3030,8 +5611,18 @@ snapshots: lightningcss-win32-arm64-msvc: 1.29.2 lightningcss-win32-x64-msvc: 1.29.2 + lodash-es@4.17.21: {} + + lodash.debounce@4.0.8: {} + lodash.sortby@4.7.0: {} + lodash.throttle@4.1.1: {} + + loose-envify@1.4.0: + dependencies: + js-tokens: 4.0.0 + loupe@3.1.3: {} lru-cache@10.4.3: {} @@ -3040,6 +5631,9 @@ snapshots: dependencies: yallist: 3.1.1 + ltgt@2.2.1: + optional: true + lucide-react@0.483.0(react@19.0.0): dependencies: react: 19.0.0 @@ -3052,6 +5646,185 @@ snapshots: math-intrinsics@1.1.0: {} + mdast-util-from-markdown@1.3.1: + dependencies: + '@types/mdast': 3.0.15 + '@types/unist': 2.0.11 + decode-named-character-reference: 1.1.0 + mdast-util-to-string: 3.2.0 + micromark: 3.2.0 + micromark-util-decode-numeric-character-reference: 1.1.0 + micromark-util-decode-string: 1.1.0 + micromark-util-normalize-identifier: 1.1.0 + micromark-util-symbol: 1.1.0 + micromark-util-types: 1.1.0 + unist-util-stringify-position: 3.0.3 + uvu: 0.5.6 + transitivePeerDependencies: + - supports-color + + mdast-util-to-string@3.2.0: + dependencies: + '@types/mdast': 3.0.15 + + mermaid@10.9.3: + dependencies: + '@braintree/sanitize-url': 6.0.2 + '@types/d3-scale': 4.0.9 + '@types/d3-scale-chromatic': 3.1.0 + cytoscape: 3.31.1 + cytoscape-cose-bilkent: 4.1.0(cytoscape@3.31.1) + d3: 7.9.0 + d3-sankey: 0.12.3 + dagre-d3-es: 7.0.10 + dayjs: 1.11.13 + dompurify: 3.1.6 + elkjs: 0.9.3 + katex: 0.16.21 + khroma: 2.1.0 + lodash-es: 4.17.21 + mdast-util-from-markdown: 1.3.1 + non-layered-tidy-tree-layout: 2.0.2 + stylis: 4.3.6 + ts-dedent: 2.2.0 + uuid: 9.0.1 + web-worker: 1.5.0 + transitivePeerDependencies: + - supports-color + + micromark-core-commonmark@1.1.0: + dependencies: + decode-named-character-reference: 1.1.0 + micromark-factory-destination: 1.1.0 + micromark-factory-label: 1.1.0 + micromark-factory-space: 1.1.0 + micromark-factory-title: 1.1.0 + micromark-factory-whitespace: 1.1.0 + micromark-util-character: 1.2.0 + micromark-util-chunked: 1.1.0 + micromark-util-classify-character: 1.1.0 + micromark-util-html-tag-name: 1.2.0 + micromark-util-normalize-identifier: 1.1.0 + micromark-util-resolve-all: 1.1.0 + micromark-util-subtokenize: 1.1.0 + micromark-util-symbol: 1.1.0 + micromark-util-types: 1.1.0 + uvu: 0.5.6 + + micromark-factory-destination@1.1.0: + dependencies: + micromark-util-character: 1.2.0 + micromark-util-symbol: 1.1.0 + micromark-util-types: 1.1.0 + + micromark-factory-label@1.1.0: + dependencies: + micromark-util-character: 1.2.0 + micromark-util-symbol: 1.1.0 + micromark-util-types: 1.1.0 + uvu: 0.5.6 + + micromark-factory-space@1.1.0: + dependencies: + micromark-util-character: 1.2.0 + micromark-util-types: 1.1.0 + + micromark-factory-title@1.1.0: + dependencies: + micromark-factory-space: 1.1.0 + micromark-util-character: 1.2.0 + micromark-util-symbol: 1.1.0 + micromark-util-types: 1.1.0 + + micromark-factory-whitespace@1.1.0: + dependencies: + micromark-factory-space: 1.1.0 + micromark-util-character: 1.2.0 + micromark-util-symbol: 1.1.0 + micromark-util-types: 1.1.0 + + micromark-util-character@1.2.0: + dependencies: + micromark-util-symbol: 1.1.0 + micromark-util-types: 1.1.0 + + micromark-util-chunked@1.1.0: + dependencies: + micromark-util-symbol: 1.1.0 + + micromark-util-classify-character@1.1.0: + dependencies: + micromark-util-character: 1.2.0 + micromark-util-symbol: 1.1.0 + micromark-util-types: 1.1.0 + + micromark-util-combine-extensions@1.1.0: + dependencies: + micromark-util-chunked: 1.1.0 + micromark-util-types: 1.1.0 + + micromark-util-decode-numeric-character-reference@1.1.0: + dependencies: + micromark-util-symbol: 1.1.0 + + micromark-util-decode-string@1.1.0: + dependencies: + decode-named-character-reference: 1.1.0 + micromark-util-character: 1.2.0 + micromark-util-decode-numeric-character-reference: 1.1.0 + micromark-util-symbol: 1.1.0 + + micromark-util-encode@1.1.0: {} + + micromark-util-html-tag-name@1.2.0: {} + + micromark-util-normalize-identifier@1.1.0: + dependencies: + micromark-util-symbol: 1.1.0 + + micromark-util-resolve-all@1.1.0: + dependencies: + micromark-util-types: 1.1.0 + + micromark-util-sanitize-uri@1.2.0: + dependencies: + micromark-util-character: 1.2.0 + micromark-util-encode: 1.1.0 + micromark-util-symbol: 1.1.0 + + micromark-util-subtokenize@1.1.0: + dependencies: + micromark-util-chunked: 1.1.0 + micromark-util-symbol: 1.1.0 + micromark-util-types: 1.1.0 + uvu: 0.5.6 + + micromark-util-symbol@1.1.0: {} + + micromark-util-types@1.1.0: {} + + micromark@3.2.0: + dependencies: + '@types/debug': 4.1.12 + debug: 4.4.0 + decode-named-character-reference: 1.1.0 + micromark-core-commonmark: 1.1.0 + micromark-factory-space: 1.1.0 + micromark-util-character: 1.2.0 + micromark-util-chunked: 1.1.0 + micromark-util-combine-extensions: 1.1.0 + micromark-util-decode-numeric-character-reference: 1.1.0 + micromark-util-encode: 1.1.0 + micromark-util-normalize-identifier: 1.1.0 + micromark-util-resolve-all: 1.1.0 + micromark-util-sanitize-uri: 1.2.0 + micromark-util-subtokenize: 1.1.0 + micromark-util-symbol: 1.1.0 + micromark-util-types: 1.1.0 + uvu: 0.5.6 + transitivePeerDependencies: + - supports-color + mime-db@1.52.0: {} mime-types@2.1.35: @@ -3064,20 +5837,49 @@ snapshots: minipass@7.1.2: {} + mri@1.2.0: {} + ms@2.1.3: {} + multimath@2.0.0: + dependencies: + glur: 1.1.2 + object-assign: 4.1.1 + nanoid@3.3.11: {} + nanoid@3.3.3: {} + + nanoid@4.0.2: {} + + napi-macros@2.0.0: + optional: true + + node-gyp-build@4.1.1: + optional: true + node-releases@2.0.19: {} + non-layered-tidy-tree-layout@2.0.2: {} + + normalize-path@3.0.0: {} + nwsapi@2.2.19: {} + object-assign@4.1.1: {} + + open-color@1.9.1: {} + package-json-from-dist@1.0.1: {} + pako@2.0.3: {} + parse5@7.2.1: dependencies: entities: 4.5.0 + path-data-parser@0.1.0: {} + path-key@3.1.1: {} path-scurry@1.11.1: @@ -3089,14 +5891,48 @@ snapshots: pathval@2.0.0: {} + perfect-freehand@1.2.0: {} + + pica@7.1.1: + dependencies: + glur: 1.1.2 + inherits: 2.0.4 + multimath: 2.0.0 + object-assign: 4.1.1 + webworkify: 1.5.0 + picocolors@1.1.1: {} + picomatch@2.3.1: {} + + png-chunk-text@1.0.0: {} + + png-chunks-encode@1.0.0: + dependencies: + crc-32: 0.3.0 + sliced: 1.0.1 + + png-chunks-extract@1.0.0: + dependencies: + crc-32: 0.3.0 + + points-on-curve@0.2.0: {} + + points-on-curve@1.0.1: {} + + points-on-path@0.2.1: + dependencies: + path-data-parser: 0.1.0 + points-on-curve: 0.2.0 + postcss@8.5.3: dependencies: nanoid: 3.3.11 picocolors: 1.1.1 source-map-js: 1.2.1 + prettier@3.5.3: {} + pretty-bytes@6.1.1: {} pretty-format@27.5.1: @@ -3105,13 +5941,33 @@ snapshots: ansi-styles: 5.2.0 react-is: 17.0.2 + prismjs@1.30.0: {} + + prop-types@15.8.1: + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + react-is: 16.13.1 + + prr@1.0.1: + optional: true + punycode@2.3.1: {} + pwacompat@2.0.17: {} + react-dom@19.0.0(react@19.0.0): dependencies: react: 19.0.0 scheduler: 0.25.0 + react-error-boundary@3.1.4(react@19.0.0): + dependencies: + '@babel/runtime': 7.26.10 + react: 19.0.0 + + react-is@16.13.1: {} + react-is@17.0.2: {} react-refresh@0.14.2: {} @@ -3124,6 +5980,17 @@ snapshots: optionalDependencies: '@types/react': 19.0.11 + react-remove-scroll@2.5.5(@types/react@19.0.11)(react@19.0.0): + dependencies: + react: 19.0.0 + react-remove-scroll-bar: 2.3.8(@types/react@19.0.11)(react@19.0.0) + react-style-singleton: 2.2.3(@types/react@19.0.11)(react@19.0.0) + tslib: 2.8.1 + use-callback-ref: 1.3.3(@types/react@19.0.11)(react@19.0.0) + use-sidecar: 1.1.3(@types/react@19.0.11)(react@19.0.0) + optionalDependencies: + '@types/react': 19.0.11 + react-remove-scroll@2.6.3(@types/react@19.0.11)(react@19.0.0): dependencies: react: 19.0.0 @@ -3145,8 +6012,23 @@ snapshots: react@19.0.0: {} + readable-stream@3.6.2: + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + optional: true + + readdirp@3.6.0: + dependencies: + picomatch: 2.3.1 + regenerator-runtime@0.14.1: {} + resolve-pkg-maps@1.0.0: {} + + robust-predicates@3.0.2: {} + rollup@4.36.0: dependencies: '@types/estree': 1.0.6 @@ -3172,10 +6054,32 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.36.0 fsevents: 2.3.3 + roughjs@4.6.4: + dependencies: + hachure-fill: 0.5.2 + path-data-parser: 0.1.0 + points-on-curve: 0.2.0 + points-on-path: 0.2.1 + rrweb-cssom@0.8.0: {} + rw@1.3.3: {} + + sade@1.8.1: + dependencies: + mri: 1.2.0 + + safe-buffer@5.2.1: + optional: true + safer-buffer@2.1.2: {} + sass@1.51.0: + dependencies: + chokidar: 3.6.0 + immutable: 4.3.7 + source-map-js: 1.2.1 + saxes@6.0.0: dependencies: xmlchars: 2.2.0 @@ -3206,6 +6110,8 @@ snapshots: signal-exit@4.1.0: {} + sliced@1.0.1: {} + solid-js@1.9.5: dependencies: csstype: 3.1.3 @@ -3234,6 +6140,11 @@ snapshots: emoji-regex: 9.2.2 strip-ansi: 7.1.0 + string_decoder@1.3.0: + dependencies: + safe-buffer: 5.2.1 + optional: true + strip-ansi@6.0.1: dependencies: ansi-regex: 5.0.1 @@ -3242,6 +6153,8 @@ snapshots: dependencies: ansi-regex: 6.1.0 + stylis@4.3.6: {} + supports-color@7.2.0: dependencies: has-flag: 4.0.0 @@ -3274,6 +6187,10 @@ snapshots: dependencies: tldts-core: 6.1.84 + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + tough-cookie@5.1.2: dependencies: tldts: 6.1.84 @@ -3286,12 +6203,38 @@ snapshots: dependencies: punycode: 2.3.1 + ts-dedent@2.2.0: {} + tslib@2.8.1: {} + tsx@4.19.3: + dependencies: + esbuild: 0.25.1 + get-tsconfig: 4.10.0 + optionalDependencies: + fsevents: 2.3.3 + + tunnel-rat@0.1.2(@types/react@19.0.11)(react@19.0.0): + dependencies: + zustand: 4.5.6(@types/react@19.0.11)(react@19.0.0) + transitivePeerDependencies: + - '@types/react' + - immer + - react + tw-animate-css@1.2.4: {} typescript@5.8.2: {} + unist-util-stringify-position@3.0.3: + dependencies: + '@types/unist': 2.0.11 + + unplugin@2.2.0: + dependencies: + acorn: 8.14.1 + webpack-virtual-modules: 0.6.2 + update-browserslist-db@1.1.3(browserslist@4.24.4): dependencies: browserslist: 4.24.4 @@ -3317,13 +6260,25 @@ snapshots: dependencies: react: 19.0.0 - vite-node@3.0.9(jiti@2.4.2)(lightningcss@1.29.2): + util-deprecate@1.0.2: + optional: true + + uuid@9.0.1: {} + + uvu@0.5.6: + dependencies: + dequal: 2.0.3 + diff: 5.2.0 + kleur: 4.1.5 + sade: 1.8.1 + + vite-node@3.0.9(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.51.0)(tsx@4.19.3): dependencies: cac: 6.7.14 debug: 4.4.0 es-module-lexer: 1.6.0 pathe: 2.0.3 - vite: 6.2.2(jiti@2.4.2)(lightningcss@1.29.2) + vite: 6.2.2(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.51.0)(tsx@4.19.3) transitivePeerDependencies: - '@types/node' - jiti @@ -3338,7 +6293,7 @@ snapshots: - tsx - yaml - vite@6.2.2(jiti@2.4.2)(lightningcss@1.29.2): + vite@6.2.2(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.51.0)(tsx@4.19.3): dependencies: esbuild: 0.25.1 postcss: 8.5.3 @@ -3347,11 +6302,13 @@ snapshots: fsevents: 2.3.3 jiti: 2.4.2 lightningcss: 1.29.2 + sass: 1.51.0 + tsx: 4.19.3 - vitest@3.0.9(jiti@2.4.2)(jsdom@26.0.0)(lightningcss@1.29.2): + vitest@3.0.9(@types/debug@4.1.12)(jiti@2.4.2)(jsdom@26.0.0)(lightningcss@1.29.2)(sass@1.51.0)(tsx@4.19.3): dependencies: '@vitest/expect': 3.0.9 - '@vitest/mocker': 3.0.9(vite@6.2.2(jiti@2.4.2)(lightningcss@1.29.2)) + '@vitest/mocker': 3.0.9(vite@6.2.2(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.51.0)(tsx@4.19.3)) '@vitest/pretty-format': 3.0.9 '@vitest/runner': 3.0.9 '@vitest/snapshot': 3.0.9 @@ -3367,10 +6324,11 @@ snapshots: tinyexec: 0.3.2 tinypool: 1.0.2 tinyrainbow: 2.0.0 - vite: 6.2.2(jiti@2.4.2)(lightningcss@1.29.2) - vite-node: 3.0.9(jiti@2.4.2)(lightningcss@1.29.2) + vite: 6.2.2(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.51.0)(tsx@4.19.3) + vite-node: 3.0.9(jiti@2.4.2)(lightningcss@1.29.2)(sass@1.51.0)(tsx@4.19.3) why-is-node-running: 2.3.0 optionalDependencies: + '@types/debug': 4.1.12 jsdom: 26.0.0 transitivePeerDependencies: - jiti @@ -3392,10 +6350,16 @@ snapshots: web-vitals@4.2.4: {} + web-worker@1.5.0: {} + webidl-conversions@4.0.2: {} webidl-conversions@7.0.0: {} + webpack-virtual-modules@0.6.2: {} + + webworkify@1.5.0: {} + whatwg-encoding@3.1.1: dependencies: iconv-lite: 0.6.3 @@ -3434,12 +6398,67 @@ snapshots: string-width: 5.1.2 strip-ansi: 7.1.0 + ws@6.2.3: + dependencies: + async-limiter: 1.0.1 + optional: true + ws@8.18.1: {} xml-name-validator@5.0.0: {} xmlchars@2.2.0: {} + xtend@4.0.2: + optional: true + + y-excalidraw@2.0.12(@excalidraw/excalidraw@0.18.0(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(yjs@13.6.24): + dependencies: + '@excalidraw/excalidraw': 0.18.0(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + fractional-indexing: 3.2.0 + yjs: 13.6.24 + + y-indexeddb@9.0.12(yjs@13.6.24): + dependencies: + lib0: 0.2.99 + yjs: 13.6.24 + + y-leveldb@0.1.2(yjs@13.6.24): + dependencies: + level: 6.0.1 + lib0: 0.2.99 + yjs: 13.6.24 + optional: true + + y-protocols@1.0.6(yjs@13.6.24): + dependencies: + lib0: 0.2.99 + yjs: 13.6.24 + + y-websocket@2.1.0(yjs@13.6.24): + dependencies: + lib0: 0.2.99 + lodash.debounce: 4.0.8 + y-protocols: 1.0.6(yjs@13.6.24) + yjs: 13.6.24 + optionalDependencies: + ws: 6.2.3 + y-leveldb: 0.1.2(yjs@13.6.24) + transitivePeerDependencies: + - bufferutil + - utf-8-validate + yallist@3.1.1: {} + yjs@13.6.24: + dependencies: + lib0: 0.2.99 + zod@3.24.2: {} + + zustand@4.5.6(@types/react@19.0.11)(react@19.0.0): + dependencies: + use-sync-external-store: 1.4.0(react@19.0.0) + optionalDependencies: + '@types/react': 19.0.11 + react: 19.0.0 diff --git a/src/components/app_sidebar.tsx b/src/components/app_sidebar.tsx new file mode 100644 index 0000000..bdb84a7 --- /dev/null +++ b/src/components/app_sidebar.tsx @@ -0,0 +1,253 @@ +import Icon from "@mdi/react"; +import { BookPlus, Calendar, ChevronsUpDown, Home, Inbox, ListTodo, LogOut, Monitor, Moon, Palette, PanelLeft, Plus, Search, Sun, Waypoints } from "lucide-react"; +import { Button } from "~/components/ui/button"; +import { Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupContent, SidebarGroupLabel, SidebarMenu, SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem, useSidebar } from "~/components/ui/sidebar"; +import { type ColorName, colors } from "~/lib/color"; +import { type IconName, icons } from "~/lib/icon"; +import { NewNoteDialog } from "~/components/note/new_note_dialog"; +import { NewCollectionDialog } from "~/components/collection/new_collection_dialog"; +import { cn } from "~/lib/utils"; +import { useCollectionNotesMetadata, useCollections } from "~/hooks/use-metadata"; +import { useIsMobile } from "~/hooks/use-mobile"; +import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger } from "~/components/ui/dropdown-menu"; +import { UserAvatar } from "~/components/user/user_avatar"; +import { Link } from "@tanstack/react-router"; +import { useTheme } from "~/hooks/use-theme"; +import { ToggleGroup, ToggleGroupItem } from "./ui/toggle-group"; + +export function AppSidebar() { + const collections = useCollections(); + + return ( + + + + + + + + + + + + + } /> + } /> + + } /> + } /> + } /> + + + + Collections + + + {collections.map((collection) => ())} + + + + + + + + + + + + ); +} + +type NavButtonProps = { + href: string; + label: string; + icon?: React.ReactNode; + children?: React.ReactNode; +} + +function NavButton(props: NavButtonProps) { + const { setOpenMobile } = useSidebar(); + + return ( + + + setOpenMobile(false)}> + {props.icon} + {props.label} + + + {props.children} + + ); +} + +function HeaderButtons(props: { children: React.ReactNode }) { + return ( +
+ {props.children} +
+ ); +} + +type CollectionButtonProps = { + id: string; + name: string; + icon: IconName | ""; + color: ColorName | ""; +} + +function CollectionButton(props: CollectionButtonProps) { + const icon = props.icon && icons[props.icon]; + const color = props.color ? colors[props.color] : colors.white; + + return ( + + + +
+ {icon && } +
+ + {props.name} + + +
+
+ ); +} + +function SidebarToggle() { + const isMobile = useIsMobile(); + const { toggleSidebar } = useSidebar() + + if (isMobile) { + return null; + } + + return ( + + + + + + ); +} + +function InboxButton() { + const notes = useCollectionNotesMetadata(""); + const count = notes.length; + + return ( + }> + {count} + + ); +} + +function NavUser() { + const isMobile = useIsMobile(); + const [theme, setTheme] = useTheme(); + // const { data: session } = useSession(); + // const user = session?.user; + const user = { + name: "Kalle Struik", + email: "kalle@kallestruik.nl" + }; + + const email = user?.email ?? undefined; + const name = user?.name ?? undefined + + function handleSignOut() { + // // End the session + // signOut(); + // // Clear all locally stored data + // localStorage.clear(); + // indexedDB.databases().then((dbs) => { + // dbs.forEach((db) => { + // db.name && indexedDB.deleteDatabase(db.name); + // }); + // }); + } + + return ( + + + + + + +
+ {name} + {email} +
+ +
+
+ + +
+ +
+ {name} + {email} +
+
+
+ + +
+
+ + Theme +
+ + setTheme("system")}> + setTheme("light")}> + setTheme("dark")}> + +
+
+ + + + Log out + +
+
+
+
+ ); +} diff --git a/src/components/collection/collection_header.tsx b/src/components/collection/collection_header.tsx new file mode 100644 index 0000000..c308270 --- /dev/null +++ b/src/components/collection/collection_header.tsx @@ -0,0 +1,46 @@ +import { mdiCog } from "@mdi/js"; +import Icon from "@mdi/react"; +import { colors } from "~/lib/color"; +import { icons } from "~/lib/icon"; +import { CollectionSettingsDialog } from "~/components/collection/collection_settings_dialog"; +import { Skeleton } from "~/components/ui/skeleton"; +import { useCollection } from "~/hooks/use-metadata"; +import type { CollectionId } from "~/lib/metadata"; + +type CollectionHeaderProps = { + collectionId: CollectionId; +}; + +export function CollectionHeader(props: CollectionHeaderProps) { + const collection = useCollection(props.collectionId); + + const color = collection ? colors[collection.get("color")] : undefined; + const icon = collection ? icons[collection.get("icon")] : undefined; + + return ( + <> +
+
+ {collection + ? (icon && ) + : + } +
+ {collection + ?
{collection.get("name")}
+ :
+ } + + + +
+ + ); +} diff --git a/src/components/collection/collection_settings_dialog.tsx b/src/components/collection/collection_settings_dialog.tsx new file mode 100644 index 0000000..7d02a9f --- /dev/null +++ b/src/components/collection/collection_settings_dialog.tsx @@ -0,0 +1,211 @@ +import * as Y from "yjs"; +import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger } from "~/components/ui/dialog"; +import { Input } from "~/components/ui/input"; +import { Button } from "~/components/ui/button"; +import { ColorPicker } from "../form/color_picker"; +import { Label } from "~/components/ui/label"; +import { IconPicker } from "../form/icon_picker"; +import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger } from "~/components/ui/alert-dialog"; +import { useCollection, useCollections } from "~/hooks/use-metadata"; +import { type CollectionId, type CollectionProperty, deleteCollection } from "~/lib/metadata"; +import Icon from "@mdi/react"; +import { mdiPin, mdiPinOff, mdiPlus, mdiTrashCan } from "@mdi/js"; +import { createCollectionProperty, deleteCollectionProperty } from "~/lib/property"; +import { useEffect } from "react"; +import { PropertyTypeCombobox } from "../form/property_type_combobox"; + +type CollectionSettingsDialogProps = { + name?: string; + collectionId: CollectionId; + children: React.ReactNode; +}; +export function CollectionSettingsDialog(props: CollectionSettingsDialogProps) { + return ( + + + {props.children} + + + + {props.name} settings + + Settings for the {props.name} collection. + + +
+ + + +
+
+
+ ); +} + +function SettingsSection(props: { collectionId: CollectionId }) { + const collection = useCollection(props.collectionId) + + if (!collection) { + return null; + } + + return ( +
+
+ collection.set("icon", icon ?? "")} + /> +
+ + collection.set("name", name.currentTarget.value)} + /> +
+
+ collection.set("color", color)} + /> +
+ ); +} + +function PropertiesSection(props: { collectionId: CollectionId }) { + const collection = useCollection(props.collectionId) + + if (!collection) { + return null; + } + + // Ensure that the properties array exists on the collection + useEffect(() => { + if (!collection.has("properties")) { + collection.set("properties", new Y.Array() as any) + } + }, [collection]) + + const properties = collection.get("properties"); + + return ( +
+
+ +
+
+ {properties?.map((property) => + deleteCollectionProperty(properties, property.get("id"))} + /> + )} +
+
+ ); +} + +type PropertyItemProps = { + property: CollectionProperty, + onDelete: () => void, +} +function PropertyItem(props: PropertyItemProps) { + const { property: prop } = props; + + return ( +
+ prop.set("name", event.target.value)} + /> + prop.set("type", type)} + /> + + + + +
+ ); +} + +function DangerSection(props: { collectionId: CollectionId }) { + const collections = useCollections(); + + return ( +
+
+
+
+
+ Delete collection + Delete the collection and all items contained within. WARNING: This action is irreversible! +
+ + + + + + + Are you absolutely sure? + + This action cannot be undone. This will permanently delete the collection and ALL items contained in it. + + + + Cancel + + + + + + +
+
+ ); +} + +type HeaderProps = { + label: string, + children?: React.ReactNode, +} + +function Header(props: HeaderProps) { + return ( +
+

{props.label}

+ {props.children} +
+ ); +} diff --git a/src/components/collection/new_collection_dialog.tsx b/src/components/collection/new_collection_dialog.tsx new file mode 100644 index 0000000..d911234 --- /dev/null +++ b/src/components/collection/new_collection_dialog.tsx @@ -0,0 +1,76 @@ +import { useState } from "react"; +import { type ColorName } from "~/lib/color"; +import { type IconName } from "~/lib/icon"; +import { ColorPicker } from "../form/color_picker"; +import { IconPicker } from "../form/icon_picker"; +import { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from "~/components/ui/dialog"; +import { Button } from "~/components/ui/button"; +import { Label } from "~/components/ui/label"; +import { Input } from "~/components/ui/input"; +import { createCollection } from "~/lib/metadata"; +import { useCollections } from "~/hooks/use-metadata"; +import { useNavigate } from "@tanstack/react-router"; + +type NewCollectionDialogProps = { + children: React.ReactNode; +} + +export function NewCollectionDialog(props: NewCollectionDialogProps) { + const collections = useCollections(); + + const [name, setName] = useState(""); + const [icon, setIcon] = useState(); + const [color, setColor] = useState("white"); + + const navigate = useNavigate(); + function submit() { + const newCollection = createCollection(collections, { + name, + icon: icon ?? "", + color, + }) + setName(""); + setIcon(undefined); + setColor("white"); + + navigate({ to: "/app/collection/$id", params: { id: newCollection.get("id") } }); + } + + return ( + + + {props.children} + + + + New collection + + Settings for the new collection + + +
+
+ +
+ + setName(name.target.value)} + /> +
+
+ +
+ + + + + +
+
+ ); +} diff --git a/src/components/editor/link_icon.tsx b/src/components/editor/link_icon.tsx new file mode 100644 index 0000000..0c3bbdb --- /dev/null +++ b/src/components/editor/link_icon.tsx @@ -0,0 +1,17 @@ +import { mdiOpenInNew } from "@mdi/js"; +import Icon from "@mdi/react"; +import { cn } from "~/lib/utils"; + +export function LinkIcon(props: { url: string }) { + return ( + + + + ); +} diff --git a/src/components/editor/task_icon.tsx b/src/components/editor/task_icon.tsx new file mode 100644 index 0000000..0dea933 --- /dev/null +++ b/src/components/editor/task_icon.tsx @@ -0,0 +1,13 @@ +import Icon from "@mdi/react"; +import { cn } from "~/lib/utils"; + +export function TaskIcon(props: { icon?: string }) { + return ( +
+ {props.icon && } +
+ ); +} diff --git a/src/components/editor/term_icon.tsx b/src/components/editor/term_icon.tsx new file mode 100644 index 0000000..4dc54a4 --- /dev/null +++ b/src/components/editor/term_icon.tsx @@ -0,0 +1,18 @@ +import { mdiHelp } from "@mdi/js"; +import Icon from "@mdi/react"; +import { Link } from "@tanstack/react-router"; +import { cn } from "~/lib/utils"; + +export function TermIcon(props: { term: string }) { + return ( + + + + ); +} diff --git a/src/components/form/collection_picker.tsx b/src/components/form/collection_picker.tsx new file mode 100644 index 0000000..f73dd0f --- /dev/null +++ b/src/components/form/collection_picker.tsx @@ -0,0 +1,85 @@ +import Icon from "@mdi/react"; +import { ChevronsUpDown } from "lucide-react"; +import { useState } from "react"; +import { Button } from "~/components/ui/button"; +import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from "~/components/ui/command"; +import { Popover, PopoverContent, PopoverTrigger } from "~/components/ui/popover"; +import { useCollection, useCollections } from "~/hooks/use-metadata"; +import { colors } from "~/lib/color"; +import { icons } from "~/lib/icon"; +import { type CollectionId } from "~/lib/metadata"; +import { cn } from "~/lib/utils"; + +type CollectionPickerProps = { + onChange?: (collectionId?: CollectionId) => void; + className?: string; +} + +export function CollectionPicker(props: CollectionPickerProps) { + const [open, setOpen] = useState(false); + const [value, setValue] = useState(); + + const collections = useCollections(); + + return ( + + + + + + + + + No collection found. + + { + setValue(undefined); + setOpen(false); + props.onChange && props.onChange(undefined); + }} + > + + + {collections.map((collection) => ( + { + setValue(collection.get("id")); + setOpen(false); + props.onChange && props.onChange(collection.get("id")); + }} + > + + + ))} + + + + + + ); +} + +function Entry(props: { collectionId?: CollectionId }) { + const collection = useCollection(props.collectionId) + + return ( +
+
+ {collection?.get("icon") && } +
+ {collection?.get("name") ?? "None"} +
+ ); +} diff --git a/src/components/form/color_picker.tsx b/src/components/form/color_picker.tsx new file mode 100644 index 0000000..5bb9b9c --- /dev/null +++ b/src/components/form/color_picker.tsx @@ -0,0 +1,84 @@ +import { ChevronsUpDown } from "lucide-react"; +import { useState } from "react"; +import { Button } from "~/components/ui/button"; +import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from "~/components/ui/command"; +import { Popover, PopoverContent, PopoverTrigger } from "~/components/ui/popover"; +import { type Color, type ColorName, colors } from "~/lib/color"; +import { cn } from "~/lib/utils"; + +type ColorPickerProps = ({ + onChange?: (color: ColorName) => void; + withNone?: false; +} | { + onChange?: (color?: ColorName) => void; + withNone: true; +}) & { + initialValue?: ColorName; + className?: string; +} + +export function ColorPicker(props: ColorPickerProps) { + const [open, setOpen] = useState(false); + const [value, setValue] = useState(props.initialValue ?? (props.withNone ? undefined : "white")); + + return ( + + + + + + + + + No color found. + + {props.withNone && { + setValue(undefined) + setOpen(false) + props.onChange && props.onChange(undefined) + }} + > + + } + {Object.entries(colors).map(([key, value]) => ( + { + setValue(currentValue as ColorName) + setOpen(false) + props.onChange && props.onChange(currentValue as ColorName) + }} + > + + + ))} + + + + + + ); +} + +function Entry(props: { color?: Color }) { + return ( +
+
+ {props.color?.label ?? "None"} +
+ ); +} diff --git a/src/components/form/icon_picker.tsx b/src/components/form/icon_picker.tsx new file mode 100644 index 0000000..fa95a7f --- /dev/null +++ b/src/components/form/icon_picker.tsx @@ -0,0 +1,82 @@ +import Icon from "@mdi/react"; +import { useState } from "react"; +import { Button } from "~/components/ui/button"; +import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from "~/components/ui/command"; +import { Popover, PopoverContent, PopoverTrigger } from "~/components/ui/popover"; +import { type Icon as IconType, type IconName, icons } from "~/lib/icon"; + +type IconPickerProps = ({ + onChange?: (icon: IconName) => void; + withNone?: false; +} | { + onChange?: (icon?: IconName) => void; + withNone: true; +}) & { + initialValue?: IconName; + className?: string; +} + +// TODO: Make this faster by not showing all icons at once +export function IconPicker(props: IconPickerProps) { + const [value, setValue] = useState(props.initialValue); + const [open, setOpen] = useState(false); + + return ( + + + + + + + + + No icon found. + + {props.withNone && { + setValue(undefined) + setOpen(false) + props.onChange && props.onChange(undefined) + }}> + + } + {Object.entries(icons).map(([key, value]) => ( + { + setValue(currentValue as IconName) + setOpen(false) + props.onChange && props.onChange(currentValue as IconName) + }} + > + + + ))} + + + + + + ); +} + +function Entry(props: { icon?: IconType, withLabel?: boolean }) { + const withLabel = props.withLabel ?? false; + return ( +
+
+ {props.icon && } +
+ {withLabel && {props.icon?.name ?? "None"}} +
+ ); +} diff --git a/src/components/form/property_type_combobox.tsx b/src/components/form/property_type_combobox.tsx new file mode 100644 index 0000000..cd72973 --- /dev/null +++ b/src/components/form/property_type_combobox.tsx @@ -0,0 +1,57 @@ +import { useState } from "react" +import { Popover, PopoverContent, PopoverTrigger } from "~/components/ui/popover" +import { ChevronsUpDown } from "lucide-react" +import { properties, type PropertyType } from "~/lib/property" +import { Button } from "~/components/ui/button" +import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from "~/components/ui/command" + +type PropertyTypeComboboxProps = { + initialValue: PropertyType + onSelect: (value: PropertyType) => void +} + +export function PropertyTypeCombobox(props: PropertyTypeComboboxProps) { + const [open, setOpen] = useState(false) + const [value, setValue] = useState(props.initialValue) + + return ( + + + + + + + + + No type found. + + {Object.entries(properties).map(([key, value]) => ( + { + setValue(currentValue as PropertyType) + setOpen(false) + props.onSelect(currentValue as PropertyType) + }} + > + {value} + + ))} + + + + + + ) +} diff --git a/src/components/note/new_note_dialog.tsx b/src/components/note/new_note_dialog.tsx new file mode 100644 index 0000000..21bd699 --- /dev/null +++ b/src/components/note/new_note_dialog.tsx @@ -0,0 +1,120 @@ +import { useState } from "react"; +import { Button } from "~/components/ui/button"; +import { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from "~/components/ui/dialog"; +import { Input } from "~/components/ui/input"; +import { Label } from "~/components/ui/label"; +import { ColorPicker } from "~/components/form/color_picker"; +import type { ColorName } from "~/lib/color"; +import { IconPicker } from "~/components/form/icon_picker"; +import type { IconName } from "~/lib/icon"; +import { CollectionPicker } from "~/components/form/collection_picker"; +import { type CollectionId, createNote } from "~/lib/metadata"; +import { useNotesMetadata } from "~/hooks/use-metadata"; +import { RadioGroup, RadioGroupItem } from "~/components/ui/radio-group"; +import { useNavigate } from "@tanstack/react-router"; + +type NewNoteDialogProps = { + children: React.ReactNode; +} + +export function NewNoteDialog(props: NewNoteDialogProps) { + const noteMetadata = useNotesMetadata(); + + const [name, setName] = useState(""); + const [icon, setIcon] = useState(); + const [collection, setCollection] = useState(); + const [primaryColor, setPrimaryColor] = useState(); + const [secondaryColor, setSecondaryColor] = useState(); + const [type, setType] = useState<"text" | "canvas">("text"); + + const navigate = useNavigate(); + function submit() { + const newNote = createNote(noteMetadata, { + name, + icon, + collectionId: collection, + primaryColor, + secondaryColor, + type, + }); + + setName(""); + setIcon(undefined); + setCollection(undefined); + setPrimaryColor(undefined); + setSecondaryColor(undefined); + setType("text"); + + navigate({ to: "/app/note/$id", params: { id: newNote.get("id") } }); + } + + return ( + + + {props.children} + + + + New note + + Settings for the new collection + + +
+
+ +
+ + setName(name.target.value)} + /> +
+
+
+ + +
+
+
+ + +
+
+ + +
+
+
+ + +
+ + +
+
+ + +
+
+
+
+ + + + + +
+
+ ); +} diff --git a/src/components/note/note_canvas.tsx b/src/components/note/note_canvas.tsx new file mode 100644 index 0000000..126a722 --- /dev/null +++ b/src/components/note/note_canvas.tsx @@ -0,0 +1,76 @@ +import type { ExcalidrawImperativeAPI } from "@excalidraw/excalidraw/types"; +import { ExcalidrawBinding, yjsToExcalidraw } from "y-excalidraw" +import { Button, Excalidraw } from "@excalidraw/excalidraw"; +import * as Y from "yjs"; +import { useEffect, useMemo, useRef, useState } from "react"; + +import "@excalidraw/excalidraw/index.css"; +import { useNoteMetadata } from "~/hooks/use-metadata"; +import { Settings } from "lucide-react"; +import { NoteSettingsDialog } from "./note_settings_dialog"; +import { useNoteArray, useNoteAwareness, useNoteMap } from "~/hooks/use-note"; +import { useEffectiveTheme } from "~/hooks/use-theme"; + +export default function NoteCanvas(props: { noteId: string }) { + const [api, setApi] = useState(null); + const [binding, setBindings] = useState(null); + const excalidrawRef = useRef(null); + const metadata = useNoteMetadata(props.noteId); + const theme = useEffectiveTheme(); + + const yElements = useNoteArray("elements") as Y.Array; + const yAssets = useNoteMap("assets"); + const awareness = useNoteAwareness(); + + useEffect(() => { + if (!api) return; + + const binding = new ExcalidrawBinding( + yElements, + yAssets, + api, + awareness, + // excalidraw dom is needed to override the undo/redo buttons in the UI as there is no way to override it via props in excalidraw + // You might need to pass {trackedOrigins: new Set()} to undomanager depending on whether your provider sets an origin or not + { excalidrawDom: excalidrawRef.current!, undoManager: new Y.UndoManager(yElements) }, + ); + setBindings(binding); + return () => { + setBindings(null); + binding.destroy(); + }; + }, [api]); + + const initData = { + elements: yjsToExcalidraw(yElements) + } + + const excalidraw = useMemo(() => (<> + {metadata && + + } + )} + />, []); + return ( +
+ {excalidraw} +
+ ); +} + + diff --git a/src/components/note/note_header.tsx b/src/components/note/note_header.tsx new file mode 100644 index 0000000..77dfe84 --- /dev/null +++ b/src/components/note/note_header.tsx @@ -0,0 +1,96 @@ +import Icon from "@mdi/react"; +import { colors } from "~/lib/color"; +import { icons } from "~/lib/icon"; +import { NotePropertiesDialog } from "~/components/note/note_properties_dialog"; +import { mdiCircle, mdiCog } from "@mdi/js"; +import { NoteSettingsDialog } from "~/components/note/note_settings_dialog"; +import { useCollection, useNoteMetadata } from "~/hooks/use-metadata"; +import type { NoteId, NoteMetadata } from "~/lib/metadata"; + +export function NoteHeader(props: { id: NoteId }) { + const noteMetadata = useNoteMetadata(props.id); + + if (!noteMetadata) { + return null; + } + + const primaryColorName = noteMetadata.get("primaryColor"); + const secondaryColorName = noteMetadata.get("secondaryColor"); + const primaryColor = primaryColorName ? colors[primaryColorName] : colors.white; + const secondaryColor = secondaryColorName ? colors[secondaryColorName] : primaryColor; + + return ( + <> +
+
+ + + +
+
+ {noteMetadata.get("icon") && } +
+
+ +
+
+ { noteMetadata.set("title", e.target.value)} + />} +
+ +
+ + ); +} + +function Properties(props: { noteMetadata: NoteMetadata }) { + const { noteMetadata } = props; + const collection = useCollection(noteMetadata.get("collectionId")); + + if (!collection) { + return No properties; + } + + const collectionProps = collection.get("properties")?.toArray(); + if (!collectionProps) { + return No properties; + } + + const pinnedProperties = collectionProps.filter(property => property.get("pinned")); + + return ( + + + + ); +} diff --git a/src/components/note/note_properties_dialog.tsx b/src/components/note/note_properties_dialog.tsx new file mode 100644 index 0000000..f58115e --- /dev/null +++ b/src/components/note/note_properties_dialog.tsx @@ -0,0 +1,114 @@ +import * as Y from "yjs"; +import { useCallback, useEffect } from "react"; +import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger } from "~/components/ui/dialog"; +import { Input } from "~/components/ui/input"; +import type { NoteMetadata, NoteProperty } from "~/lib/metadata"; +import { createNoteProperty, type PropertyType } from "~/lib/property"; +import { useCollection } from "~/hooks/use-metadata"; + +type NotePropertiesDialogProps = { + noteMetadata: NoteMetadata; + children: React.ReactNode; +}; +export function NotePropertiesDialog(props: NotePropertiesDialogProps) { + const { noteMetadata } = props; + + if (!noteMetadata) { + return props.children; + } + + return ( + + + {props.children} + + + + {noteMetadata.get("title")} properties + + Properties for the {noteMetadata.get("title")} note + + +
+ {noteMetadata.has("collectionId") + ? + : Only notes that belong to a collection can have properties + } +
+
+
+ ); +} + +function PropertiesSection(props: { noteMetadata: NoteMetadata }) { + const { noteMetadata } = props; + const collection = useCollection(noteMetadata.get("collectionId")); + + useEffect(() => { + if (!noteMetadata.has("properties")) { + noteMetadata.set("properties", new Y.Array() as any) + } + }, [noteMetadata]) + + const noteProperties = noteMetadata.get("properties"); + if (!noteProperties) { + return null; + } + + if (!collection) { + return null; + } + + const collectionProps = collection.get("properties")?.toArray(); + if (!collectionProps) { + return null; + } + + + + return ( +
+ {collectionProps.map((property) => { + const propertyId = property.get("id"); + const noteProperty = noteProperties + ?.toArray() + .find(it => it.get("propertyId") == propertyId) + return createNoteProperty(noteProperties, { propertyId })} + /> + })} +
+ ); +} + + +type PropertyItemProps = { + noteProperty?: NoteProperty; + name: string; + type: PropertyType; + create: () => NoteProperty; +} +function PropertyItem(props: PropertyItemProps) { + const handleChange = useCallback((value: string) => { + if (props.noteProperty) { + props.noteProperty.set("value", value); + } else { + props.create().set("value", value); + } + }, [props.noteProperty, props.create]); + + return ( +
+ {props.name} + handleChange(value.currentTarget.value)} + /> +
+ ); +} diff --git a/src/components/note/note_settings_dialog.tsx b/src/components/note/note_settings_dialog.tsx new file mode 100644 index 0000000..3652f66 --- /dev/null +++ b/src/components/note/note_settings_dialog.tsx @@ -0,0 +1,75 @@ +import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger } from "~/components/ui/dialog"; +import { IconPicker } from "../form/icon_picker"; +import { Input } from "~/components/ui/input"; +import { Label } from "~/components/ui/label"; +import { ColorPicker } from "../form/color_picker"; +import type { NoteMetadata } from "~/lib/metadata"; + +type NoteSettingsDialogProps = { + noteMetadata: NoteMetadata; + children: React.ReactNode; +}; + +export function NoteSettingsDialog(props: NoteSettingsDialogProps) { + const { noteMetadata } = props; + if (!noteMetadata) { + return props.children; + } + + const primaryColor = noteMetadata.get("primaryColor"); + const secondaryColor = noteMetadata.get("secondaryColor"); + + return ( + + + {props.children} + + + + {noteMetadata.get("title")} settings + + Settings for {noteMetadata.get("title")}. + + + {
+
+ noteMetadata.set("icon", newIcon ?? "")} + initialValue={noteMetadata.get("icon") == "" ? undefined : noteMetadata.get("icon")} + /> +
+ + noteMetadata.set("title", name.target.value)} + /> +
+
+
+
+ + noteMetadata.set("primaryColor", color ?? "")} + initialValue={primaryColor == "" ? undefined : primaryColor} + className="w-full flex-grow" + /> +
+
+ + noteMetadata.set("secondaryColor", color ?? "")} + initialValue={secondaryColor == "" ? undefined : secondaryColor} + className="w-full flex-grow" + /> +
+
+
} +
+
+ ); +} diff --git a/src/components/note/notes_grid.tsx b/src/components/note/notes_grid.tsx new file mode 100644 index 0000000..452192d --- /dev/null +++ b/src/components/note/notes_grid.tsx @@ -0,0 +1,105 @@ +import { mdiMagnify } from "@mdi/js"; +import Icon from "@mdi/react"; +import { Link } from "@tanstack/react-router"; +import { Command } from "cmdk"; +import { colors } from "~/lib/color"; +import { icons } from "~/lib/icon"; +import type { NoteMetadata } from "~/lib/metadata"; + +type NotesGridProps = { + notes?: NoteMetadata[]; + allUnpinned?: boolean; +}; +export function NotesGrid(props: NotesGridProps) { + const allUnpinned = props.allUnpinned ?? false; + + const pinnedNotes = props.notes?.filter(it => !allUnpinned && it.get("pinned"))?.map((note) => ); + const normalNotes = props.notes?.filter(it => allUnpinned || !it.get("pinned"))?.map((note) => ); + + return ( + +
+
+ +
+ + + + +
+ + No notes found + + {pinnedNotes} + + + + {normalNotes} + + +
+ ); +} + +type DetailedNoteProps = { + note: NoteMetadata; +} + +function DetailedNote(props: DetailedNoteProps) { + const { note } = props; + + const primaryColorName = note.get("primaryColor") + const secondaryColorName = note.get("secondaryColor") + + const primaryColor = primaryColorName ? colors[primaryColorName] : colors.white; + const secondaryColor = secondaryColorName ? colors[secondaryColorName] : primaryColor; + const icon = note.get("icon") ? icons[note.get("icon")] : undefined; + + return ( + + +
+ {icon &&
+ +
} +
+

{note.get("title")}

+
+ + + ); +} + +type NormalNoteProps = { + note: NoteMetadata; +}; +function NormalNote(props: NormalNoteProps) { + const { note } = props; + + const colorName = note.get("primaryColor"); + + const color = colorName ? colors[colorName] : colors.white; + const icon = note.get("icon") ? icons[note.get("icon")] : undefined; + + return ( + + +
+
+
+ {icon && } +

{note.get("title")}

+
+
+ + + ); +} diff --git a/src/components/ui/alert-dialog.tsx b/src/components/ui/alert-dialog.tsx new file mode 100644 index 0000000..59eaa6c --- /dev/null +++ b/src/components/ui/alert-dialog.tsx @@ -0,0 +1,155 @@ +import * as React from "react" +import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog" + +import { cn } from "~/lib/utils" +import { buttonVariants } from "~/components/ui/button" + +function AlertDialog({ + ...props +}: React.ComponentProps) { + return +} + +function AlertDialogTrigger({ + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AlertDialogPortal({ + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AlertDialogOverlay({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AlertDialogContent({ + className, + ...props +}: React.ComponentProps) { + return ( + + + + + ) +} + +function AlertDialogHeader({ + className, + ...props +}: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function AlertDialogFooter({ + className, + ...props +}: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function AlertDialogTitle({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AlertDialogDescription({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AlertDialogAction({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AlertDialogCancel({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +export { + AlertDialog, + AlertDialogPortal, + AlertDialogOverlay, + AlertDialogTrigger, + AlertDialogContent, + AlertDialogHeader, + AlertDialogFooter, + AlertDialogTitle, + AlertDialogDescription, + AlertDialogAction, + AlertDialogCancel, +} diff --git a/src/components/ui/avatar.tsx b/src/components/ui/avatar.tsx new file mode 100644 index 0000000..205a1a1 --- /dev/null +++ b/src/components/ui/avatar.tsx @@ -0,0 +1,51 @@ +import * as React from "react" +import * as AvatarPrimitive from "@radix-ui/react-avatar" + +import { cn } from "~/lib/utils" + +function Avatar({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AvatarImage({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AvatarFallback({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +export { Avatar, AvatarImage, AvatarFallback } diff --git a/src/components/ui/command.tsx b/src/components/ui/command.tsx new file mode 100644 index 0000000..a4db9f0 --- /dev/null +++ b/src/components/ui/command.tsx @@ -0,0 +1,175 @@ +import * as React from "react" +import { Command as CommandPrimitive } from "cmdk" +import { SearchIcon } from "lucide-react" + +import { cn } from "~/lib/utils" +import { + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle, +} from "~/components/ui/dialog" + +function Command({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function CommandDialog({ + title = "Command Palette", + description = "Search for a command to run...", + children, + ...props +}: React.ComponentProps & { + title?: string + description?: string +}) { + return ( + + + {title} + {description} + + + + {children} + + + + ) +} + +function CommandInput({ + className, + ...props +}: React.ComponentProps) { + return ( +
+ + +
+ ) +} + +function CommandList({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function CommandEmpty({ + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function CommandGroup({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function CommandSeparator({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function CommandItem({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function CommandShortcut({ + className, + ...props +}: React.ComponentProps<"span">) { + return ( + + ) +} + +export { + Command, + CommandDialog, + CommandInput, + CommandList, + CommandEmpty, + CommandGroup, + CommandItem, + CommandShortcut, + CommandSeparator, +} diff --git a/src/components/ui/dialog.tsx b/src/components/ui/dialog.tsx new file mode 100644 index 0000000..367a960 --- /dev/null +++ b/src/components/ui/dialog.tsx @@ -0,0 +1,133 @@ +import * as React from "react" +import * as DialogPrimitive from "@radix-ui/react-dialog" +import { XIcon } from "lucide-react" + +import { cn } from "~/lib/utils" + +function Dialog({ + ...props +}: React.ComponentProps) { + return +} + +function DialogTrigger({ + ...props +}: React.ComponentProps) { + return +} + +function DialogPortal({ + ...props +}: React.ComponentProps) { + return +} + +function DialogClose({ + ...props +}: React.ComponentProps) { + return +} + +function DialogOverlay({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function DialogContent({ + className, + children, + ...props +}: React.ComponentProps) { + return ( + + + + {children} + + + Close + + + + ) +} + +function DialogHeader({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function DialogFooter({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function DialogTitle({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function DialogDescription({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +export { + Dialog, + DialogClose, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogOverlay, + DialogPortal, + DialogTitle, + DialogTrigger, +} diff --git a/src/components/ui/dropdown-menu.tsx b/src/components/ui/dropdown-menu.tsx new file mode 100644 index 0000000..68c82bc --- /dev/null +++ b/src/components/ui/dropdown-menu.tsx @@ -0,0 +1,255 @@ +import * as React from "react" +import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu" +import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react" + +import { cn } from "~/lib/utils" + +function DropdownMenu({ + ...props +}: React.ComponentProps) { + return +} + +function DropdownMenuPortal({ + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function DropdownMenuTrigger({ + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function DropdownMenuContent({ + className, + sideOffset = 4, + ...props +}: React.ComponentProps) { + return ( + + + + ) +} + +function DropdownMenuGroup({ + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function DropdownMenuItem({ + className, + inset, + variant = "default", + ...props +}: React.ComponentProps & { + inset?: boolean + variant?: "default" | "destructive" +}) { + return ( + + ) +} + +function DropdownMenuCheckboxItem({ + className, + children, + checked, + ...props +}: React.ComponentProps) { + return ( + + + + + + + {children} + + ) +} + +function DropdownMenuRadioGroup({ + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function DropdownMenuRadioItem({ + className, + children, + ...props +}: React.ComponentProps) { + return ( + + + + + + + {children} + + ) +} + +function DropdownMenuLabel({ + className, + inset, + ...props +}: React.ComponentProps & { + inset?: boolean +}) { + return ( + + ) +} + +function DropdownMenuSeparator({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function DropdownMenuShortcut({ + className, + ...props +}: React.ComponentProps<"span">) { + return ( + + ) +} + +function DropdownMenuSub({ + ...props +}: React.ComponentProps) { + return +} + +function DropdownMenuSubTrigger({ + className, + inset, + children, + ...props +}: React.ComponentProps & { + inset?: boolean +}) { + return ( + + {children} + + + ) +} + +function DropdownMenuSubContent({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +export { + DropdownMenu, + DropdownMenuPortal, + DropdownMenuTrigger, + DropdownMenuContent, + DropdownMenuGroup, + DropdownMenuLabel, + DropdownMenuItem, + DropdownMenuCheckboxItem, + DropdownMenuRadioGroup, + DropdownMenuRadioItem, + DropdownMenuSeparator, + DropdownMenuShortcut, + DropdownMenuSub, + DropdownMenuSubTrigger, + DropdownMenuSubContent, +} diff --git a/src/components/ui/label.tsx b/src/components/ui/label.tsx new file mode 100644 index 0000000..9aa23b4 --- /dev/null +++ b/src/components/ui/label.tsx @@ -0,0 +1,22 @@ +import * as React from "react" +import * as LabelPrimitive from "@radix-ui/react-label" + +import { cn } from "~/lib/utils" + +function Label({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +export { Label } diff --git a/src/components/ui/popover.tsx b/src/components/ui/popover.tsx new file mode 100644 index 0000000..201f7d4 --- /dev/null +++ b/src/components/ui/popover.tsx @@ -0,0 +1,46 @@ +import * as React from "react" +import * as PopoverPrimitive from "@radix-ui/react-popover" + +import { cn } from "~/lib/utils" + +function Popover({ + ...props +}: React.ComponentProps) { + return +} + +function PopoverTrigger({ + ...props +}: React.ComponentProps) { + return +} + +function PopoverContent({ + className, + align = "center", + sideOffset = 4, + ...props +}: React.ComponentProps) { + return ( + + + + ) +} + +function PopoverAnchor({ + ...props +}: React.ComponentProps) { + return +} + +export { Popover, PopoverTrigger, PopoverContent, PopoverAnchor } diff --git a/src/components/ui/radio-group.tsx b/src/components/ui/radio-group.tsx new file mode 100644 index 0000000..b1c5667 --- /dev/null +++ b/src/components/ui/radio-group.tsx @@ -0,0 +1,45 @@ +"use client" + +import * as React from "react" +import * as RadioGroupPrimitive from "@radix-ui/react-radio-group" +import { CircleIcon } from "lucide-react" + +import { cn } from "~/lib/utils" + +function RadioGroup({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function RadioGroupItem({ + className, + ...props +}: React.ComponentProps) { + return ( + + + + + + ) +} + +export { RadioGroup, RadioGroupItem } diff --git a/src/components/ui/sidebar.tsx b/src/components/ui/sidebar.tsx index 3de327a..d44cb24 100644 --- a/src/components/ui/sidebar.tsx +++ b/src/components/ui/sidebar.tsx @@ -308,7 +308,7 @@ function SidebarInset({ className, ...props }: React.ComponentProps<"main">) { data-slot="sidebar-inset" className={cn( "bg-background relative flex w-full flex-1 flex-col", - "md:peer-data-[variant=inset]:m-2 md:peer-data-[variant=inset]:ml-0 md:peer-data-[variant=inset]:rounded-xl md:peer-data-[variant=inset]:shadow-sm md:peer-data-[variant=inset]:peer-data-[state=collapsed]:ml-2", + "md:group-peer-data-[variant=inset]:m-2 md:group-peer-data-[variant=inset]:ml-0 md:group-peer-data-[variant=inset]:rounded-xl md:group-peer-data-[variant=inset]:shadow-sm", className )} {...props} diff --git a/src/components/ui/toggle-group.tsx b/src/components/ui/toggle-group.tsx new file mode 100644 index 0000000..711481b --- /dev/null +++ b/src/components/ui/toggle-group.tsx @@ -0,0 +1,71 @@ +import * as React from "react" +import * as ToggleGroupPrimitive from "@radix-ui/react-toggle-group" +import { type VariantProps } from "class-variance-authority" + +import { cn } from "~/lib/utils" +import { toggleVariants } from "~/components/ui/toggle" + +const ToggleGroupContext = React.createContext< + VariantProps +>({ + size: "default", + variant: "default", +}) + +function ToggleGroup({ + className, + variant, + size, + children, + ...props +}: React.ComponentProps & + VariantProps) { + return ( + + + {children} + + + ) +} + +function ToggleGroupItem({ + className, + children, + variant, + size, + ...props +}: React.ComponentProps & + VariantProps) { + const context = React.useContext(ToggleGroupContext) + + return ( + + {children} + + ) +} + +export { ToggleGroup, ToggleGroupItem } diff --git a/src/components/ui/toggle.tsx b/src/components/ui/toggle.tsx new file mode 100644 index 0000000..6192e67 --- /dev/null +++ b/src/components/ui/toggle.tsx @@ -0,0 +1,47 @@ +"use client" + +import * as React from "react" +import * as TogglePrimitive from "@radix-ui/react-toggle" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "~/lib/utils" + +const toggleVariants = cva( + "inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium hover:bg-muted hover:text-muted-foreground disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:shrink-0 focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] outline-none transition-[color,box-shadow] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive whitespace-nowrap", + { + variants: { + variant: { + default: "bg-transparent", + outline: + "border border-input bg-transparent shadow-xs hover:bg-accent hover:text-accent-foreground", + }, + size: { + default: "h-9 px-2 min-w-9", + sm: "h-8 px-1.5 min-w-8", + lg: "h-10 px-2.5 min-w-10", + }, + }, + defaultVariants: { + variant: "default", + size: "default", + }, + } +) + +function Toggle({ + className, + variant, + size, + ...props +}: React.ComponentProps & + VariantProps) { + return ( + + ) +} + +export { Toggle, toggleVariants } diff --git a/src/components/user/user_avatar.tsx b/src/components/user/user_avatar.tsx new file mode 100644 index 0000000..71e6307 --- /dev/null +++ b/src/components/user/user_avatar.tsx @@ -0,0 +1,51 @@ +import { useEffect, useState } from "react"; +import { Avatar, AvatarFallback, AvatarImage } from "~/components/ui/avatar"; + +type User = { + name?: string; + email?: string; + image?: string; +}; + +type UserAvatarProps = { + className?: string; + user?: User; +}; + +export function UserAvatar(props: UserAvatarProps) { + const email = props.user?.email ?? undefined; + const gravatar = useGravatarUrl(email); + const avatar = props.user?.image ?? gravatar; + const name = props.user?.name ?? undefined + const initials = getInitials(name); + + return ( + + + {initials} + + ); +} + +function useGravatarUrl(email?: string, size = 80) { + const [url, setUrl] = useState(); + useEffect(() => { + if (!email) return; + + const trimmedEmail = email.trim().toLowerCase(); + crypto.subtle.digest('SHA-256', new TextEncoder().encode(trimmedEmail)).then(hashBuffer => { + const hashArray = Array.from(new Uint8Array(hashBuffer)); + const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join(''); + setUrl(`https://www.gravatar.com/avatar/${hashHex}?s=${size}&d=identicon`); + }); + }, [email, size]); + + return url; +} + +function getInitials(name?: string) { + if (!name) return ""; + + const parts = name.split(" "); + return parts.map(part => part[0]).join(""); +} diff --git a/src/components/yjs/metadata-inspector.tsx b/src/components/yjs/metadata-inspector.tsx new file mode 100644 index 0000000..b06dd9d --- /dev/null +++ b/src/components/yjs/metadata-inspector.tsx @@ -0,0 +1,64 @@ +import * as Y from "yjs"; +import { useCollections, useNotesMetadata } from "~/hooks/use-metadata"; + +export function MetadataInspector() { + const collections = useCollections() + const notes = useNotesMetadata() + + return
+ Collections: + Notes: +
+} + +export function YjsInspector(props: { toRender: any }) { + return +} + +type YjsNodeProps = { + node: T + depth: number + label?: string +} + +function YjsNode(props: YjsNodeProps) { + if (props.node instanceof Y.Map) return + if (props.node instanceof Y.Array) return + if (typeof props.node == "string") return + + return
+ {props.label}: {props.node.toString()} +
+} + +function YjsMap(props: YjsNodeProps>) { + const entries = Array.from(props.node.entries()) + return
+ {props.label ? `${props.label}: ` : ""}{"{"}YMap +
+ {entries.map(([key, item]) => )} +
+ {"}"} +
+} + +function YjsArray(props: YjsNodeProps>) { + return
+ {props.label ? `${props.label}: ` : ""}{"["}YArray +
+ {props.node.map((item, index) => )} +
+ {"]"} +
+} + +function Badge(props: { children: React.ReactNode }) { + return + {props.children} + +} + +function String(props: YjsNodeProps) { + return
{props.label}: "{props.node}"
+} + diff --git a/src/editor/Editor.tsx b/src/editor/Editor.tsx new file mode 100644 index 0000000..8e13dd0 --- /dev/null +++ b/src/editor/Editor.tsx @@ -0,0 +1,90 @@ +"use client"; + +import * as Y from "yjs"; +import { type InitialConfigType, LexicalComposer } from "@lexical/react/LexicalComposer"; +import { PlainTextPlugin } from "@lexical/react/LexicalPlainTextPlugin"; +import { CollaborationPlugin } from '@lexical/react/LexicalCollaborationPlugin'; +import { ContentEditable } from "@lexical/react/LexicalContentEditable"; +import { LexicalErrorBoundary } from "@lexical/react/LexicalErrorBoundary"; +import { HeaderMarkerNode, HeaderNode } from "./nodes/header_node"; +import { ParagraphPlugin } from "./plugins/paragraph_plugin"; +import { HeaderPlugin } from "./plugins/header_plugin"; +import { SelectionPlugin } from "./plugins/selection_plugin"; +import { FormattedTextMarkerNode, FormattedTextNode } from "./nodes/formatted_text"; +import { FormattedTextPlugin } from "./plugins/formatted_text_plugin"; +import { TaskIconNode, TaskMarkerNode, TaskNode } from "./nodes/task_node"; +import { TaskPlugin } from "./plugins/task_plugin"; +import { LinkIconNode, LinkMarkerNode, LinkNode, LinkUrlNode } from "./nodes/link_node"; +import { LinkPlugin } from "./plugins/link_plugin"; +import { TermIconNode, TermMarkerNode, TermNode } from "./nodes/term_node"; +import { TermPlugin } from "./plugins/term_plugin"; +import { useCallback } from "react"; +import type { Provider } from "@lexical/yjs"; +import type { NoteId } from "~/lib/metadata"; +import type { Klass, LexicalNode } from "lexical"; +import { useNoteDoc, useNoteProviders } from "~/hooks/use-note"; + +export function Editor(props: { noteId: NoteId }) { + const provider = useNoteProviders().websocket; + const doc = useNoteDoc(); + + const providerFactory = useCallback((id: string, yjsDocMap: Map): Provider => { + // Just overwrite it, because we have the doc managed externally + yjsDocMap.set(id, doc); + + return provider as any as Provider; + }, [provider, doc]); + + return ( + +
+ } + placeholder={
Enter some text...
} + ErrorBoundary={LexicalErrorBoundary} + /> +
+ + + + + + + + + + +
+ ); +} + +const excludedProperties = new Map, Set>([ + [HeaderNode, new Set(["__hasFocus"])], + [TermNode, new Set(["__hasFocus"])], + [TaskNode, new Set(["__hasFocus"])], + [LinkNode, new Set(["__hasFocus"])], + [FormattedTextNode, new Set(["__hasFocus"])], +]) + +const theme = { +}; + +const initialEditorConfig: InitialConfigType = { + namespace: "NoteEditor", + theme, + onError: console.error, + editorState: null, + nodes: [ + HeaderNode, HeaderMarkerNode, + FormattedTextNode, FormattedTextMarkerNode, + TaskNode, TaskMarkerNode, TaskIconNode, + LinkNode, LinkMarkerNode, LinkUrlNode, LinkIconNode, + TermNode, TermMarkerNode, TermIconNode, + ], +}; + diff --git a/src/editor/editor_utils.ts b/src/editor/editor_utils.ts new file mode 100644 index 0000000..b6b0413 --- /dev/null +++ b/src/editor/editor_utils.ts @@ -0,0 +1,26 @@ +import { $isRootOrShadowRoot, ElementNode, LexicalNode } from "lexical"; +import { $isLinkUrlNode } from "./nodes/link_node"; +import { $isTermNode } from "./nodes/term_node"; + +export function findBlockNode(node: LexicalNode): LexicalNode { + let toCheck: LexicalNode | null = node + while (!isBlockNode(toCheck)) { + if ($isRootOrShadowRoot(node)) { + console.error("findBlockNode encountered the root node. This should not be possible!"); + throw new Error("Could not find a block node in the parent chain"); + } + + if (toCheck === null) { + throw new Error("Could not find a block node in the parent chain"); + } + toCheck = toCheck.getParent(); + } + + return toCheck!; +} + + +export function isBlockNode(node?: LexicalNode | null): boolean { + return node instanceof ElementNode && !node.isInline(); +} + diff --git a/src/editor/nodes/focusable_node.ts b/src/editor/nodes/focusable_node.ts new file mode 100644 index 0000000..1e97965 --- /dev/null +++ b/src/editor/nodes/focusable_node.ts @@ -0,0 +1,32 @@ +import { ElementNode, type LexicalNode } from "lexical"; + +export class FocusableNode extends ElementNode { + __hasFocus: boolean = false; + + setFocus(hasFocus: boolean) { + const self = this.getWritable(); + self.__hasFocus = hasFocus; + } + + constructor(key?: string, hasFocus?: boolean) { + super(key); + hasFocus && (this.__hasFocus = hasFocus); + } + + createDOM(): HTMLElement { + const element = document.createElement("div"); + element.classList.toggle("focus", this.__hasFocus); + return element; + } + + updateDOM(old: FocusableNode, dom: HTMLElement): boolean { + if (this.__hasFocus != old.__hasFocus) { + dom.classList.toggle("focus", this.__hasFocus); + } + return false; + } +} + +export function $isFocusableNode(node: LexicalNode): node is FocusableNode { + return node instanceof FocusableNode; +} diff --git a/src/editor/nodes/formatted_text.ts b/src/editor/nodes/formatted_text.ts new file mode 100644 index 0000000..689fb38 --- /dev/null +++ b/src/editor/nodes/formatted_text.ts @@ -0,0 +1,109 @@ +import { ElementNode, type LexicalNode, type SerializedElementNode, type Spread } from "lexical"; +import { FocusableNode } from "./focusable_node"; +import type { TextFormat } from "../serialized_editor_content"; + +type SerializedTextFormatNode = Spread; + +export class FormattedTextNode extends FocusableNode { + __internalStyle: TextFormat; + + getStyle(): TextFormat { + const self = this.getLatest(); + return self.__internalStyle; + } + + static getType(): string { + return "formatted-text"; + } + + static clone(node: FormattedTextNode): FormattedTextNode { + return new FormattedTextNode(node.__internalStyle, node.__key, node.__hasFocus); + } + + constructor(style: TextFormat, key?: string, hasFocus?: boolean) { + super(key, hasFocus); + this.__internalStyle = style; + } + + isInline(): boolean { + return true; + } + + createDOM(): HTMLElement { + const element = super.createDOM(); + element.classList.add("formatted-text"); + element.classList.add(`format-${this.__internalStyle}`); + return element; + } + + updateDOM(old: FormattedTextNode, dom: HTMLElement): boolean { + return super.updateDOM(old, dom); + } + + exportJSON(): SerializedTextFormatNode { + return { + ...super.exportJSON(), + type: "formatted-text", + style: this.__internalStyle, + }; + } + + static importJSON(serializedNode: SerializedTextFormatNode): FormattedTextNode { + return $createFormattedTextNode(serializedNode.style); + } + +} + +export function $createFormattedTextNode(style: TextFormat) { + return new FormattedTextNode(style); +} + +export function $isFormattedTextNode(node?: LexicalNode | null): node is FormattedTextNode { + return node instanceof FormattedTextNode; +} + +export class FormattedTextMarkerNode extends ElementNode { + static getType(): string { + return "formatted-text-marker"; + } + + static clone(node: FormattedTextMarkerNode): FormattedTextMarkerNode { + return new FormattedTextMarkerNode(node.__key); + } + + isInline(): boolean { + return true; + } + + createDOM(): HTMLElement { + const element = document.createElement("span"); + element.classList.add("formatted-text-marker"); + return element; + } + + updateDOM(): boolean { + return false; + } + + exportJSON(): SerializedElementNode { + return { + ...super.exportJSON(), + type: "formatted-text-marker", + }; + } + + static importJSON() { + return $createFormattedTextMarkerNode(); + } +} + +export function $createFormattedTextMarkerNode() { + return new FormattedTextMarkerNode(); +} + +export function $isFormattedTextMarkerNode(node?: LexicalNode | null): node is FormattedTextMarkerNode { + return node instanceof FormattedTextMarkerNode; +} + diff --git a/src/editor/nodes/header_node.ts b/src/editor/nodes/header_node.ts new file mode 100644 index 0000000..65109ca --- /dev/null +++ b/src/editor/nodes/header_node.ts @@ -0,0 +1,108 @@ +import { $createParagraphNode, ElementNode, type LexicalNode, ParagraphNode, type RangeSelection, type SerializedElementNode, type Spread } from "lexical"; +import { FocusableNode } from "./focusable_node"; + +type SerializedHeaderNode = Spread; + +export class HeaderNode extends FocusableNode { + __level: number; + + getLevel() { + const self = this.getLatest(); + return self.__level; + } + + static getType() { + return "header"; + } + + static clone(node: HeaderNode) { + return new HeaderNode(node.__level, node.__key, node.__hasFocus); + } + + constructor(level: number, key?: string, hasFocus?: boolean) { + super(key, hasFocus); + this.__level = level; + } + + createDOM(): HTMLElement { + const element = super.createDOM(); + element.classList.add("header", `header-${this.__level}`); + return element; + } + + updateDOM(old: HeaderNode, dom: HTMLElement): boolean { + return super.updateDOM(old, dom); + } + + insertNewAfter(_selection: RangeSelection, restoreSelection?: boolean): ParagraphNode { + const newElement = $createParagraphNode(); + this.insertAfter(newElement, restoreSelection); + return newElement; + } + + exportJSON(): SerializedHeaderNode { + return { + ...super.exportJSON(), + type: "header", + level: this.__level, + }; + } + + static importJSON(serializedHeaderNode: SerializedHeaderNode) { + return $createHeaderNode(serializedHeaderNode.level); + } +} + +export function $createHeaderNode(level: number) { + return new HeaderNode(level); +} + +export function $isHeaderNode(node?: LexicalNode | null): node is HeaderNode { + return node instanceof HeaderNode; +} + +export class HeaderMarkerNode extends ElementNode { + static getType() { + return "header-marker"; + } + + static clone(node: HeaderMarkerNode) { + return new HeaderMarkerNode(node.__key); + } + + isInline(): boolean { + return true; + } + + createDOM(): HTMLElement { + const element = document.createElement("span"); + element.classList.add("header-marker"); + return element; + } + + updateDOM(): boolean { + return false; + } + + exportJSON(): SerializedElementNode { + return { + ...super.exportJSON(), + type: "header-marker", + }; + } + + static importJSON() { + return $createHeaderMarkerNode(); + } +} + +export function $createHeaderMarkerNode() { + return new HeaderMarkerNode(); +} + +export function $isHeaderMarkerNode(node?: LexicalNode | null): node is HeaderMarkerNode { + return node instanceof HeaderMarkerNode; +} + diff --git a/src/editor/nodes/link_node.tsx b/src/editor/nodes/link_node.tsx new file mode 100644 index 0000000..dd87a58 --- /dev/null +++ b/src/editor/nodes/link_node.tsx @@ -0,0 +1,184 @@ +import { DecoratorNode, ElementNode, type LexicalNode, type SerializedElementNode, type SerializedLexicalNode, type Spread } from "lexical"; +import { FocusableNode } from "./focusable_node"; +import type { ReactNode } from "react"; +import { LinkIcon } from "~/components/editor/link_icon"; + +type SerializedLinkNode = Spread; + +export class LinkNode extends FocusableNode { + __url: string; + + getUrl(): string { + const self = this.getLatest(); + return self.__url; + } + + static getType() { + return "link"; + } + static clone(node: LinkNode) { + return new LinkNode(node.__url, node.__key, node.__hasFocus); + } + constructor(url: string, key?: string, hasFocus?: boolean) { + super(key, hasFocus); + this.__url = url; + } + + isInline(): boolean { + return true; + } + createDOM(): HTMLElement { + const element = super.createDOM(); + element.classList.add("link"); + return element; + } + updateDOM(old: LinkNode, dom: HTMLElement): boolean { + return super.updateDOM(old, dom); + } + exportJSON(): SerializedLinkNode { + return { + ...super.exportJSON(), + type: "link", + url: this.__url, + }; + } + static importJSON(serializedNode: SerializedLinkNode) { + return $createLinkNode(serializedNode.url); + } +} + +export function $createLinkNode(url: string) { + return new LinkNode(url); +} + +export function $isLinkNode(node?: LexicalNode | null): node is LinkNode { + return node instanceof LinkNode; +} + +export class LinkMarkerNode extends ElementNode { + static getType() { + return "link-marker"; + } + static clone(node: LinkMarkerNode) { + return new LinkMarkerNode(node.__key); + } + isInline(): boolean { + return true; + } + createDOM(): HTMLElement { + const element = document.createElement("span"); + element.classList.add("link-marker"); + return element; + } + updateDOM(): boolean { + return false; + } + exportJSON(): SerializedElementNode { + return { + ...super.exportJSON(), + type: "link-marker", + }; + } + static importJSON() { + return $createLinkMarkerNode(); + } +} + +export function $createLinkMarkerNode() { + return new LinkMarkerNode(); +} + +export function $isLinkMarkerNode(node?: LexicalNode | null): node is LinkMarkerNode { + return node instanceof LinkMarkerNode; +} + +export class LinkUrlNode extends ElementNode { + static getType() { + return "link-url"; + } + static clone(node: LinkUrlNode) { + return new LinkUrlNode(node.__key); + } + isInline(): boolean { + return true; + } + createDOM(): HTMLElement { + const element = document.createElement("span"); + element.classList.add("link-url"); + return element; + } + updateDOM(): boolean { + return false; + } + exportJSON(): SerializedElementNode { + return { + ...super.exportJSON(), + type: "link-url", + }; + } + static importJSON() { + return $createLinkUrlNode(); + } +} + +export function $createLinkUrlNode() { + return new LinkUrlNode(); +} + +export function $isLinkUrlNode(node?: LexicalNode | null): node is LinkUrlNode { + return node instanceof LinkUrlNode; +} + +type SerializedLinkIconNode = Spread; + +export class LinkIconNode extends DecoratorNode { + __url: string; + + getUrl() { + const self = this.getLatest(); + return self.__url; + } + + static getType() { + return "link-icon"; + } + static clone(node: LinkIconNode) { + return new LinkIconNode(node.__url, node.__key); + } + constructor(url: string, key?: string) { + super(key); + this.__url = url; + } + createDOM(): HTMLElement { + return document.createElement("span"); + } + updateDOM(): boolean { + return false; + } + decorate(): ReactNode { + return (); + } + exportJSON(): SerializedLinkIconNode { + return { + ...super.exportJSON(), + type: "link-icon", + url: this.__url, + } + } + static importJSON(serializedNode: SerializedLinkIconNode) { + return $createLinkIconNode(serializedNode.url); + } +} + +export function $createLinkIconNode(url: string) { + return new LinkIconNode(url); +} + +export function $isLinkIconNode(node?: LexicalNode | null): node is LinkIconNode { + return node instanceof LinkIconNode; +} + diff --git a/src/editor/nodes/task_node.tsx b/src/editor/nodes/task_node.tsx new file mode 100644 index 0000000..e5d141d --- /dev/null +++ b/src/editor/nodes/task_node.tsx @@ -0,0 +1,159 @@ +import { $createParagraphNode, DecoratorNode, ElementNode, type LexicalNode, ParagraphNode, type RangeSelection, type SerializedElementNode, type SerializedLexicalNode, type Spread } from "lexical"; +import { FocusableNode } from "./focusable_node"; +import type { ReactNode } from "react"; +import { type TaskType, taskTypes } from "../plugins/task_plugin"; +import { TaskIcon } from "~/components/editor/task_icon"; +import type { TaskLabel } from "../serialized_editor_content"; + +type SerializedTaskNode = Spread; + +export class TaskNode extends FocusableNode { + __taskType: TaskType; + + getTaskType() { + const self = this.getLatest(); + return self.__taskType; + } + + static getType() { + return "task"; + } + static clone(node: TaskNode) { + return new TaskNode(node.__taskType, node.__key, node.__hasFocus); + } + + constructor(taskType: TaskType, key?: string, hasFocus?: boolean) { + super(key, hasFocus); + this.__taskType = taskType; + } + + createDOM(): HTMLElement { + const element = super.createDOM(); + element.classList.add("task"); + element.setAttribute("data-task-type", this.__taskType.label); + return element; + } + updateDOM(old: TaskNode, dom: HTMLElement): boolean { + return super.updateDOM(old, dom); + } + insertNewAfter(_selection: RangeSelection, restoreSelection?: boolean): ParagraphNode { + const newElement = $createParagraphNode(); + this.insertAfter(newElement, restoreSelection); + return newElement; + } + exportJSON(): SerializedTaskNode { + return { + ...super.exportJSON(), + type: "task", + taskType: this.__taskType.label, + }; + } + static importJSON(serializedTaskNode: SerializedTaskNode) { + const taskType = taskTypes.find(it => it.label == serializedTaskNode.taskType); + // FIXME: Deal with task type not existing gracefully. Uncreate the node? + return $createTaskNode(taskType!); + } +} + +export function $createTaskNode(taskType: TaskType) { + return new TaskNode(taskType); +} + +export function $isTaskNode(node?: LexicalNode | null): node is TaskNode { + return node instanceof TaskNode; +} + +export class TaskMarkerNode extends ElementNode { + static getType() { + return "task-marker"; + } + static clone(node: TaskMarkerNode) { + return new TaskMarkerNode(node.__key); + } + isInline(): boolean { + return true; + } + createDOM(): HTMLElement { + const element = document.createElement("span"); + element.classList.add("task-marker"); + return element; + } + updateDOM(): boolean { + return false; + } + exportJSON() { + return { + ...super.exportJSON(), + type: "task-marker", + }; + } + static importJSON() { + return $createTaskMarkerNode(); + } +} + +export function $createTaskMarkerNode() { + return new TaskMarkerNode(); +} + +export function $isTaskMarkerNode(node?: LexicalNode | null): node is TaskMarkerNode { + return node instanceof TaskMarkerNode; +} + + +type SerializedTaskIconNode = Spread; + +export class TaskIconNode extends DecoratorNode { + __taskType: TaskType; + + getTaskType() { + const self = this.getLatest(); + return self.__taskType; + } + + static getType() { + return "task-icon"; + } + static clone(node: TaskIconNode) { + return new TaskIconNode(node.__taskType, node.__key); + } + + constructor(taskType: TaskType, key?: string) { + super(key); + this.__taskType = taskType; + } + + createDOM(): HTMLElement { + return document.createElement("span"); + } + updateDOM(): boolean { + return false; + } + decorate(): ReactNode { + return (); + } + exportJSON(): SerializedTaskIconNode { + return { + ...super.exportJSON(), + type: "task-icon", + taskType: this.__taskType.label, + }; + } + static importJSON(serializedTaskIconNode: SerializedTaskIconNode) { + const taskType = taskTypes.find(it => it.label == serializedTaskIconNode.taskType); + // FIXME: Deal with task type not existing gracefully. Uncreate the node? + return $createTaskIconNode(taskType!); + } +} + +export function $createTaskIconNode(taskType: TaskType) { + return new TaskIconNode(taskType); +} + +export function $isTaskIconNode(node?: LexicalNode | null): node is TaskIconNode { + return node instanceof TaskIconNode; +} diff --git a/src/editor/nodes/term_node.tsx b/src/editor/nodes/term_node.tsx new file mode 100644 index 0000000..ecff609 --- /dev/null +++ b/src/editor/nodes/term_node.tsx @@ -0,0 +1,142 @@ +import { DecoratorNode, ElementNode, type LexicalNode, type SerializedElementNode, type SerializedLexicalNode, type Spread } from "lexical"; +import { FocusableNode } from "./focusable_node"; +import type { ReactNode } from "react"; +import { TermIcon } from "~/components/editor/term_icon"; + +type SerializedTermNode = Spread; + +export class TermNode extends FocusableNode { + __term: string; + + getTerm() { + const self = this.getLatest(); + return self.__term; + } + static getType() { + return "term"; + } + static clone(node: TermNode) { + return new TermNode(node.__term, node.__key, node.__hasFocus); + } + constructor(term: string, key?: string, hasFocus?: boolean) { + super(key, hasFocus); + this.__term = term; + } + isInline() { + return true; + } + createDOM() { + const element = super.createDOM(); + element.classList.add("term"); + return element; + } + updateDOM(old: TermNode, dom: HTMLElement) { + return super.updateDOM(old, dom); + } + exportJSON(): SerializedTermNode { + return { + ...super.exportJSON(), + type: "term", + term: this.__term, + }; + } + static importJSON(serializedNode: SerializedTermNode) { + return $createTermNode(serializedNode.term); + } +} + +export function $createTermNode(term: string) { + return new TermNode(term); +} +export function $isTermNode(node?: LexicalNode | null): node is TermNode { + return node instanceof TermNode; +} + +export class TermMarkerNode extends ElementNode { + static getType() { + return "term-marker"; + } + static clone(node: TermMarkerNode) { + return new TermMarkerNode(node.__key); + } + isInline() { + return true; + } + createDOM() { + const element = document.createElement("span"); + element.classList.add("term-marker"); + return element; + } + updateDOM() { + return false; + } + exportJSON() { + return { + ...super.exportJSON(), + type: "term-marker", + }; + } + static importJSON() { + return $createTermMarkerNode(); + } +} + +export function $createTermMarkerNode() { + return new TermMarkerNode(); +} +export function $isTermMarkerNode(node?: LexicalNode | null): node is TermMarkerNode { + return node instanceof TermMarkerNode; +} + +type SerializedTermIconNode = Spread; + +export class TermIconNode extends DecoratorNode { + __term: string; + + getTerm() { + const self = this.getLatest(); + return self.__term; + } + + static getType() { + return "term-icon"; + } + static clone(node: TermIconNode) { + return new TermIconNode(node.__term, node.__key); + } + constructor(term: string, key?: string) { + super(key); + this.__term = term; + } + createDOM() { + return document.createElement("span"); + } + updateDOM() { + return false; + } + decorate(): ReactNode { + return (); + } + exportJSON(): SerializedTermIconNode { + return { + ...super.exportJSON(), + type: "term-icon", + term: this.__term, + }; + } + static importJSON(serializedNode: SerializedTermIconNode) { + return $createTermIconNode(serializedNode.term); + } +} + +export function $createTermIconNode(term: string) { + return new TermIconNode(term); +} +export function $isTermIconNode(node?: LexicalNode | null): node is TermIconNode { + return node instanceof TermIconNode; +} + diff --git a/src/editor/plugins/formatted_text_plugin.tsx b/src/editor/plugins/formatted_text_plugin.tsx new file mode 100644 index 0000000..ff24e52 --- /dev/null +++ b/src/editor/plugins/formatted_text_plugin.tsx @@ -0,0 +1,102 @@ +import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext"; +import { mergeRegister } from "@lexical/utils"; +import { type LexicalEditor, SELECTION_CHANGE_COMMAND, TextNode } from "lexical"; +import { useEffect } from "react"; +import { $createFormattedTextMarkerNode, $createFormattedTextNode, $isFormattedTextMarkerNode, $isFormattedTextNode, FormattedTextMarkerNode, FormattedTextNode } from "../nodes/formatted_text"; +import { type TextFormat, textFormats } from "../serialized_editor_content"; + +export function FormattedTextPlugin() { + const [editor] = useLexicalComposerContext(); + + useEffect(() => mergeRegister( + // Create formatted text + ...Object.keys(textFormats).map((format) => + editor.registerNodeTransform(TextNode, createFormattedTextNode(format as TextFormat, editor)) + ), + // Remove formatted text marker nodes when not inside formatted text node + editor.registerNodeTransform(FormattedTextMarkerNode, (node) => { + const parent = node.getParent(); + if (!parent) return; + + if ($isFormattedTextNode(parent)) { + const format = parent.getStyle(); + const markerChars = textFormats[format]; + if (node.getTextContent() === markerChars) return; + } + + node.getChildren().reverse().forEach((child) => node.insertAfter(child)); + node.remove(); + }), + // Remove formatted text nodes without matching markers + editor.registerNodeTransform(FormattedTextNode, (node) => { + const format = node.getStyle(); + const markerChars = textFormats[format]; + + const firstMarker = node.getFirstChild(); + const lastMarker = node.getLastChild(); + if ( + !$isFormattedTextMarkerNode(firstMarker) || + !$isFormattedTextMarkerNode(lastMarker) + ) { + node.getChildren().reverse().forEach((child) => node.insertAfter(child)); + node.remove(); + return; + } + const firstMarkerContent = firstMarker.getTextContent(); + const lastMarkerContent = lastMarker.getTextContent(); + + if (firstMarkerContent !== markerChars || lastMarkerContent !== markerChars) { + node.getChildren().reverse().forEach((child) => node.insertAfter(child)); + node.remove(); + return; + } + }), + // Remove formatted text nodes without content + editor.registerNodeTransform(FormattedTextNode, (node) => { + const format = node.getStyle(); + const formattedChars = textFormats[format]; + + const content = node.getTextContent(); + if (content !== formattedChars + formattedChars) return; + + node.getChildren().reverse().forEach((child) => node.insertAfter(child)); + node.remove(); + }), + )); + + return null; +} + +const createFormattedTextNode = (format: TextFormat, editor: LexicalEditor) => (node: TextNode) => { + const markerChars = textFormats[format]; + const markerCharsLen = markerChars.length; + + const content = node.getTextContent(); + + // TODO: search in all text nodes within the parent block node + const start = content.indexOf(markerChars); + if (start === -1) return; + const end = content.indexOf(markerChars, start + markerCharsLen); + if (end === -1) return; + if (start === end - markerCharsLen) return; + + const startIndex = start > 0 ? 1 : 0; + const contentIndex = startIndex + 1; + const endIndex = contentIndex + 1; + + const textNodes = node.splitText(start, start + markerCharsLen, end, end + markerCharsLen); + + const formattedTextNode = $createFormattedTextNode(format); + textNodes[startIndex]!.insertBefore(formattedTextNode); + + + const startMarker = $createFormattedTextMarkerNode(); + startMarker.append(textNodes[startIndex]!); + + const endMarker = $createFormattedTextMarkerNode(); + endMarker.append(textNodes[endIndex]!); + + formattedTextNode.append(startMarker, textNodes[contentIndex]!, endMarker); + + editor.dispatchCommand(SELECTION_CHANGE_COMMAND, undefined); +} diff --git a/src/editor/plugins/header_plugin.tsx b/src/editor/plugins/header_plugin.tsx new file mode 100644 index 0000000..d4afdc4 --- /dev/null +++ b/src/editor/plugins/header_plugin.tsx @@ -0,0 +1,78 @@ +import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext"; +import { $createParagraphNode, $isParagraphNode, $isTextNode, SELECTION_CHANGE_COMMAND, TextNode } from "lexical"; +import { useEffect } from "react"; +import { $createHeaderMarkerNode, $createHeaderNode, $isHeaderMarkerNode, $isHeaderNode, HeaderMarkerNode, HeaderNode } from "../nodes/header_node"; +import { mergeRegister } from "@lexical/utils"; + +const HEADER_REGEX = /^#+ /; + +export function HeaderPlugin() { + const [editor] = useLexicalComposerContext(); + + useEffect(() => mergeRegister( + // Create a header node if the text node matches the HEADER_REGEX + editor.registerNodeTransform(TextNode, (textNode) => { + const prevNode = textNode.getPreviousSibling(); + if (prevNode) return; + const paragraphNode = textNode.getParent(); + if (!$isParagraphNode(paragraphNode)) return; + + const content = textNode.getTextContent(); + const regexMatch = content.match(HEADER_REGEX); + if (!regexMatch) return; + + const children = paragraphNode.getChildren(); + + const firstTextNode = children[0]; + if (!$isTextNode(firstTextNode)) return; + + const markerLength = regexMatch[0].length; + const textNodes = firstTextNode.splitText(markerLength); + + const headerMarkerContent = textNodes[0]; + if (!headerMarkerContent) return; + + const headerNode = $createHeaderNode(markerLength - 1); + const headerMarkerNode = $createHeaderMarkerNode(); + + headerMarkerNode.append(headerMarkerContent); + + headerNode.append(headerMarkerNode); + headerNode.append(...textNodes.slice(1)); + headerNode.append(...children.slice(1)); + + paragraphNode.replace(headerNode, true); + + editor.dispatchCommand(SELECTION_CHANGE_COMMAND, undefined); + }), + // Remove headers if they don't match the HEADER_REGEX + editor.registerNodeTransform(HeaderNode, (headerNode) => { + const content = headerNode.getTextContent(); + if (content.match(HEADER_REGEX)) return; + headerNode.replace($createParagraphNode(), true); + }), + // Remove header markers if they don't match the HEADER_REGEX + editor.registerNodeTransform(HeaderMarkerNode, (node) => { + const headerNode = node.getParent(); + const content = node.getTextContent(); + if ($isHeaderNode(headerNode) && + content.match(HEADER_REGEX) && + content.length - 1 == headerNode.getLevel()) { + return; + } + + node.getChildren().reverse().forEach(child => node.insertAfter(child)); + node.remove(); + }), + // Remove header nodes without a header marker + editor.registerNodeTransform(HeaderNode, (node) => { + const children = node.getChildren(); + const headerMarker = children[0]; + if ($isHeaderMarkerNode(headerMarker)) return; + + node.replace($createParagraphNode(), true); + }), + )); + + return null; +} diff --git a/src/editor/plugins/link_plugin.tsx b/src/editor/plugins/link_plugin.tsx new file mode 100644 index 0000000..91df579 --- /dev/null +++ b/src/editor/plugins/link_plugin.tsx @@ -0,0 +1,136 @@ +import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext"; +import { mergeRegister } from "@lexical/utils"; +import { SELECTION_CHANGE_COMMAND, TextNode } from "lexical"; +import { useEffect } from "react"; +import { $createLinkIconNode, $createLinkMarkerNode, $createLinkNode, $createLinkUrlNode, $isLinkIconNode, $isLinkMarkerNode, $isLinkNode, $isLinkUrlNode, LinkIconNode, LinkMarkerNode, LinkNode, LinkUrlNode } from "../nodes/link_node"; + +const LINK_REGEX = /\[(.+)\]\((.*)\)/; + +export function LinkPlugin() { + const [editor] = useLexicalComposerContext(); + + useEffect(() => mergeRegister( + // Create link node + editor.registerNodeTransform(TextNode, (node) => { + const content = node.getTextContent(); + const matches = content.match(LINK_REGEX); + if (!matches) return; + + const labelLength = matches[1]!.length; + const urlLength = matches[2]!.length; + + // start + // | labelStart + // | | labelEnd + // | | | middle + // | | | | urlStart + // | | | | | urlEnd + // | | | | | | end Index of the segment + // | | | | | | | in the textNodes array + // 0|1|2 |3|4|5 |6|7 <--| + // |[|label|]|(|url|)| + const start = content.indexOf(matches[0]); + const labelStart = start + 1; + const labelEnd = labelStart + labelLength; + const middle = labelEnd + 1; + const urlStart = middle + 1; + const urlEnd = urlStart + urlLength; + const end = urlEnd + 1; + + const textNodes = node.splitText(start, labelStart, labelEnd, middle, urlStart, urlEnd, end); + const labelStartIndex = start > 0 ? 1 : 0; + const labelIndex = labelStartIndex + 1; + const labelEndIndex = labelIndex + 1; + const urlStartIndex = labelEndIndex + 1; + const urlIndex = urlStartIndex + 1; + const urlEndIndex = urlIndex + 1; + + const url = matches[2]!; + + const linkNode = $createLinkNode(url); + textNodes[labelStartIndex]!.insertBefore(linkNode); + + const labelStartNode = $createLinkMarkerNode(); + labelStartNode.append(textNodes[labelStartIndex]!); + const labelEndNode = $createLinkMarkerNode(); + labelEndNode.append(textNodes[labelEndIndex]!); + const urlStartNode = $createLinkMarkerNode(); + urlStartNode.append(textNodes[urlStartIndex]!); + const urlNode = $createLinkUrlNode(); + urlNode.append(textNodes[urlIndex]!); + const urlEndNode = $createLinkMarkerNode(); + urlEndNode.append(textNodes[urlEndIndex]!); + const iconNode = $createLinkIconNode(url); + + linkNode.append(labelStartNode, textNodes[labelIndex]!, labelEndNode, urlStartNode, urlNode, urlEndNode, iconNode); + + editor.dispatchCommand(SELECTION_CHANGE_COMMAND, undefined); + }), + // Remove dangling link marker nodes and verify their content + editor.registerNodeTransform(LinkMarkerNode, (node) => { + const linkNode = node.getParent(); + if ($isLinkNode(linkNode)) { + const content = node.getTextContent(); + const index = node.getIndexWithinParent(); + const parentChildCount = linkNode.getChildren().length; + + if (index === 0 && content === "[") return; + if (index === parentChildCount - 5 && content === "]") return; + if (index === parentChildCount - 4 && content === "(") return; + if (index === parentChildCount - 2 && content === ")") return; + } + + node.getChildren().reverse().forEach((child) => node.insertAfter(child)); + node.remove(); + }), + // Remove dangling link url nodes + editor.registerNodeTransform(LinkUrlNode, (node) => { + const linkNode = node.getParent(); + if ($isLinkNode(linkNode)) { + const url = linkNode.getUrl(); + const content = node.getTextContent(); + if (url === content) return; + } + + node.getChildren().reverse().forEach((child) => node.insertAfter(child)); + node.remove(); + }), + // Remove dangling link icon nodes + editor.registerNodeTransform(LinkIconNode, (node) => { + const linkNode = node.getParent(); + if ($isLinkNode(linkNode)) { + const url = linkNode.getUrl(); + const iconUrl = node.getUrl(); + if (url === iconUrl) return; + } + + node.remove(); + }), + // Remove links with missing markers + editor.registerNodeTransform(LinkNode, (node) => { + const children = node.getChildren(); + const startLabel = children.at(0); + const endLabel = children.at(-5); + const startUrl = children.at(-4); + const url = children.at(-3); + const endUrl = children.at(-2); + const icon = children.at(-1); + + if ( + $isLinkMarkerNode(startLabel) && + $isLinkMarkerNode(endLabel) && + $isLinkMarkerNode(startUrl) && + $isLinkUrlNode(url) && + $isLinkMarkerNode(endUrl) && + $isLinkIconNode(icon) + ) { + return; + } + + children.reverse().forEach((child) => node.insertAfter(child)); + node.remove(); + }), + )) + + return null; +} diff --git a/src/editor/plugins/paragraph_plugin.tsx b/src/editor/plugins/paragraph_plugin.tsx new file mode 100644 index 0000000..2d72068 --- /dev/null +++ b/src/editor/plugins/paragraph_plugin.tsx @@ -0,0 +1,48 @@ +import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext"; +import { CAN_USE_BEFORE_INPUT, IS_APPLE_WEBKIT, IS_IOS, IS_SAFARI, mergeRegister } from "@lexical/utils"; +import { $getSelection, $isRangeSelection, COMMAND_PRIORITY_NORMAL, INSERT_PARAGRAPH_COMMAND, KEY_ENTER_COMMAND } from "lexical"; +import { useEffect } from "react"; + +export function ParagraphPlugin() { + const [editor] = useLexicalComposerContext(); + + useEffect(() => mergeRegister( + editor.registerCommand(INSERT_PARAGRAPH_COMMAND, () => { + const selection = $getSelection(); + if (!$isRangeSelection(selection)) { + return false; + } + selection.insertParagraph(); + return true; + }, COMMAND_PRIORITY_NORMAL), + editor.registerCommand( + KEY_ENTER_COMMAND, + (event) => { + const selection = $getSelection(); + if (!$isRangeSelection(selection)) { + return false; + } + if (event !== null) { + // If we have beforeinput, then we can avoid blocking + // the default behavior. This ensures that the iOS can + // intercept that we're actually inserting a paragraph, + // and autocomplete, autocapitalize etc work as intended. + // This can also cause a strange performance issue in + // Safari, where there is a noticeable pause due to + // preventing the key down of enter. + if ( + (IS_IOS || IS_SAFARI || IS_APPLE_WEBKIT) && + CAN_USE_BEFORE_INPUT + ) { + return false; + } + event.preventDefault(); + } + return editor.dispatchCommand(INSERT_PARAGRAPH_COMMAND, undefined); + }, + COMMAND_PRIORITY_NORMAL, + ), + )) + + return null; +} diff --git a/src/editor/plugins/selection_plugin.tsx b/src/editor/plugins/selection_plugin.tsx new file mode 100644 index 0000000..a87aaed --- /dev/null +++ b/src/editor/plugins/selection_plugin.tsx @@ -0,0 +1,49 @@ +import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext"; +import { mergeRegister } from "@lexical/utils"; +import { $getNodeByKey, $getSelection, COMMAND_PRIORITY_EDITOR, type LexicalNode, SELECTION_CHANGE_COMMAND } from "lexical"; +import { useEffect, useState } from "react"; +import { $isFocusableNode } from "../nodes/focusable_node"; + +export function SelectionPlugin() { + const [editor] = useLexicalComposerContext(); + const [previousSelectedNodes, setPreviousSelectedNodes] = useState([]); + + useEffect(() => mergeRegister( + editor.registerCommand(SELECTION_CHANGE_COMMAND, () => { + const selection = $getSelection(); + const nodes = selection?.getNodes() || []; + + previousSelectedNodes + .map((key) => $getNodeByKey(key)) + .forEach(markTreeAsUnfocused); + + nodes.forEach(markTreeAsFocused); + setPreviousSelectedNodes(nodes.map(n => n.getKey())); + + return false; + }, COMMAND_PRIORITY_EDITOR), + )); + + return null; +} + + +function markTreeAsFocused(node: LexicalNode | null) { + if (!node) return; + + if ($isFocusableNode(node)) { + node.setFocus(true); + } + + markTreeAsFocused(node.getParent()); +} + +function markTreeAsUnfocused(node: LexicalNode | null) { + if (!node) return; + + if ($isFocusableNode(node)) { + node.setFocus(false); + } + + markTreeAsUnfocused(node.getParent()); +} diff --git a/src/editor/plugins/task_plugin.tsx b/src/editor/plugins/task_plugin.tsx new file mode 100644 index 0000000..58e823d --- /dev/null +++ b/src/editor/plugins/task_plugin.tsx @@ -0,0 +1,116 @@ +import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext"; +import { mergeRegister } from "@lexical/utils"; +import { $createParagraphNode, $isParagraphNode, $isTextNode, type LexicalEditor, SELECTION_CHANGE_COMMAND, TextNode } from "lexical"; +import { useEffect } from "react"; +import { $createTaskMarkerNode, $createTaskNode, $createTaskIconNode, TaskMarkerNode, $isTaskNode, TaskIconNode, TaskNode, $isTaskIconNode, $isTaskMarkerNode } from "../nodes/task_node"; +import { type Icon, icons } from "~/lib/icon"; +import { type TaskLabel } from "../serialized_editor_content"; + +export type TaskType = { + label: TaskLabel + icon?: Icon +} + +export const taskTypes: TaskType[] = [ + { + label: "TODO", + icon: undefined, + }, + { + label: "DOING", + icon: icons.minus, + }, + { + label: "DONE", + icon: icons.check, + }, + { + label: "IDEA", + icon: icons.lightbulb, + }, + { + label: "DEADLINE", + icon: icons.exclamation, + }, +] + +export function TaskPlugin() { + const [editor] = useLexicalComposerContext(); + + useEffect(() => mergeRegister( + // Create a task node if the text node matches the TASK_REGEX + ...taskTypes.map(({ label }) => + editor.registerNodeTransform(TextNode, createTaskNode(label, editor)) + ), + // Remove task marker nodes not inside a task node or with the wrong content + editor.registerNodeTransform(TaskMarkerNode, (node) => { + const taskNode = node.getParent(); + if ($isTaskNode(taskNode)) { + const taskType = taskNode.getTaskType(); + const content = node.getTextContent(); + if (content === taskType.label) return; + } + + node.getChildren().reverse().forEach(child => node.insertAfter(child)); + node.remove(); + }), + // Remove task icon nodes not inside a task node + editor.registerNodeTransform(TaskIconNode, (node) => { + const taskNode = node.getParent(); + if ($isTaskNode(taskNode)) { + const taskType = taskNode.getTaskType(); + if (node.getTaskType() === taskType) return; + } + + node.remove(); + }), + // Remove task nodes without an icon or marker + editor.registerNodeTransform(TaskNode, (node) => { + const iconNode = node.getChildAtIndex(0); + const markerNode = node.getChildAtIndex(1); + + if ($isTaskIconNode(iconNode) && $isTaskMarkerNode(markerNode)) return; + + node.replace($createParagraphNode(), true); + }), + )); + + return null; +} + +const createTaskNode = (label: TaskLabel, editor: LexicalEditor) => (node: TextNode) => { + const prevNode = node.getPreviousSibling(); + if (prevNode) return; + const paragraphNode = node.getParent(); + if (!$isParagraphNode(paragraphNode)) return; + + const content = node.getTextContent(); + if (!content.startsWith(label + " ")) return; + + const children = paragraphNode.getChildren(); + + const firstTextNode = children[0]; + if (!$isTextNode(firstTextNode)) return; + + const textNodes = firstTextNode.splitText(label.length); + + const todoMarkerContent = textNodes[0]; + if (!todoMarkerContent) return; + + const taskType = taskTypes.find(it => it.label == label); + if (!taskType) return; + + const taskNode = $createTaskNode(taskType); + const taskIconNode = $createTaskIconNode(taskType); + const taskMarkerNode = $createTaskMarkerNode(); + + taskMarkerNode.append(todoMarkerContent); + + taskNode.append(taskIconNode); + taskNode.append(taskMarkerNode); + taskNode.append(...textNodes.slice(1)); + taskNode.append(...children.slice(1)); + + paragraphNode.replace(taskNode, true); + editor.dispatchCommand(SELECTION_CHANGE_COMMAND, undefined); +} diff --git a/src/editor/plugins/term_plugin.tsx b/src/editor/plugins/term_plugin.tsx new file mode 100644 index 0000000..7b75ea8 --- /dev/null +++ b/src/editor/plugins/term_plugin.tsx @@ -0,0 +1,83 @@ +import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext"; +import { mergeRegister } from "@lexical/utils"; +import { SELECTION_CHANGE_COMMAND, TextNode } from "lexical"; +import { useEffect } from "react"; +import { $createTermIconNode, $createTermMarkerNode, $createTermNode, $isTermIconNode, $isTermMarkerNode, $isTermNode, TermIconNode, TermMarkerNode, TermNode } from "../nodes/term_node"; + +const TERM_REGEX = /\[\[(.+)\]\]/; + +export function TermPlugin() { + const [editor] = useLexicalComposerContext(); + + useEffect(() => mergeRegister( + editor.registerNodeTransform(TextNode, (node) => { + const content = node.getTextContent(); + const matches = content.match(TERM_REGEX); + if (!matches) return; + + const term = matches[1]!; + + const start = content.indexOf(matches[0]); + const end = start + matches[0].length; + + const startIndex = start > 0 ? 1 : 0; + const contentIndex = startIndex + 1; + const endIndex = contentIndex + 1; + + const textNodes = node.splitText(start, start + 2, end - 2, end); + + const termNode = $createTermNode(term); + textNodes[startIndex]!.insertBefore(termNode); + + const iconNode = $createTermIconNode(term); + termNode.append(iconNode); + const startMarkerNode = $createTermMarkerNode(); + startMarkerNode.append(textNodes[startIndex]!); + const endMarkerNode = $createTermMarkerNode(); + endMarkerNode.append(textNodes[endIndex]!); + + termNode.append(startMarkerNode, textNodes[contentIndex]!, endMarkerNode); + + editor.dispatchCommand(SELECTION_CHANGE_COMMAND, undefined); + }), + editor.registerNodeTransform(TermMarkerNode, (node) => { + const termNode = node.getParent(); + if ($isTermNode(termNode)) return; + + node.getChildren().reverse().forEach((child) => node.insertAfter(child)); + node.remove(); + }), + editor.registerNodeTransform(TermIconNode, (node) => { + const termNode = node.getParent(); + if ($isTermNode(termNode)) return; + + node.remove(); + }), + editor.registerNodeTransform(TermNode, (node) => { + const children = node.getChildren(); + const content = node.getTextContent(); + const term = node.getTerm(); + + const iconNode = children.at(0); + const startMarkerNode = children.at(1); + const endMarkerNode = children.at(-1); + + if ($isTermIconNode(iconNode) && + $isTermMarkerNode(startMarkerNode) && + $isTermMarkerNode(endMarkerNode) && + iconNode.getTerm() === term && + startMarkerNode.getTextContent() === "[[" && + endMarkerNode.getTextContent() === "]]" && + content === `[[${term}]]` + ) { + return + } + + + node.getChildren().reverse().forEach((child) => node.insertAfter(child)); + node.remove(); + }), + )); + + return null; +} diff --git a/src/editor/serialized_editor_content.ts b/src/editor/serialized_editor_content.ts new file mode 100644 index 0000000..517a0a2 --- /dev/null +++ b/src/editor/serialized_editor_content.ts @@ -0,0 +1,263 @@ +// TODO: This needs to be removed, but some exported types need to be moved first +import { $getRoot, $isLineBreakNode, $isParagraphNode, $isTextNode, type LexicalNode } from "lexical"; +import { $isHeaderNode } from "./nodes/header_node"; +import { $isTaskNode } from "./nodes/task_node"; +import { $isFormattedTextNode } from "./nodes/formatted_text"; +import { $isLinkNode } from "./nodes/link_node"; +import { $isTermNode } from "./nodes/term_node"; + +export type SerializedEditorContent = { + content: SerializedBlockNode[]; +} + +type SerializedTextContainingNode = + SerializedTextNode + | SerializedFormattedTextNode + | SerializedLinebreakNode + | SerializedLinkNode + | SerializedTermNode; +type SerializedBlockNode = + SerializedHeaderNode + | SerializedParagraphNode + | SerializedTaskNode; + +type SerializedTextNode = { + type: "text"; + content: string; +} + +type SerializedLinebreakNode = { + type: "linebreak"; +} + +type SerializedParagraphNode = { + type: "paragraph"; + content: SerializedTextContainingNode[]; +} + +type SerializedHeaderNode = { + type: "header"; + level: number; + content: SerializedTextContainingNode[]; +} + +export type TextFormat = "bold" | "italic" | "underline" | "strikethrough" | "code"; +export const textFormats: Record = { + bold: "**", + italic: "//", + underline: "__", + strikethrough: "~~", + code: "`", +}; +type SerializedFormattedTextNode = { + type: "formatted_text"; + format: TextFormat; + content: SerializedTextContainingNode[]; +} + +type SerializedLinkNode = { + type: "link"; + label: SerializedTextContainingNode[]; + url: string; +} + +type SerializedTermNode = { + type: "term"; + term: string; +} + + +export type TaskLabel = "TODO" | "DOING" | "DONE" | "IDEA" | "DEADLINE"; +type SerializedTaskNode = { + type: "task"; + label: TaskLabel; + content: SerializedTextContainingNode[]; + +} + +export function $serializeEditorContent(): SerializedEditorContent { + return { + content: $getRoot().getChildren().map(serializeBlockNode), + }; +} + +function serializeBlockNode(node: LexicalNode): SerializedBlockNode { + switch (true) { + case $isParagraphNode(node): return { + type: "paragraph", + content: node.getChildren().map(serializeTextContainingNode), + } + case $isHeaderNode(node): return { + type: "header", + level: node.getLevel(), + // Slice(1) to remove the marker node + content: node.getChildren().slice(1).map(serializeTextContainingNode), + } + case $isTaskNode(node): return { + type: "task", + label: node.getTaskType().label, + // Slice(2) to remove the marker nodes + content: node.getChildren().slice(2).map(serializeTextContainingNode), + } + default: throw new Error(`Unknown block node type: ${node.getType()}`); + } +} + +function serializeTextContainingNode(node: LexicalNode): SerializedTextContainingNode { + switch (true) { + case $isTextNode(node): return { + type: "text", + content: node.getTextContent(), + } + case $isFormattedTextNode(node): return { + type: "formatted_text", + format: node.getStyle(), + content: node.getChildren().slice(1, -1).map(serializeTextContainingNode), + } + case $isLineBreakNode(node): return { + type: "linebreak", + } + case $isLinkNode(node): return { + type: "link", + label: node.getChildren().slice(1, -5).map(serializeTextContainingNode), + url: node.getUrl(), + + } + case $isTermNode(node): return { + type: "term", + term: node.getTerm(), + } + default: throw new Error(`Unknown text containing node type: ${node.getType()}`); + } +} + +export function deserializeEditorContent(serialized: SerializedEditorContent) { + return { + root: { + type: "root", + children: serialized.content.map(deserializeBlockNode), + } + }; +} + +function deserializeBlockNode(serialized: SerializedBlockNode) { + switch (serialized.type) { + case "header": return { + type: "header", + level: serialized.level, + children: [ + { + type: "header-marker", + children: [createTextNode(`${"#".repeat(serialized.level)} `)] + }, + ...serialized.content.map(deserializeTextContainingNode) + ], + } + case "paragraph": return { + type: "paragraph", + children: [ + ...serialized.content.map(deserializeTextContainingNode) + ], + } + case "task": return { + type: "task", + taskType: serialized.label, + children: [ + { + type: "task-icon", + taskType: serialized.label, + }, + { + type: "task-marker", + children: [createTextNode(`${serialized.label} `)] + }, + ...serialized.content.map(deserializeTextContainingNode) + ], + } + } +} + +function deserializeTextContainingNode(serialized: SerializedTextContainingNode): any { + switch (serialized.type) { + case "text": return createTextNode(serialized.content); + case "formatted_text": return { + type: "formatted-text", + style: serialized.format, + children: [ + { + type: "formatted-text-marker", + children: [createTextNode(textFormats[serialized.format])] + }, + ...serialized.content.map(deserializeTextContainingNode), + { + type: "formatted-text-marker", + children: [createTextNode(textFormats[serialized.format])] + }, + ], + } + case "link": return { + type: "link", + children: [ + { + type: "link-marker", + children: [createTextNode("[")], + }, + ...serialized.label.map(deserializeTextContainingNode), + { + type: "link-marker", + children: [createTextNode("]")], + }, + { + type: "link-marker", + children: [createTextNode("(")], + }, + { + type: "link-url", + children: [createTextNode(serialized.url)], + }, + { + type: "link-marker", + children: [createTextNode(")")], + }, + { + type: "link-icon", + url: serialized.url, + } + ], + }; + case "term": return { + type: "term", + term: serialized.term, + children: [ + { + type: "term-icon", + term: serialized.term, + }, + { + type: "term-marker", + children: [createTextNode("[[")] + }, + createTextNode(serialized.term), + { + type: "term-marker", + children: [createTextNode("]]")] + }, + + ], + } + case "linebreak": return { type: "linebreak" }; + } +} + +function createTextNode(content: string) { + return { + type: "text", + text: content, + + version: 1, + detail: 0, + format: 0, + mode: "normal", + style: "", + } +} diff --git a/src/global.d.ts b/src/global.d.ts new file mode 100644 index 0000000..d344e68 --- /dev/null +++ b/src/global.d.ts @@ -0,0 +1,8 @@ +import 'react'; + +declare module 'react' { + // Allow CSS variables inside inline styles + interface CSSProperties { + [key: `--${string}`]: string | number + } +} diff --git a/src/hooks/use-metadata.tsx b/src/hooks/use-metadata.tsx new file mode 100644 index 0000000..fb35ae8 --- /dev/null +++ b/src/hooks/use-metadata.tsx @@ -0,0 +1,65 @@ +import React, { useContext } from "react"; +import * as Y from "yjs"; +import { useYDoc } from "~/hooks/use-ydoc"; +import type { CollectionId, CollectionMetadata, CollectionsMetadata, NoteId, NoteMetadata, NotesMetadata } from "~/lib/metadata"; +import { useObserve } from "~/hooks/use-observe"; + +const MetadataContext = React.createContext(null); + +export function MetadataProvider(props: { children: React.ReactNode }) { + const { ydoc } = useYDoc("metadata"); + + return ( + + {props.children} + + ); +} + +export function useCollections(): CollectionsMetadata { + const doc = useContext(MetadataContext) + + if (!doc) { + throw new Error("useCollections must be used within a MetadataProvider") + } + + const collections = doc.getArray("collections") + useObserve(collections, "deep") + + return collections as any as CollectionsMetadata +} + +export function useCollection(id: CollectionId | undefined): CollectionMetadata | undefined { + const collections = useCollections() + const collection = collections.toArray().find(it => it.get("id") == id) + + return collection +} + + +export function useNotesMetadata(): NotesMetadata { + const doc = useContext(MetadataContext) + + if (!doc) { + throw new Error("useNotes must be used within a MetadataProvider") + } + + const notes = doc.getArray("notes") + useObserve(notes, "deep") + + return notes as any as NotesMetadata +} + +export function useCollectionNotesMetadata(collectionId: CollectionId | ""): NoteMetadata[] { + const notes = useNotesMetadata() + + return notes.toArray().filter(it => it.get("collectionId") == collectionId) + +} + +export function useNoteMetadata(id: NoteId): NoteMetadata | undefined { + const notes = useNotesMetadata() + const note = notes.toArray().find(it => it.get("id") == id) + + return note +} diff --git a/src/hooks/use-note.tsx b/src/hooks/use-note.tsx new file mode 100644 index 0000000..4df18af --- /dev/null +++ b/src/hooks/use-note.tsx @@ -0,0 +1,81 @@ +import React from "react"; +import * as Y from "yjs"; +import type { NoteId } from "~/lib/metadata"; +import { useYDoc } from "./use-ydoc"; +import type { WebsocketProvider } from "y-websocket"; +import type { IndexeddbPersistence } from "y-indexeddb"; + +const NoteContext = React.createContext<{ + doc: Y.Doc, + providers: { + indexeddb: IndexeddbPersistence, + websocket: WebsocketProvider, + } +} | null>(null); + +export function NoteProvider(props: { children: React.ReactNode, id: NoteId }) { + const { ydoc, indexeddbProvider, websocketProvider } = useYDoc(`note-${props.id}`); + + // If we don't have the provider yet, delay rendering until we do. + if (!indexeddbProvider || !websocketProvider) { + return null; + } + + return ( + + {props.children} + + ); +} + +export function useNoteDoc() { + const ctx = React.useContext(NoteContext); + if (!ctx) { + throw new Error("useNoteDoc must be used within a NoteProvider") + } + return ctx.doc; +} + +export function useNoteMap(name: string) { + const ctx = React.useContext(NoteContext); + if (!ctx) { + throw new Error("useNoteMap must be used within a NoteProvider") + } + const map = ctx.doc.getMap(name); + return map; +} + +export function useNoteArray(name: string) { + const ctx = React.useContext(NoteContext); + if (!ctx) { + throw new Error("useNoteArray must be used within a NoteProvider") + } + const array = ctx.doc.getArray(name); + return array; +} + +export function useNoteProviders() { + const ctx = React.useContext(NoteContext); + + if (!ctx) { + throw new Error("useNoteProviders must be used within a NoteProvider") + } + + return ctx.providers; +} + +export function useNoteAwareness() { + const ctx = React.useContext(NoteContext); + if (!ctx) { + throw new Error("useNoteAwareness must be used within a NoteProvider") + } + + const awareness = ctx.providers.websocket.awareness; + return awareness; +} diff --git a/src/hooks/use-observe.ts b/src/hooks/use-observe.ts new file mode 100644 index 0000000..35c6b41 --- /dev/null +++ b/src/hooks/use-observe.ts @@ -0,0 +1,22 @@ +import { useEffect } from "react"; +import * as Y from "yjs" +import { useRedraw } from "~/hooks/use-redraw" + +export function useObserve(object: Y.AbstractType, kind: "deep" | "normal" = "normal") { + const redraw = useRedraw(); + + useEffect(() => { + if (kind === "deep") { + object.observeDeep(redraw); + } else if (kind === "normal") { + object.observe(redraw); + } + return () => { + if (kind === "deep") { + object.unobserveDeep(redraw); + } else if (kind === "normal") { + object.unobserve(redraw); + } + } + }, []); +} diff --git a/src/hooks/use-redraw.ts b/src/hooks/use-redraw.ts new file mode 100644 index 0000000..043511b --- /dev/null +++ b/src/hooks/use-redraw.ts @@ -0,0 +1,7 @@ +import { useState } from "react"; + +export function useRedraw() { + const [, setTick] = useState(0); + + return () => setTick((tick) => tick + 1); +} diff --git a/src/hooks/use-theme.tsx b/src/hooks/use-theme.tsx new file mode 100644 index 0000000..685c642 --- /dev/null +++ b/src/hooks/use-theme.tsx @@ -0,0 +1,47 @@ +import React, { useCallback, useContext, useLayoutEffect, useState } from "react"; + +type Theme = "system" | "light" | "dark"; + +const ThemeContex = React.createContext<{ theme: Theme, effectiveTheme: "light" | "dark", setTheme: (theme: Theme) => void }>({ + theme: "system", + effectiveTheme: "light", + setTheme: () => { } +}); + +export function ThemeProvider(props: { children: React.ReactNode }) { + const [localTheme, setLocalTheme] = useState(() => localStorage.getItem("theme") as Theme || "system"); + const systemTheme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"; + + const effectiveTheme = localTheme === "system" ? systemTheme : localTheme; + + useLayoutEffect(() => { + document.documentElement.setAttribute("data-theme", effectiveTheme) + }, [effectiveTheme]) + + const setTheme = useCallback((newTheme: Theme) => { + setLocalTheme(newTheme); + localStorage.setItem("theme", newTheme); + }, []); + + return ( + + {props.children} + + ); +} + +export function useTheme() { + const { theme, setTheme } = useContext(ThemeContex); + + return [theme, setTheme] as const; +} + +export function useEffectiveTheme() { + const { effectiveTheme } = useContext(ThemeContex); + + return effectiveTheme; +} diff --git a/src/hooks/use-ydoc.ts b/src/hooks/use-ydoc.ts new file mode 100644 index 0000000..6beea38 --- /dev/null +++ b/src/hooks/use-ydoc.ts @@ -0,0 +1,33 @@ +import { useEffect, useState } from 'react'; +import * as Y from 'yjs'; +import { IndexeddbPersistence } from 'y-indexeddb' +import { WebsocketProvider } from 'y-websocket'; + +export function useYDoc(name: string) { + const [ydoc] = useState(() => new Y.Doc()); + const [indexeddbProvider, setIndexeddbProvider] = useState(null); + const [websocketProvider, setWebsocketProvider] = useState(null); + + useEffect(() => { + if (!ydoc) { + return + } + + const indexeddbProvider = new IndexeddbPersistence(name, ydoc); + setIndexeddbProvider(indexeddbProvider); + + const websocketProvider = new WebsocketProvider(`ws://localhost:1234`, name, ydoc, { + // TODO: For now we don't connect to a server yet, this obviously needs to change. + connect: false, + }); + setWebsocketProvider(websocketProvider); + + return () => { + indexeddbProvider.destroy(); + websocketProvider.destroy(); + ydoc.destroy(); + }; + }, [ydoc]); + + return { ydoc, indexeddbProvider, websocketProvider }; +} diff --git a/src/lib/color.ts b/src/lib/color.ts new file mode 100644 index 0000000..f61e21a --- /dev/null +++ b/src/lib/color.ts @@ -0,0 +1,26 @@ +export type ColorName = keyof typeof colors; +export type Color = typeof colors[ColorName]; + +// Tailwind colors +// Base: 400 +// Hover: 300 +export const colors = { + red: { label: "Red", base: "#f87171", hover: "#fca5a5" }, + orange: { label: "Orange", base: "#fb923c", hover: "#fdba74" }, + amber: { label: "Amber", base: "#fbbf24", hover: "#fcd34d" }, + yellow: { label: "Yellow", base: "#facc15", hover: "#fde047" }, + lime: { label: "Lime", base: "#a3e635", hover: "#bef264" }, + green: { label: "Green", base: "#4ade80", hover: "#86efac" }, + emerald: { label: "Emerald", base: "#34d399", hover: "#6ee7b7" }, + teal: { label: "Teal", base: "#2dd4bf", hover: "#5eead4" }, + cyan: { label: "Cyan", base: "#22d3ee", hover: "#67e8f9" }, + sky: { label: "Sky", base: "#38bdf8", hover: "#7dd3fc" }, + blue: { label: "Blue", base: "#60a5fa", hover: "#93c5fd" }, + indigo: { label: "Indigo", base: "#818cf8", hover: "#a5b4fc" }, + violet: { label: "Violet", base: "#a78bfa", hover: "#c4b5fd" }, + purple: { label: "Purple", base: "#c084fc", hover: "#d8b4fe" }, + fuchsia: { label: "Fuchsia", base: "#e879f9", hover: "#f0abfc" }, + pink: { label: "Pink", base: "#f472b6", hover: "#f9a8d4" }, + rose: { label: "Rose", base: "#fb7185", hover: "#fda4af" }, + white: { label: "White", base: "#a3a3a3", hover: "#d4d4d4" }, +}; diff --git a/src/lib/icon.ts b/src/lib/icon.ts new file mode 100644 index 0000000..1aa3393 --- /dev/null +++ b/src/lib/icon.ts @@ -0,0 +1,12 @@ +import * as mdi_icons from "@mdi/js"; + +export type IconName = string; //keyof typeof icons; +export type Icon = typeof icons[IconName]; + +export const icons = Object.fromEntries(Object.entries(mdi_icons).map(([key, value]) => ([ + key.replace(/([A-Z])/g, "-$1").replace(/^mdi-/, "").toLowerCase(), + { + path: value, + name: key.replace("mdi", "").replace(/([A-Z])/g, " $1"), + } +]))); diff --git a/src/lib/metadata.ts b/src/lib/metadata.ts new file mode 100644 index 0000000..f1b71fe --- /dev/null +++ b/src/lib/metadata.ts @@ -0,0 +1,99 @@ +import * as Y from "yjs" + +import type { ColorName } from "~/lib/color" +import type { IconName } from "~/lib/icon" +import type { YArray, YMap } from "~/lib/yjs" +import type { PropertyType } from "~/lib/property" + +export type NoteId = string +export type CollectionId = string +export type PropertyId = string + +export type NotesMetadata = YArray + +export type NoteMetadata = YMap<{ + id: NoteId + collectionId: CollectionId | "" + title: string + icon: IconName | "" + primaryColor: ColorName | "" + secondaryColor: ColorName | "" + pinned: boolean + properties: YArray + type: "text" | "canvas" +}> + +export type NoteProperty = YMap<{ + propertyId: PropertyId + value: string +}> + +export type CollectionsMetadata = YArray + +export type CollectionMetadata = YMap<{ + id: CollectionId + name: string + color: ColorName + icon: IconName | "" + properties?: YArray +}> + +export type CollectionProperty = YMap<{ + id: PropertyId + name: string + type: PropertyType + pinned: boolean +}> + +export function createCollection(md: CollectionsMetadata, data: { + name: string, + color: ColorName, + icon: IconName, +}) { + const collection = new Y.Map() as any as CollectionMetadata; + collection.set("id", randomUUID()); + collection.set("name", data.name); + collection.set("color", data.color); + collection.set("icon", data.icon); + collection.set("properties", new Y.Array() as any); + + md.push([collection]) + return collection; +} + +export function deleteCollection(md: CollectionsMetadata, id: CollectionId) { + const index = md.toArray().findIndex(it => it.get("id") == id); + if (index == -1) { + return; + } + + md.delete(index, 1) +} + +export function createNote(md: NotesMetadata, data: { + name: string, + icon: IconName | undefined, + collectionId: CollectionId | undefined, + primaryColor: ColorName | undefined, + secondaryColor: ColorName | undefined, + type: "text" | "canvas" +}) { + const note = new Y.Map() as any as NoteMetadata; + note.set("id", randomUUID()); + note.set("title", data.name); + note.set("icon", data.icon ?? ""); + note.set("collectionId", data.collectionId ?? ""); + note.set("primaryColor", data.primaryColor ?? ""); + note.set("secondaryColor", data.secondaryColor ?? ""); + note.set("pinned", false); + note.set("properties", new Y.Array() as any); + note.set("type", data.type); + + md.push([note]) + + return note; +} + +export function randomUUID() { + return crypto.randomUUID() +} diff --git a/src/lib/property.ts b/src/lib/property.ts new file mode 100644 index 0000000..397de81 --- /dev/null +++ b/src/lib/property.ts @@ -0,0 +1,42 @@ +import { randomUUID, type CollectionProperty, type NoteProperty, type PropertyId } from "~/lib/metadata"; +import type { YArray } from "~/lib/yjs"; +import * as Y from "yjs"; + +export const propertyTypes = ["shortText"] as const; + +export type PropertyType = typeof propertyTypes[number]; + +export const properties: { [P in PropertyType]: string } = { + "shortText": "Short text", +}; + +export function createCollectionProperty(md: YArray) { + const property = new Y.Map() as any as CollectionProperty; + property.set("id", randomUUID()); + property.set("name", ""); + property.set("type", "shortText"); + property.set("pinned", false); + + md.push([property]); + return property; +} + +export function deleteCollectionProperty(md: YArray, id: PropertyId) { + const index = md.toArray().findIndex((prop) => prop.get("id") === id); + if (index === -1) { + return; + } + + md.delete(index, 1); +} + +export function createNoteProperty(md: YArray, data: { + propertyId: PropertyId; +}) { + const property = new Y.Map() as any as NoteProperty; + property.set("propertyId", data.propertyId); + property.set("value", ""); + + md.push([property]); + return property; +} diff --git a/src/lib/yjs.ts b/src/lib/yjs.ts new file mode 100644 index 0000000..5c19c6b --- /dev/null +++ b/src/lib/yjs.ts @@ -0,0 +1,48 @@ +import * as Y from 'yjs' + +export type YValue = object | boolean | string | number | Uint8Array | YMap | YArray | Y.Text + +type YValueToJSON = + T extends YMap ? YMapToJSON + : T extends YArray ? YArrayToJSON + : T extends Y.Text ? string + : T + +type YMapToJSON = T extends YMap + ? { [KEY in keyof X]: YValueToJSON } + : never; + +type YArrayToJSON = T extends YArray ? YValueToJSON[] : never; + +export type YMap = { + set: (key: X, value: T[X]) => void + get: (key: X) => T[X] + delete: (key: X) => void + has: (key: X) => boolean + clear: () => void + toJSON: () => YMapToJSON> + forEach: (callback: (value: T[X], key: X, map: YMap) => void) => void + entries: () => Iterator<[string, YValue]> + values: () => Iterator + keys: () => Iterator + clone: () => YMap + + size: number +} + +export type YArray = { + insert: (index: number, content: T[]) => void + delete: (index: number, length: number) => void + push: (values: T[]) => void + unshift: (content: T[]) => void + get: (index: number) => T | undefined + slice: (start: number, end?: number) => T[] + toArray: () => T[] + toJSON: () => YArrayToJSON> + forEach: (callback: (value: T, index: number, yarray: YArray) => void) => void + map: (callback: (value: T, index: number, yarray: YArray) => X) => X[] + clone: () => YArray + + length: number +} + diff --git a/src/main.tsx b/src/main.tsx index ba85d7d..b209f38 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -2,28 +2,14 @@ import { StrictMode } from 'react' import ReactDOM from 'react-dom/client' import { RouterProvider, - createRootRoute, - createRoute, createRouter, } from '@tanstack/react-router' import '~/styles.css' import reportWebVitals from '~/reportWebVitals.ts' -import { RootLayout } from '~/layouts/RootLayout' -import { RootPage } from '~/pages/Root' - -const rootRoute = createRootRoute({ - component: RootLayout, -}) - -const indexRoute = createRoute({ - getParentRoute: () => rootRoute, - path: '/', - component: RootPage, -}) - -const routeTree = rootRoute.addChildren([indexRoute]) +// Import the generated route tree +import { routeTree } from "./routeTree.gen"; const router = createRouter({ routeTree, diff --git a/src/routeTree.gen.ts b/src/routeTree.gen.ts new file mode 100644 index 0000000..bd9a7ed --- /dev/null +++ b/src/routeTree.gen.ts @@ -0,0 +1,369 @@ +/* eslint-disable */ + +// @ts-nocheck + +// noinspection JSUnusedGlobalSymbols + +// This file was automatically generated by TanStack Router. +// You should NOT make any changes in this file as it will be overwritten. +// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. + +// Import Routes + +import { Route as rootRoute } from './routes/__root' +import { Route as AppImport } from './routes/app' +import { Route as IndexImport } from './routes/index' +import { Route as AppIndexImport } from './routes/app/index' +import { Route as AppTodoImport } from './routes/app/todo' +import { Route as AppSearchImport } from './routes/app/search' +import { Route as AppInboxImport } from './routes/app/inbox' +import { Route as AppGraphImport } from './routes/app/graph' +import { Route as AppCalendarImport } from './routes/app/calendar' +import { Route as AppTermTermImport } from './routes/app/term.$term' +import { Route as AppNoteIdImport } from './routes/app/note.$id' +import { Route as AppCollectionIdImport } from './routes/app/collection.$id' + +// Create/Update Routes + +const AppRoute = AppImport.update({ + id: '/app', + path: '/app', + getParentRoute: () => rootRoute, +} as any) + +const IndexRoute = IndexImport.update({ + id: '/', + path: '/', + getParentRoute: () => rootRoute, +} as any) + +const AppIndexRoute = AppIndexImport.update({ + id: '/', + path: '/', + getParentRoute: () => AppRoute, +} as any) + +const AppTodoRoute = AppTodoImport.update({ + id: '/todo', + path: '/todo', + getParentRoute: () => AppRoute, +} as any) + +const AppSearchRoute = AppSearchImport.update({ + id: '/search', + path: '/search', + getParentRoute: () => AppRoute, +} as any) + +const AppInboxRoute = AppInboxImport.update({ + id: '/inbox', + path: '/inbox', + getParentRoute: () => AppRoute, +} as any) + +const AppGraphRoute = AppGraphImport.update({ + id: '/graph', + path: '/graph', + getParentRoute: () => AppRoute, +} as any) + +const AppCalendarRoute = AppCalendarImport.update({ + id: '/calendar', + path: '/calendar', + getParentRoute: () => AppRoute, +} as any) + +const AppTermTermRoute = AppTermTermImport.update({ + id: '/term/$term', + path: '/term/$term', + getParentRoute: () => AppRoute, +} as any) + +const AppNoteIdRoute = AppNoteIdImport.update({ + id: '/note/$id', + path: '/note/$id', + getParentRoute: () => AppRoute, +} as any) + +const AppCollectionIdRoute = AppCollectionIdImport.update({ + id: '/collection/$id', + path: '/collection/$id', + getParentRoute: () => AppRoute, +} as any) + +// Populate the FileRoutesByPath interface + +declare module '@tanstack/react-router' { + interface FileRoutesByPath { + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexImport + parentRoute: typeof rootRoute + } + '/app': { + id: '/app' + path: '/app' + fullPath: '/app' + preLoaderRoute: typeof AppImport + parentRoute: typeof rootRoute + } + '/app/calendar': { + id: '/app/calendar' + path: '/calendar' + fullPath: '/app/calendar' + preLoaderRoute: typeof AppCalendarImport + parentRoute: typeof AppImport + } + '/app/graph': { + id: '/app/graph' + path: '/graph' + fullPath: '/app/graph' + preLoaderRoute: typeof AppGraphImport + parentRoute: typeof AppImport + } + '/app/inbox': { + id: '/app/inbox' + path: '/inbox' + fullPath: '/app/inbox' + preLoaderRoute: typeof AppInboxImport + parentRoute: typeof AppImport + } + '/app/search': { + id: '/app/search' + path: '/search' + fullPath: '/app/search' + preLoaderRoute: typeof AppSearchImport + parentRoute: typeof AppImport + } + '/app/todo': { + id: '/app/todo' + path: '/todo' + fullPath: '/app/todo' + preLoaderRoute: typeof AppTodoImport + parentRoute: typeof AppImport + } + '/app/': { + id: '/app/' + path: '/' + fullPath: '/app/' + preLoaderRoute: typeof AppIndexImport + parentRoute: typeof AppImport + } + '/app/collection/$id': { + id: '/app/collection/$id' + path: '/collection/$id' + fullPath: '/app/collection/$id' + preLoaderRoute: typeof AppCollectionIdImport + parentRoute: typeof AppImport + } + '/app/note/$id': { + id: '/app/note/$id' + path: '/note/$id' + fullPath: '/app/note/$id' + preLoaderRoute: typeof AppNoteIdImport + parentRoute: typeof AppImport + } + '/app/term/$term': { + id: '/app/term/$term' + path: '/term/$term' + fullPath: '/app/term/$term' + preLoaderRoute: typeof AppTermTermImport + parentRoute: typeof AppImport + } + } +} + +// Create and export the route tree + +interface AppRouteChildren { + AppCalendarRoute: typeof AppCalendarRoute + AppGraphRoute: typeof AppGraphRoute + AppInboxRoute: typeof AppInboxRoute + AppSearchRoute: typeof AppSearchRoute + AppTodoRoute: typeof AppTodoRoute + AppIndexRoute: typeof AppIndexRoute + AppCollectionIdRoute: typeof AppCollectionIdRoute + AppNoteIdRoute: typeof AppNoteIdRoute + AppTermTermRoute: typeof AppTermTermRoute +} + +const AppRouteChildren: AppRouteChildren = { + AppCalendarRoute: AppCalendarRoute, + AppGraphRoute: AppGraphRoute, + AppInboxRoute: AppInboxRoute, + AppSearchRoute: AppSearchRoute, + AppTodoRoute: AppTodoRoute, + AppIndexRoute: AppIndexRoute, + AppCollectionIdRoute: AppCollectionIdRoute, + AppNoteIdRoute: AppNoteIdRoute, + AppTermTermRoute: AppTermTermRoute, +} + +const AppRouteWithChildren = AppRoute._addFileChildren(AppRouteChildren) + +export interface FileRoutesByFullPath { + '/': typeof IndexRoute + '/app': typeof AppRouteWithChildren + '/app/calendar': typeof AppCalendarRoute + '/app/graph': typeof AppGraphRoute + '/app/inbox': typeof AppInboxRoute + '/app/search': typeof AppSearchRoute + '/app/todo': typeof AppTodoRoute + '/app/': typeof AppIndexRoute + '/app/collection/$id': typeof AppCollectionIdRoute + '/app/note/$id': typeof AppNoteIdRoute + '/app/term/$term': typeof AppTermTermRoute +} + +export interface FileRoutesByTo { + '/': typeof IndexRoute + '/app/calendar': typeof AppCalendarRoute + '/app/graph': typeof AppGraphRoute + '/app/inbox': typeof AppInboxRoute + '/app/search': typeof AppSearchRoute + '/app/todo': typeof AppTodoRoute + '/app': typeof AppIndexRoute + '/app/collection/$id': typeof AppCollectionIdRoute + '/app/note/$id': typeof AppNoteIdRoute + '/app/term/$term': typeof AppTermTermRoute +} + +export interface FileRoutesById { + __root__: typeof rootRoute + '/': typeof IndexRoute + '/app': typeof AppRouteWithChildren + '/app/calendar': typeof AppCalendarRoute + '/app/graph': typeof AppGraphRoute + '/app/inbox': typeof AppInboxRoute + '/app/search': typeof AppSearchRoute + '/app/todo': typeof AppTodoRoute + '/app/': typeof AppIndexRoute + '/app/collection/$id': typeof AppCollectionIdRoute + '/app/note/$id': typeof AppNoteIdRoute + '/app/term/$term': typeof AppTermTermRoute +} + +export interface FileRouteTypes { + fileRoutesByFullPath: FileRoutesByFullPath + fullPaths: + | '/' + | '/app' + | '/app/calendar' + | '/app/graph' + | '/app/inbox' + | '/app/search' + | '/app/todo' + | '/app/' + | '/app/collection/$id' + | '/app/note/$id' + | '/app/term/$term' + fileRoutesByTo: FileRoutesByTo + to: + | '/' + | '/app/calendar' + | '/app/graph' + | '/app/inbox' + | '/app/search' + | '/app/todo' + | '/app' + | '/app/collection/$id' + | '/app/note/$id' + | '/app/term/$term' + id: + | '__root__' + | '/' + | '/app' + | '/app/calendar' + | '/app/graph' + | '/app/inbox' + | '/app/search' + | '/app/todo' + | '/app/' + | '/app/collection/$id' + | '/app/note/$id' + | '/app/term/$term' + fileRoutesById: FileRoutesById +} + +export interface RootRouteChildren { + IndexRoute: typeof IndexRoute + AppRoute: typeof AppRouteWithChildren +} + +const rootRouteChildren: RootRouteChildren = { + IndexRoute: IndexRoute, + AppRoute: AppRouteWithChildren, +} + +export const routeTree = rootRoute + ._addFileChildren(rootRouteChildren) + ._addFileTypes() + +/* ROUTE_MANIFEST_START +{ + "routes": { + "__root__": { + "filePath": "__root.tsx", + "children": [ + "/", + "/app" + ] + }, + "/": { + "filePath": "index.tsx" + }, + "/app": { + "filePath": "app.tsx", + "children": [ + "/app/calendar", + "/app/graph", + "/app/inbox", + "/app/search", + "/app/todo", + "/app/", + "/app/collection/$id", + "/app/note/$id", + "/app/term/$term" + ] + }, + "/app/calendar": { + "filePath": "app/calendar.tsx", + "parent": "/app" + }, + "/app/graph": { + "filePath": "app/graph.tsx", + "parent": "/app" + }, + "/app/inbox": { + "filePath": "app/inbox.tsx", + "parent": "/app" + }, + "/app/search": { + "filePath": "app/search.tsx", + "parent": "/app" + }, + "/app/todo": { + "filePath": "app/todo.tsx", + "parent": "/app" + }, + "/app/": { + "filePath": "app/index.tsx", + "parent": "/app" + }, + "/app/collection/$id": { + "filePath": "app/collection.$id.tsx", + "parent": "/app" + }, + "/app/note/$id": { + "filePath": "app/note.$id.tsx", + "parent": "/app" + }, + "/app/term/$term": { + "filePath": "app/term.$term.tsx", + "parent": "/app" + } + } +} +ROUTE_MANIFEST_END */ diff --git a/src/layouts/RootLayout.tsx b/src/routes/__root.tsx similarity index 58% rename from src/layouts/RootLayout.tsx rename to src/routes/__root.tsx index 0923a86..82d6975 100644 --- a/src/layouts/RootLayout.tsx +++ b/src/routes/__root.tsx @@ -1,9 +1,14 @@ -import { Outlet } from "@tanstack/react-router"; -import { TanStackRouterDevtools } from "@tanstack/react-router-devtools"; +import { createRootRoute, Outlet } from "@tanstack/react-router"; +// import { TanStackRouterDevtools } from "@tanstack/react-router-devtools"; import { useEffect } from "react"; import { getSerwist } from "virtual:serwist"; +import { ThemeProvider } from "~/hooks/use-theme"; -export function RootLayout() { +export const Route = createRootRoute({ + component: RootLayout, +}); + +function RootLayout() { useEffect(() => { const loadSerwist = async () => { if ("serviceWorker" in navigator) { @@ -20,9 +25,9 @@ export function RootLayout() { loadSerwist(); }, []); return ( - <> + - - + {/**/} + ); } diff --git a/src/routes/app.tsx b/src/routes/app.tsx new file mode 100644 index 0000000..8143588 --- /dev/null +++ b/src/routes/app.tsx @@ -0,0 +1,52 @@ +import { createFileRoute, Outlet } from "@tanstack/react-router"; +import { PanelLeft } from "lucide-react"; +import { AppSidebar } from "~/components/app_sidebar"; +import { Button } from "~/components/ui/button"; +import { SidebarInset, SidebarProvider, useSidebar } from "~/components/ui/sidebar"; +import { MetadataProvider } from "~/hooks/use-metadata"; +import { useIsMobile } from "~/hooks/use-mobile"; + +export const Route = createFileRoute("/app")({ + component: AppLayout, +}); + +export function AppLayout() { + return ( + <> + + + +
+ + + + +
+
+
+ + ); +} + +function MobileBar() { + const isMobile = useIsMobile(); + const { toggleSidebar } = useSidebar(); + + if (!isMobile) { + return; + } + + return ( +
+ +
+ ); + +} diff --git a/src/routes/app/calendar.tsx b/src/routes/app/calendar.tsx new file mode 100644 index 0000000..834b5c3 --- /dev/null +++ b/src/routes/app/calendar.tsx @@ -0,0 +1,9 @@ +import { createFileRoute } from '@tanstack/react-router' + +export const Route = createFileRoute('/app/calendar')({ + component: RouteComponent, +}) + +function RouteComponent() { + return
Hello "/app/calendar"!
+} diff --git a/src/routes/app/collection.$id.tsx b/src/routes/app/collection.$id.tsx new file mode 100644 index 0000000..52cd700 --- /dev/null +++ b/src/routes/app/collection.$id.tsx @@ -0,0 +1,20 @@ +import { createFileRoute } from "@tanstack/react-router" +import { CollectionHeader } from "~/components/collection/collection_header"; +import { NotesGrid } from "~/components/note/notes_grid"; +import { useCollectionNotesMetadata } from "~/hooks/use-metadata"; + +export const Route = createFileRoute("/app/collection/$id")({ + component: RouteComponent, +}) + +function RouteComponent() { + const { id } = Route.useParams(); + const notes = useCollectionNotesMetadata(id); + + return ( +
+ + +
+ ); +} diff --git a/src/routes/app/graph.tsx b/src/routes/app/graph.tsx new file mode 100644 index 0000000..7d9e38c --- /dev/null +++ b/src/routes/app/graph.tsx @@ -0,0 +1,9 @@ +import { createFileRoute } from '@tanstack/react-router' + +export const Route = createFileRoute('/app/graph')({ + component: RouteComponent, +}) + +function RouteComponent() { + return
Hello "/app/graph"!
+} diff --git a/src/routes/app/inbox.tsx b/src/routes/app/inbox.tsx new file mode 100644 index 0000000..66329aa --- /dev/null +++ b/src/routes/app/inbox.tsx @@ -0,0 +1,15 @@ +import { createFileRoute } from '@tanstack/react-router' +import { NotesGrid } from '~/components/note/notes_grid'; +import { useCollectionNotesMetadata } from '~/hooks/use-metadata'; + +export const Route = createFileRoute('/app/inbox')({ + component: RouteComponent, +}) + +function RouteComponent() { + const notes = useCollectionNotesMetadata("") + + return ( + + ); +} diff --git a/src/routes/app/index.tsx b/src/routes/app/index.tsx new file mode 100644 index 0000000..d96c07c --- /dev/null +++ b/src/routes/app/index.tsx @@ -0,0 +1,12 @@ +import { createFileRoute } from "@tanstack/react-router" +import { MetadataInspector } from "~/components/yjs/metadata-inspector"; + +export const Route = createFileRoute("/app/")({ + component: RouteComponent, +}); + +function RouteComponent() { + return ( + + ) +} diff --git a/src/routes/app/note.$id.tsx b/src/routes/app/note.$id.tsx new file mode 100644 index 0000000..b2989f6 --- /dev/null +++ b/src/routes/app/note.$id.tsx @@ -0,0 +1,36 @@ +import { createFileRoute } from "@tanstack/react-router" +import NoteCanvas from "~/components/note/note_canvas"; +import { NoteHeader } from "~/components/note/note_header"; +import { Editor } from "~/editor/Editor"; +import { useNoteMetadata } from "~/hooks/use-metadata"; +import { NoteProvider } from "~/hooks/use-note"; + +export const Route = createFileRoute("/app/note/$id")({ + component: RouteComponent, +}) + +function RouteComponent() { + const { id } = Route.useParams(); + return ( + + + + ); +} + +function Content(props: { id: string }) { + const metadata = useNoteMetadata(props.id); + + switch (metadata?.get("type")) { + case "text": return <> + +
+ +
+ ; + case "canvas": return <> + + ; + default: return null; + } +} diff --git a/src/routes/app/search.tsx b/src/routes/app/search.tsx new file mode 100644 index 0000000..3c48e66 --- /dev/null +++ b/src/routes/app/search.tsx @@ -0,0 +1,13 @@ +import { createFileRoute } from "@tanstack/react-router" +import { NotesGrid } from "~/components/note/notes_grid"; +import { useNotesMetadata } from "~/hooks/use-metadata"; + +export const Route = createFileRoute("/app/search")({ + component: RouteComponent, +}) + +function RouteComponent() { + const notes = useNotesMetadata().toArray(); + + return +} diff --git a/src/routes/app/term.$term.tsx b/src/routes/app/term.$term.tsx new file mode 100644 index 0000000..f29cd5d --- /dev/null +++ b/src/routes/app/term.$term.tsx @@ -0,0 +1,10 @@ +import { createFileRoute } from '@tanstack/react-router' + +export const Route = createFileRoute('/app/term/$term')({ + component: RouteComponent, +}) + +function RouteComponent() { + const { term } = Route.useParams(); + return
Hello "/app/term/{term}"!
+} diff --git a/src/routes/app/todo.tsx b/src/routes/app/todo.tsx new file mode 100644 index 0000000..1122ac9 --- /dev/null +++ b/src/routes/app/todo.tsx @@ -0,0 +1,9 @@ +import { createFileRoute } from '@tanstack/react-router' + +export const Route = createFileRoute('/app/todo')({ + component: RouteComponent, +}) + +function RouteComponent() { + return
Hello "/app/todo"!
+} diff --git a/src/pages/Root.tsx b/src/routes/index.tsx similarity index 80% rename from src/pages/Root.tsx rename to src/routes/index.tsx index 4eaf616..721a05e 100644 --- a/src/pages/Root.tsx +++ b/src/routes/index.tsx @@ -1,6 +1,11 @@ +import { createFileRoute, Link } from '@tanstack/react-router'; import logo from '~/logo.svg' -export function RootPage() { +export const Route = createFileRoute("/")({ + component: RootPage, +}); + +function RootPage() { return (
@@ -28,6 +33,11 @@ export function RootPage() { > Learn TanStack + + Go to app +
); diff --git a/src/styles.css b/src/styles.css index c1404e3..455cb99 100644 --- a/src/styles.css +++ b/src/styles.css @@ -1,7 +1,7 @@ @import "tailwindcss"; @import "tw-animate-css"; -@custom-variant dark (&:is(.dark *)); +@custom-variant dark (&:is([data-theme="dark"] *)); body { @apply m-0; @@ -52,7 +52,7 @@ code { --sidebar-ring: oklch(0.708 0 0); } -.dark { +[data-theme="dark"] { --background: oklch(0.145 0 0); --foreground: oklch(0.985 0 0); --card: oklch(0.205 0 0); diff --git a/vite.config.js b/vite.config.js index 99b2cef..72ffeb0 100644 --- a/vite.config.js +++ b/vite.config.js @@ -2,12 +2,14 @@ import { defineConfig } from "vite"; import { serwist } from "@serwist/vite"; import viteReact from "@vitejs/plugin-react"; import tailwindcss from "@tailwindcss/vite"; +import { TanStackRouterVite } from "@tanstack/router-plugin/vite"; import path from "path"; // https://vitejs.dev/config/ export default defineConfig({ plugins: [ + TanStackRouterVite(), viteReact(), tailwindcss(), serwist({ @@ -16,6 +18,8 @@ export default defineConfig({ globDirectory: "dist", injectionPoint: "self.__SW_MANIFEST", rollupFormat: "iife", + // Insanely large max size, since the app **HAS** to function fully offline + maximumFileSizeToCacheInBytes: 512 * 1024 * 1024, }), ], server: {