diff --git a/.changeset/add-tldraw-canvas-support.md b/.changeset/add-tldraw-canvas-support.md new file mode 100644 index 000000000..9e3c14e5b --- /dev/null +++ b/.changeset/add-tldraw-canvas-support.md @@ -0,0 +1,5 @@ +--- +"@inkeep/open-knowledge": patch +--- + +Add support for standalone `tldraw` drawing canvases when opening `.tldr`, `.tldraw`, or `.okdraw` files. Canvas states are serialized to JSON and persisted directly in the file's collaborative CRDT state. diff --git a/packages/app/package.json b/packages/app/package.json index 493e4b280..d82f67fb2 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -161,6 +161,7 @@ "tailwind-merge": "^3.5.0", "tailwind-scrollbar": "^4.0.2", "tailwindcss": "^4.2.2", + "tldraw": "^5.2.5", "tw-animate-css": "^1.4.0", "ws": "^8.20.0", "y-codemirror.next": "^0.3.5", diff --git a/packages/app/src/components/EditorActivityPool.tsx b/packages/app/src/components/EditorActivityPool.tsx index fc819e458..d7063bcdb 100644 --- a/packages/app/src/components/EditorActivityPool.tsx +++ b/packages/app/src/components/EditorActivityPool.tsx @@ -34,7 +34,7 @@ * (invalidate + nav), or (c) Activity eviction from the MRU mount list. */ -import { isManagedArtifactDocName, isMermaidDocFile } from '@inkeep/open-knowledge-core'; +import { isManagedArtifactDocName, isMermaidDocFile, isTldrawDocFile } from '@inkeep/open-knowledge-core'; import { t } from '@lingui/core/macro'; import { Loader2, RefreshCw } from 'lucide-react'; import { @@ -81,6 +81,11 @@ const MermaidDocEditor = lazy(async () => ({ default: (await import('./MermaidDocEditor')).MermaidDocEditor, })); +// Standalone tldraw editor for `.tldr` / `.canvas` docs. +const LazyTldrawEditor = lazy(async () => ({ + default: (await import('./TldrawDocEditor')).TldrawDocEditor, +})); + /** * Large-doc threshold in Y.Text characters. Above this, the non-active editor * is defer-mounted on cold load instead of pre-mounting both per @@ -759,6 +764,7 @@ function ActivityEntry({ // Standalone Mermaid docs (`.mmd`/`.mermaid`) render a dedicated diagram+source // editor instead of the markdown dual-editor (they are Y.Text-only, no bridge). const isMermaid = isMermaidDocFile(entry.docName); + const isTldraw = isTldrawDocFile(entry.docName); // Per-Activity portal target for . Stable DOM element // exclusively owned by THIS ActivityEntry — `useState` with a lazy @@ -1009,6 +1015,10 @@ function ActivityEntry({ provider={entry.provider} isSourceMode={isSourceMode} /> + ) : isTldraw ? ( + }> + + ) : ( /* Dual-editor mount with size-gated defer for large docs. Small docs render both (pre-mount-both default — mode swap stays diff --git a/packages/app/src/components/TldrawDocEditor.tsx b/packages/app/src/components/TldrawDocEditor.tsx new file mode 100644 index 000000000..2398147a6 --- /dev/null +++ b/packages/app/src/components/TldrawDocEditor.tsx @@ -0,0 +1,86 @@ +/** + * Editor for a standalone tldraw doc (`.tldr`). + * Syncs the canvas state to the underlying `Y.Text('source')` CRDT as a JSON snapshot. + */ + +import { useLingui } from '@lingui/react/macro'; +import type { HocuspocusProvider } from '@hocuspocus/provider'; +import { useEffect, useState } from 'react'; +import { Tldraw, createTLStore, defaultShapeUtils, defaultBindingUtils } from 'tldraw'; +import 'tldraw/tldraw.css'; +import { replaceYText } from './MermaidDocEditor'; + +export function TldrawDocEditor({ + provider, +}: { + docName: string; + provider: HocuspocusProvider; +}) { + const { t } = useLingui(); + const ytext = provider.document.getText('source'); + + const [store] = useState(() => { + const newStore = createTLStore({ shapeUtils: defaultShapeUtils, bindingUtils: defaultBindingUtils }); + const str = ytext.toString(); + if (str && str.trim() !== '') { + try { + const snapshot = JSON.parse(str); + newStore.loadSnapshot(snapshot); + } catch (e) { + console.error('Failed to load tldraw snapshot', e); + } + } + return newStore; + }); + + useEffect(() => { + let lastSavedString = ytext.toString(); + + // Listen to local tldraw changes and save to Y.Text + const unlisten = store.listen( + () => { + const snapshot = store.getSnapshot(); + const str = JSON.stringify(snapshot); + if (str !== lastSavedString) { + lastSavedString = str; + replaceYText(ytext, str); + } + }, + { source: 'user', scope: 'document' } + ); + + // Listen to remote Y.Text changes and load into tldraw + const sync = () => { + const str = ytext.toString(); + if (str !== lastSavedString) { + lastSavedString = str; + if (str && str.trim() !== '') { + try { + const snapshot = JSON.parse(str); + store.loadSnapshot(snapshot); + } catch (e) { + console.error('Failed to parse remote tldraw update', e); + } + } + } + }; + + ytext.observe(sync); + return () => { + unlisten(); + ytext.unobserve(sync); + }; + }, [store, ytext]); + + return ( +
+
+ +
+
+ ); +} diff --git a/packages/core/src/constants/upload.ts b/packages/core/src/constants/upload.ts index 8fd4e002f..82cb0d3e0 100644 --- a/packages/core/src/constants/upload.ts +++ b/packages/core/src/constants/upload.ts @@ -575,7 +575,7 @@ export const WIKI_EMBED_EXTENSIONS: ReadonlySet = new Set([ 'mobi', ]); -export type InlineAssetMediaKind = 'image' | 'video' | 'audio' | 'pdf' | 'text' | 'mermaid'; +export type InlineAssetMediaKind = 'image' | 'video' | 'audio' | 'pdf' | 'text' | 'mermaid' | 'tldraw'; // Sidebar-clickable asset extensions, grouped by inline-render path. // Each set is a STRICT subset of its canonical-class set (asserted below), @@ -681,6 +681,8 @@ export const TEXT_VIEWER_FALLBACK_EXTENSIONS: ReadonlySet = new Set(['ba // view in the app-side viewer, so the file always reads. export const MERMAID_FILE_EXTENSIONS: ReadonlySet = new Set(['mmd', 'mermaid']); +// Standalone tldraw canvas files. Rendered via the editor's `` component. +export const TLDRAW_FILE_EXTENSIONS: ReadonlySet = new Set(['tldr', 'tldraw', 'okdraw']); /** * True when a path/docName names a standalone Mermaid source file. Pure string * op (no `node:path`) so it runs in the browser too — core stays server-free. @@ -698,6 +700,15 @@ export function isMermaidDocFile(path: string): boolean { return MERMAID_FILE_EXTENSIONS.has(path.slice(lastDot + 1).toLowerCase()); } +/** + * True when a path/docName names a standalone tldraw source file. + */ +export function isTldrawDocFile(path: string): boolean { + const lastDot = path.lastIndexOf('.'); + if (lastDot < 0) return false; + return TLDRAW_FILE_EXTENSIONS.has(path.slice(lastDot + 1).toLowerCase()); +} + // Code-file extensions live in a sibling module so the // language→extension table can be shared with the app-side TextViewer // (which maps the same canonical IDs to CodeMirror language packs). @@ -718,6 +729,7 @@ export const LINKABLE_ASSET_EXTENSIONS: ReadonlySet = new Set([ ...ASSET_EXTENSIONS, ...TEXT_VIEWER_FALLBACK_EXTENSIONS, ...MERMAID_FILE_EXTENSIONS, + ...TLDRAW_FILE_EXTENSIONS, ]); /** @@ -739,6 +751,7 @@ export function mediaKindForSidebarAssetExtension(ext: string): InlineAssetMedia if (SIDEBAR_TEXT_ASSET_EXTENSIONS.has(normalized)) return 'text'; if (TEXT_VIEWER_FALLBACK_EXTENSIONS.has(normalized)) return 'text'; if (MERMAID_FILE_EXTENSIONS.has(normalized)) return 'mermaid'; + if (TLDRAW_FILE_EXTENSIONS.has(normalized)) return 'tldraw'; // Files whose extension matches a codeblock-supported language // (`ts` / `py` / `go` / ...) open by default in the read-only // text viewer with syntax highlighting — same kind dispatch as diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 41a4cb215..ccc9a5ecd 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -284,6 +284,7 @@ export { INLINE_RENDERABLE_EXTENSIONS, type InlineAssetMediaKind, isMermaidDocFile, + isTldrawDocFile, LINKABLE_ASSET_EXTENSIONS, MERMAID_FILE_EXTENSIONS, mediaKindForSidebarAssetExtension, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5930e3d05..d61c629fa 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -590,6 +590,9 @@ importers: tailwindcss: specifier: ^4.2.2 version: 4.3.2 + tldraw: + specifier: ^5.2.5 + version: 5.2.5(@floating-ui/dom@1.7.6)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) tw-animate-css: specifier: ^1.4.0 version: 1.4.0 @@ -5698,6 +5701,49 @@ packages: y-protocols: ^1.0.1 yjs: ^13.5.38 + '@tldraw/driver@5.2.5': + resolution: {integrity: sha512-smu4NnqTdIM59/hHTvuXxqJWQ5aLpFUR59ibS//o30P9pgMAoN4v/7NhYx9g+V7RAeOzIpDqREouz3hZdoEt9Q==} + engines: {node: '>=22.12.0'} + + '@tldraw/editor@5.2.5': + resolution: {integrity: sha512-/2aIuOLik7+tIzdmnjZ5XuxPRfSzZnNXVbxqCiFIVgbwe1W4jwm2lr2K4EpGduzdnbTNNvh6nPDJGg/jamAvnA==} + engines: {node: '>=22.12.0'} + peerDependencies: + react: 19.2.5 + react-dom: 19.2.5 + + '@tldraw/state-react@5.2.5': + resolution: {integrity: sha512-/DbTo2Oulg/ZlQwfqlyJq+rjpYBIdwl9iELB01ImQ3aDIfM5TEyabc65baj/eCW3UNlFwKVTRft9m5z4jlPbVw==} + engines: {node: '>=22.12.0'} + peerDependencies: + react: 19.2.5 + react-dom: 19.2.5 + + '@tldraw/state@5.2.5': + resolution: {integrity: sha512-fTn6YG6+MMlc3Rshb0/9k2YQ5TnKjy9FLwiGD7ph5maj9quSdpWQJNRgYxk7o2tRd/m7j+NhgylIb/F+2iKwbw==} + engines: {node: '>=22.12.0'} + + '@tldraw/store@5.2.5': + resolution: {integrity: sha512-d3sWGoFH1j9jip71VdbVYQC5WLebn2ydo2n1BG3J+PhRhv7DGP9B1bojPUd9lEPBlB4VaS6/BmP4zyk/sLmAnw==} + engines: {node: '>=22.12.0'} + peerDependencies: + react: 19.2.5 + + '@tldraw/tlschema@5.2.5': + resolution: {integrity: sha512-Oe9f1e0pcO6M42RVeBTAMqUXdWMJTeCFnM9DsBvJoY94ukIau5huUARAJ2mIV+oqUQS3pkDOrDz/L0KSN9+Eig==} + engines: {node: '>=22.12.0'} + peerDependencies: + react: 19.2.5 + react-dom: 19.2.5 + + '@tldraw/utils@5.2.5': + resolution: {integrity: sha512-8f2KqXVsPdXhZsGC2c1c+V19hpct7BQIcKnsq1b+zKUqDf7B/lNSHm/S63NeRhmsOTgzEIeOXVQYHX7OYXo2Xg==} + engines: {node: '>=22.12.0'} + + '@tldraw/validate@5.2.5': + resolution: {integrity: sha512-FgFbMuKVdFUWyZFq0wr+ojZ/zeXlm4/Ed47duvYfDWoGkQ5r/j2q4vnfeB0MVyGHxGbvVMqDOgGbWPbCJoDspA==} + engines: {node: '>=22.12.0'} + '@tokenizer/inflate@0.4.1': resolution: {integrity: sha512-2mAv+8pkG6GIZiF1kNg1jAjh27IDxEPKwdGul3snfztFerfPGI1LjDezZp3i7BElXompqEtPmoPx6c2wgtWsOA==} engines: {node: '>=18'} @@ -8133,6 +8179,9 @@ packages: resolution: {integrity: sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==} engines: {node: '>=0.10.0'} + idb@7.1.1: + resolution: {integrity: sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==} + ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} @@ -8339,6 +8388,10 @@ packages: resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} engines: {node: '>=12'} + is-plain-object@5.0.0: + resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==} + engines: {node: '>=0.10.0'} + is-potential-custom-element-name@1.0.1: resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} @@ -8703,12 +8756,21 @@ packages: resolution: {integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==} deprecated: This package is deprecated. Use require('node:util').isDeepStrictEqual instead. + lodash.isequalwith@4.4.0: + resolution: {integrity: sha512-dcZON0IalGBpRmJBmMkaoV7d3I80R2O+FrzsZyHdNSFrANq/cgDqKQNmAHE8UEj4+QYWwwhkQOVdLHiAopzlsQ==} + lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} lodash.startcase@4.4.0: resolution: {integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==} + lodash.throttle@4.1.1: + resolution: {integrity: sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==} + + lodash.uniq@4.5.0: + resolution: {integrity: sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==} + lodash@4.18.1: resolution: {integrity: sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==} @@ -9823,6 +9885,9 @@ packages: resolution: {integrity: sha512-So0Sqw869y/S2oE3Nuc0uT3Dhqgvsj8FSrwBdsuTosVsG8ME5/OcudU1GxsrIFdFABgy17GHnTVO9TYV/bLQcA==} engines: {node: '>=16.0.0'} + quickselect@2.0.0: + resolution: {integrity: sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw==} + radix-ui@1.4.3: resolution: {integrity: sha512-aWizCQiyeAenIdUbqEpXgRA1ya65P13NKn/W8rWkcN0OPkRDxdBVLWnIEDsS2RpwCK2nobI7oMUSmexzTDyAmA==} peerDependencies: @@ -9844,6 +9909,9 @@ packages: resolution: {integrity: sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==} engines: {node: '>= 0.10'} + rbush@3.0.1: + resolution: {integrity: sha512-XRaVO0YecOpEuIvbhbpTrZgoiI6xBlz6hnlr6EHhd+0x9ase6EmeN+hdwwUaJvLcsFFQ8iWVF1GAK1yB0BWi0w==} + rc@1.2.8: resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} hasBin: true @@ -10732,6 +10800,13 @@ packages: resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==} engines: {node: '>=14.0.0'} + tldraw@5.2.5: + resolution: {integrity: sha512-bTnOkeZhi+RgdHXPjxp0/nG6kbXzLC8ZlwNMpmGjWjT2Oel6uaJWni7PxFPIK4p2zInmnRamdU06ArDStLvPag==} + engines: {node: '>=22.12.0'} + peerDependencies: + react: 19.2.5 + react-dom: 19.2.5 + tldts-core@7.4.8: resolution: {integrity: sha512-c1P7u0EhACHj7lPy4MJm8iTFEU8+nB0LCtddH0fhP7noaVoXAqafMtOOeX+ulpuPBqnrRgRhw494RICT3mbhnw==} @@ -15746,6 +15821,77 @@ snapshots: y-protocols: 1.0.7(yjs@13.6.31) yjs: 13.6.31 + '@tldraw/driver@5.2.5(@floating-ui/dom@1.7.6)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + dependencies: + '@tldraw/editor': 5.2.5(@floating-ui/dom@1.7.6)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@tldraw/utils': 5.2.5 + transitivePeerDependencies: + - '@floating-ui/dom' + - '@types/react' + - '@types/react-dom' + - react + - react-dom + + '@tldraw/editor@5.2.5(@floating-ui/dom@1.7.6)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + dependencies: + '@tiptap/core': 3.22.3(@tiptap/pm@3.22.4) + '@tiptap/pm': 3.22.4 + '@tiptap/react': 3.22.3(@floating-ui/dom@1.7.6)(@tiptap/core@3.22.3(@tiptap/pm@3.22.4))(@tiptap/pm@3.22.4)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@tldraw/state': 5.2.5 + '@tldraw/state-react': 5.2.5(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@tldraw/store': 5.2.5(react@19.2.5) + '@tldraw/tlschema': 5.2.5(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@tldraw/utils': 5.2.5 + '@tldraw/validate': 5.2.5 + classnames: 2.5.1 + eventemitter3: 4.0.7 + idb: 7.1.1 + is-plain-object: 5.0.0 + rbush: 3.0.1 + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) + transitivePeerDependencies: + - '@floating-ui/dom' + - '@types/react' + - '@types/react-dom' + + '@tldraw/state-react@5.2.5(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + dependencies: + '@tldraw/state': 5.2.5 + '@tldraw/utils': 5.2.5 + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) + + '@tldraw/state@5.2.5': + dependencies: + '@tldraw/utils': 5.2.5 + + '@tldraw/store@5.2.5(react@19.2.5)': + dependencies: + '@tldraw/state': 5.2.5 + '@tldraw/utils': 5.2.5 + react: 19.2.5 + + '@tldraw/tlschema@5.2.5(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + dependencies: + '@tldraw/state': 5.2.5 + '@tldraw/store': 5.2.5(react@19.2.5) + '@tldraw/utils': 5.2.5 + '@tldraw/validate': 5.2.5 + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) + + '@tldraw/utils@5.2.5': + dependencies: + lodash.isequal: 4.5.0 + lodash.isequalwith: 4.4.0 + lodash.throttle: 4.1.1 + lodash.uniq: 4.5.0 + + '@tldraw/validate@5.2.5': + dependencies: + '@tldraw/utils': 5.2.5 + '@tokenizer/inflate@0.4.1': dependencies: debug: 4.4.3 @@ -18654,6 +18800,8 @@ snapshots: dependencies: safer-buffer: 2.1.2 + idb@7.1.1: {} + ieee754@1.2.1: {} ignore@5.3.2: {} @@ -18824,6 +18972,8 @@ snapshots: is-plain-obj@4.1.0: {} + is-plain-object@5.0.0: {} + is-potential-custom-element-name@1.0.1: {} is-promise@4.0.0: {} @@ -19163,10 +19313,16 @@ snapshots: lodash.isequal@4.5.0: {} + lodash.isequalwith@4.4.0: {} + lodash.merge@4.6.2: {} lodash.startcase@4.4.0: {} + lodash.throttle@4.1.1: {} + + lodash.uniq@4.5.0: {} + lodash@4.18.1: {} log-symbols@6.0.0: @@ -20803,6 +20959,8 @@ snapshots: '@jitl/quickjs-wasmfile-release-sync': 0.32.0 quickjs-emscripten-core: 0.32.0 + quickselect@2.0.0: {} + radix-ui@1.4.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.5(react@19.2.5))(react@19.2.5): dependencies: '@radix-ui/primitive': 1.1.4 @@ -20875,6 +21033,10 @@ snapshots: iconv-lite: 0.7.3 unpipe: 1.0.0 + rbush@3.0.1: + dependencies: + quickselect: 2.0.0 + rc@1.2.8: dependencies: deep-extend: 0.6.0 @@ -22062,6 +22224,29 @@ snapshots: tinyrainbow@3.1.0: {} + tldraw@5.2.5(@floating-ui/dom@1.7.6)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.5(react@19.2.5))(react@19.2.5): + dependencies: + '@tiptap/core': 3.22.3(@tiptap/pm@3.22.4) + '@tiptap/extension-code': 3.22.3(@tiptap/core@3.22.3(@tiptap/pm@3.22.4)) + '@tiptap/extension-highlight': 3.22.3(@tiptap/core@3.22.3(@tiptap/pm@3.22.4)) + '@tiptap/extension-list': 3.22.3(@tiptap/core@3.22.3(@tiptap/pm@3.22.4))(@tiptap/pm@3.22.4) + '@tiptap/pm': 3.22.4 + '@tiptap/react': 3.22.3(@floating-ui/dom@1.7.6)(@tiptap/core@3.22.3(@tiptap/pm@3.22.4))(@tiptap/pm@3.22.4)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@tiptap/starter-kit': 3.22.3 + '@tldraw/driver': 5.2.5(@floating-ui/dom@1.7.6)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@tldraw/editor': 5.2.5(@floating-ui/dom@1.7.6)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@tldraw/store': 5.2.5(react@19.2.5) + classnames: 2.5.1 + idb: 7.1.1 + lz-string: 1.5.0 + radix-ui: 1.4.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) + transitivePeerDependencies: + - '@floating-ui/dom' + - '@types/react' + - '@types/react-dom' + tldts-core@7.4.8: {} tldts@7.4.8: