@dxos/plugin-tldraw 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +105 -0
- package/PLUGIN.mdl +412 -0
- package/README.md +3 -0
- package/dist/assets/embed-icons/codepen.png +0 -0
- package/dist/assets/embed-icons/codesandbox.png +0 -0
- package/dist/assets/embed-icons/desmos.png +0 -0
- package/dist/assets/embed-icons/excalidraw.png +0 -0
- package/dist/assets/embed-icons/felt.png +0 -0
- package/dist/assets/embed-icons/figma.png +0 -0
- package/dist/assets/embed-icons/github_gist.png +0 -0
- package/dist/assets/embed-icons/google_calendar.png +0 -0
- package/dist/assets/embed-icons/google_maps.png +0 -0
- package/dist/assets/embed-icons/google_slides.png +0 -0
- package/dist/assets/embed-icons/observable.png +0 -0
- package/dist/assets/embed-icons/replit.png +0 -0
- package/dist/assets/embed-icons/scratch.png +0 -0
- package/dist/assets/embed-icons/spotify.png +0 -0
- package/dist/assets/embed-icons/tldraw.png +0 -0
- package/dist/assets/embed-icons/val_town.png +0 -0
- package/dist/assets/embed-icons/vimeo.png +0 -0
- package/dist/assets/embed-icons/youtube.png +0 -0
- package/dist/assets/fonts/IBMPlexMono-Medium.woff2 +0 -0
- package/dist/assets/fonts/IBMPlexSans-Medium.woff2 +0 -0
- package/dist/assets/fonts/IBMPlexSerif-Medium.woff2 +0 -0
- package/dist/assets/fonts/Montserrat-Regular.woff2 +0 -0
- package/dist/assets/fonts/Shantell_Sans-Tldrawish.woff2 +0 -0
- package/dist/assets/icons/icon/0_merged.svg +1 -0
- package/dist/assets/icons/icon/align-bottom.svg +1 -0
- package/dist/assets/icons/icon/align-center-horizontal.svg +1 -0
- package/dist/assets/icons/icon/align-center-vertical.svg +1 -0
- package/dist/assets/icons/icon/align-left.svg +1 -0
- package/dist/assets/icons/icon/align-right.svg +1 -0
- package/dist/assets/icons/icon/align-top.svg +1 -0
- package/dist/assets/icons/icon/arrow-left.svg +1 -0
- package/dist/assets/icons/icon/arrowhead-arrow.svg +1 -0
- package/dist/assets/icons/icon/arrowhead-bar.svg +1 -0
- package/dist/assets/icons/icon/arrowhead-diamond.svg +1 -0
- package/dist/assets/icons/icon/arrowhead-dot.svg +1 -0
- package/dist/assets/icons/icon/arrowhead-none.svg +1 -0
- package/dist/assets/icons/icon/arrowhead-square.svg +1 -0
- package/dist/assets/icons/icon/arrowhead-triangle-inverted.svg +1 -0
- package/dist/assets/icons/icon/arrowhead-triangle.svg +1 -0
- package/dist/assets/icons/icon/blob.svg +1 -0
- package/dist/assets/icons/icon/bring-forward.svg +1 -0
- package/dist/assets/icons/icon/bring-to-front.svg +1 -0
- package/dist/assets/icons/icon/broken.svg +1 -0
- package/dist/assets/icons/icon/chat-teardrop-text.svg +1 -0
- package/dist/assets/icons/icon/check-circle.svg +1 -0
- package/dist/assets/icons/icon/check.svg +1 -0
- package/dist/assets/icons/icon/chevron-down.svg +1 -0
- package/dist/assets/icons/icon/chevron-left.svg +1 -0
- package/dist/assets/icons/icon/chevron-right.svg +1 -0
- package/dist/assets/icons/icon/chevron-up.svg +1 -0
- package/dist/assets/icons/icon/chevrons-ne.svg +1 -0
- package/dist/assets/icons/icon/chevrons-sw.svg +1 -0
- package/dist/assets/icons/icon/clipboard-copied.svg +1 -0
- package/dist/assets/icons/icon/clipboard-copy.svg +1 -0
- package/dist/assets/icons/icon/color.svg +1 -0
- package/dist/assets/icons/icon/cross-2.svg +1 -0
- package/dist/assets/icons/icon/cross-circle.svg +1 -0
- package/dist/assets/icons/icon/dash-dashed.svg +1 -0
- package/dist/assets/icons/icon/dash-dotted.svg +1 -0
- package/dist/assets/icons/icon/dash-draw.svg +1 -0
- package/dist/assets/icons/icon/dash-solid.svg +1 -0
- package/dist/assets/icons/icon/disconnected.svg +1 -0
- package/dist/assets/icons/icon/discord.svg +1 -0
- package/dist/assets/icons/icon/distribute-horizontal.svg +1 -0
- package/dist/assets/icons/icon/distribute-vertical.svg +1 -0
- package/dist/assets/icons/icon/dot.svg +1 -0
- package/dist/assets/icons/icon/dots-horizontal.svg +1 -0
- package/dist/assets/icons/icon/dots-vertical.svg +1 -0
- package/dist/assets/icons/icon/drag-handle-dots.svg +1 -0
- package/dist/assets/icons/icon/duplicate.svg +1 -0
- package/dist/assets/icons/icon/edit.svg +1 -0
- package/dist/assets/icons/icon/external-link.svg +1 -0
- package/dist/assets/icons/icon/fill-fill.svg +1 -0
- package/dist/assets/icons/icon/fill-none.svg +1 -0
- package/dist/assets/icons/icon/fill-pattern.svg +1 -0
- package/dist/assets/icons/icon/fill-semi.svg +1 -0
- package/dist/assets/icons/icon/fill-solid.svg +1 -0
- package/dist/assets/icons/icon/follow.svg +1 -0
- package/dist/assets/icons/icon/following.svg +1 -0
- package/dist/assets/icons/icon/font-draw.svg +8 -0
- package/dist/assets/icons/icon/font-mono.svg +1 -0
- package/dist/assets/icons/icon/font-sans.svg +1 -0
- package/dist/assets/icons/icon/font-serif.svg +1 -0
- package/dist/assets/icons/icon/geo-arrow-down.svg +1 -0
- package/dist/assets/icons/icon/geo-arrow-left.svg +1 -0
- package/dist/assets/icons/icon/geo-arrow-right.svg +1 -0
- package/dist/assets/icons/icon/geo-arrow-up.svg +1 -0
- package/dist/assets/icons/icon/geo-check-box.svg +1 -0
- package/dist/assets/icons/icon/geo-cloud.svg +1 -0
- package/dist/assets/icons/icon/geo-diamond.svg +1 -0
- package/dist/assets/icons/icon/geo-ellipse.svg +1 -0
- package/dist/assets/icons/icon/geo-heart.svg +1 -0
- package/dist/assets/icons/icon/geo-hexagon.svg +1 -0
- package/dist/assets/icons/icon/geo-octagon.svg +1 -0
- package/dist/assets/icons/icon/geo-oval.svg +1 -0
- package/dist/assets/icons/icon/geo-pentagon.svg +1 -0
- package/dist/assets/icons/icon/geo-rectangle.svg +1 -0
- package/dist/assets/icons/icon/geo-rhombus-2.svg +1 -0
- package/dist/assets/icons/icon/geo-rhombus.svg +1 -0
- package/dist/assets/icons/icon/geo-star.svg +1 -0
- package/dist/assets/icons/icon/geo-trapezoid.svg +1 -0
- package/dist/assets/icons/icon/geo-triangle.svg +1 -0
- package/dist/assets/icons/icon/geo-x-box.svg +1 -0
- package/dist/assets/icons/icon/github.svg +1 -0
- package/dist/assets/icons/icon/group.svg +1 -0
- package/dist/assets/icons/icon/horizontal-align-end.svg +1 -0
- package/dist/assets/icons/icon/horizontal-align-middle.svg +1 -0
- package/dist/assets/icons/icon/horizontal-align-start.svg +1 -0
- package/dist/assets/icons/icon/icon-names.json +143 -0
- package/dist/assets/icons/icon/info-circle.svg +1 -0
- package/dist/assets/icons/icon/leading.svg +1 -0
- package/dist/assets/icons/icon/link.svg +1 -0
- package/dist/assets/icons/icon/lock.svg +1 -0
- package/dist/assets/icons/icon/menu.svg +1 -0
- package/dist/assets/icons/icon/minus.svg +1 -0
- package/dist/assets/icons/icon/mixed.svg +1 -0
- package/dist/assets/icons/icon/pack.svg +1 -0
- package/dist/assets/icons/icon/plus.svg +1 -0
- package/dist/assets/icons/icon/question-mark-circle.svg +1 -0
- package/dist/assets/icons/icon/question-mark.svg +1 -0
- package/dist/assets/icons/icon/redo.svg +1 -0
- package/dist/assets/icons/icon/reset-zoom.svg +1 -0
- package/dist/assets/icons/icon/rotate-ccw.svg +1 -0
- package/dist/assets/icons/icon/rotate-cw.svg +1 -0
- package/dist/assets/icons/icon/send-backward.svg +1 -0
- package/dist/assets/icons/icon/send-to-back.svg +1 -0
- package/dist/assets/icons/icon/share-1.svg +1 -0
- package/dist/assets/icons/icon/size-extra-large.svg +1 -0
- package/dist/assets/icons/icon/size-large.svg +1 -0
- package/dist/assets/icons/icon/size-medium.svg +1 -0
- package/dist/assets/icons/icon/size-small.svg +1 -0
- package/dist/assets/icons/icon/spline-cubic.svg +1 -0
- package/dist/assets/icons/icon/spline-line.svg +1 -0
- package/dist/assets/icons/icon/stack-horizontal.svg +1 -0
- package/dist/assets/icons/icon/stack-vertical.svg +1 -0
- package/dist/assets/icons/icon/status-offline.svg +1 -0
- package/dist/assets/icons/icon/stretch-horizontal.svg +1 -0
- package/dist/assets/icons/icon/stretch-vertical.svg +1 -0
- package/dist/assets/icons/icon/text-align-center.svg +1 -0
- package/dist/assets/icons/icon/text-align-left.svg +1 -0
- package/dist/assets/icons/icon/text-align-right.svg +1 -0
- package/dist/assets/icons/icon/toggle-off.svg +1 -0
- package/dist/assets/icons/icon/toggle-on.svg +1 -0
- package/dist/assets/icons/icon/tool-arrow.svg +1 -0
- package/dist/assets/icons/icon/tool-eraser.svg +1 -0
- package/dist/assets/icons/icon/tool-frame.svg +1 -0
- package/dist/assets/icons/icon/tool-hand.svg +1 -0
- package/dist/assets/icons/icon/tool-highlight.svg +1 -0
- package/dist/assets/icons/icon/tool-laser.svg +1 -0
- package/dist/assets/icons/icon/tool-line.svg +1 -0
- package/dist/assets/icons/icon/tool-media.svg +1 -0
- package/dist/assets/icons/icon/tool-note.svg +1 -0
- package/dist/assets/icons/icon/tool-pencil.svg +1 -0
- package/dist/assets/icons/icon/tool-pointer.svg +1 -0
- package/dist/assets/icons/icon/tool-screenshot.svg +1 -0
- package/dist/assets/icons/icon/tool-text.svg +1 -0
- package/dist/assets/icons/icon/trash.svg +1 -0
- package/dist/assets/icons/icon/twitter.svg +1 -0
- package/dist/assets/icons/icon/undo.svg +1 -0
- package/dist/assets/icons/icon/ungroup.svg +1 -0
- package/dist/assets/icons/icon/unlock.svg +1 -0
- package/dist/assets/icons/icon/vertical-align-end.svg +1 -0
- package/dist/assets/icons/icon/vertical-align-middle.svg +1 -0
- package/dist/assets/icons/icon/vertical-align-start.svg +1 -0
- package/dist/assets/icons/icon/warning-triangle.svg +1 -0
- package/dist/assets/icons/icon/zoom-in.svg +1 -0
- package/dist/assets/icons/icon/zoom-out.svg +1 -0
- package/dist/assets/translations/ar.json +295 -0
- package/dist/assets/translations/ca.json +295 -0
- package/dist/assets/translations/cs.json +352 -0
- package/dist/assets/translations/da.json +160 -0
- package/dist/assets/translations/de.json +310 -0
- package/dist/assets/translations/en.json +1 -0
- package/dist/assets/translations/es.json +299 -0
- package/dist/assets/translations/fa.json +384 -0
- package/dist/assets/translations/fi.json +312 -0
- package/dist/assets/translations/fr.json +372 -0
- package/dist/assets/translations/gl.json +349 -0
- package/dist/assets/translations/he.json +86 -0
- package/dist/assets/translations/hi-in.json +290 -0
- package/dist/assets/translations/hr.json +358 -0
- package/dist/assets/translations/hu.json +365 -0
- package/dist/assets/translations/id.json +373 -0
- package/dist/assets/translations/it.json +330 -0
- package/dist/assets/translations/ja.json +351 -0
- package/dist/assets/translations/ko-kr.json +396 -0
- package/dist/assets/translations/ku.json +93 -0
- package/dist/assets/translations/languages.json +150 -0
- package/dist/assets/translations/main.json +396 -0
- package/dist/assets/translations/my.json +112 -0
- package/dist/assets/translations/ne.json +295 -0
- package/dist/assets/translations/no.json +62 -0
- package/dist/assets/translations/pl.json +113 -0
- package/dist/assets/translations/pt-br.json +333 -0
- package/dist/assets/translations/pt-pt.json +86 -0
- package/dist/assets/translations/ro.json +371 -0
- package/dist/assets/translations/ru.json +375 -0
- package/dist/assets/translations/sl.json +373 -0
- package/dist/assets/translations/sv.json +96 -0
- package/dist/assets/translations/te.json +112 -0
- package/dist/assets/translations/th.json +295 -0
- package/dist/assets/translations/tr.json +355 -0
- package/dist/assets/translations/uk.json +375 -0
- package/dist/assets/translations/vi.json +295 -0
- package/dist/assets/translations/zh-cn.json +386 -0
- package/dist/assets/translations/zh-tw.json +340 -0
- package/dist/lib/TldrawPlugin.mjs +22 -0
- package/dist/lib/TldrawPlugin.mjs.map +1 -0
- package/dist/lib/assets/plugin-tldraw-BDrn_mrV.css +24 -0
- package/dist/lib/capabilities.mjs +8 -0
- package/dist/lib/capabilities.mjs.map +1 -0
- package/dist/lib/chunk-TldrawArticle.mjs +60 -0
- package/dist/lib/chunk-TldrawArticle.mjs.map +1 -0
- package/dist/lib/chunk-TldrawCard.mjs +35 -0
- package/dist/lib/chunk-TldrawCard.mjs.map +1 -0
- package/dist/lib/chunk-meta.mjs +40 -0
- package/dist/lib/chunk-meta.mjs.map +1 -0
- package/dist/lib/chunk-settings.mjs +23 -0
- package/dist/lib/chunk-settings.mjs.map +1 -0
- package/dist/lib/chunk-sketch-variant.mjs +23 -0
- package/dist/lib/chunk-sketch-variant.mjs.map +1 -0
- package/dist/lib/chunk-types.mjs +73 -0
- package/dist/lib/chunk-types.mjs.map +1 -0
- package/dist/lib/components.mjs +443 -0
- package/dist/lib/components.mjs.map +1 -0
- package/dist/lib/containers.mjs +8 -0
- package/dist/lib/containers.mjs.map +1 -0
- package/dist/lib/hooks.mjs +116 -0
- package/dist/lib/hooks.mjs.map +1 -0
- package/dist/lib/index.mjs +4 -0
- package/dist/lib/meta.mjs +2 -0
- package/dist/lib/model.mjs +905 -0
- package/dist/lib/model.mjs.map +1 -0
- package/dist/lib/plugin.mjs +8 -0
- package/dist/lib/plugin.mjs.map +1 -0
- package/dist/lib/testing.mjs +246 -0
- package/dist/lib/testing.mjs.map +1 -0
- package/dist/lib/translations.mjs +10 -0
- package/dist/lib/translations.mjs.map +1 -0
- package/dist/lib/types.mjs +2 -0
- package/dist/types/dx.config.d.ts +28 -0
- package/dist/types/dx.config.d.ts.map +1 -0
- package/dist/types/src/TldrawPlugin.d.ts +4 -0
- package/dist/types/src/TldrawPlugin.d.ts.map +1 -0
- package/dist/types/src/capabilities/index.d.ts +4 -0
- package/dist/types/src/capabilities/index.d.ts.map +1 -0
- package/dist/types/src/capabilities/settings.d.ts +5 -0
- package/dist/types/src/capabilities/settings.d.ts.map +1 -0
- package/dist/types/src/capabilities/sketch-variant.d.ts +5 -0
- package/dist/types/src/capabilities/sketch-variant.d.ts.map +1 -0
- package/dist/types/src/components/Canvas/Canvas.d.ts +17 -0
- package/dist/types/src/components/Canvas/Canvas.d.ts.map +1 -0
- package/dist/types/src/components/Canvas/Canvas.stories.d.ts +15 -0
- package/dist/types/src/components/Canvas/Canvas.stories.d.ts.map +1 -0
- package/dist/types/src/components/Canvas/index.d.ts +4 -0
- package/dist/types/src/components/Canvas/index.d.ts.map +1 -0
- package/dist/types/src/components/actions.d.ts +6 -0
- package/dist/types/src/components/actions.d.ts.map +1 -0
- package/dist/types/src/components/custom/CustomGrid.d.ts +12 -0
- package/dist/types/src/components/custom/CustomGrid.d.ts.map +1 -0
- package/dist/types/src/components/custom/CustomMenu.d.ts +4 -0
- package/dist/types/src/components/custom/CustomMenu.d.ts.map +1 -0
- package/dist/types/src/components/custom/CustomStylePanel.d.ts +4 -0
- package/dist/types/src/components/custom/CustomStylePanel.d.ts.map +1 -0
- package/dist/types/src/components/custom/CustomToolbar.d.ts +10 -0
- package/dist/types/src/components/custom/CustomToolbar.d.ts.map +1 -0
- package/dist/types/src/components/custom/DottedGrid.d.ts +4 -0
- package/dist/types/src/components/custom/DottedGrid.d.ts.map +1 -0
- package/dist/types/src/components/custom/MeshGrid.d.ts +4 -0
- package/dist/types/src/components/custom/MeshGrid.d.ts.map +1 -0
- package/dist/types/src/components/custom/index.d.ts +6 -0
- package/dist/types/src/components/custom/index.d.ts.map +1 -0
- package/dist/types/src/components/index.d.ts +2 -0
- package/dist/types/src/components/index.d.ts.map +1 -0
- package/dist/types/src/containers/TldrawArticle/TldrawArticle.d.ts +8 -0
- package/dist/types/src/containers/TldrawArticle/TldrawArticle.d.ts.map +1 -0
- package/dist/types/src/containers/TldrawArticle/index.d.ts +2 -0
- package/dist/types/src/containers/TldrawArticle/index.d.ts.map +1 -0
- package/dist/types/src/containers/TldrawCard/TldrawCard.d.ts +9 -0
- package/dist/types/src/containers/TldrawCard/TldrawCard.d.ts.map +1 -0
- package/dist/types/src/containers/TldrawCard/TldrawCard.stories.d.ts +15 -0
- package/dist/types/src/containers/TldrawCard/TldrawCard.stories.d.ts.map +1 -0
- package/dist/types/src/containers/TldrawCard/index.d.ts +2 -0
- package/dist/types/src/containers/TldrawCard/index.d.ts.map +1 -0
- package/dist/types/src/containers/index.d.ts +4 -0
- package/dist/types/src/containers/index.d.ts.map +1 -0
- package/dist/types/src/hooks/adapter.d.ts +15 -0
- package/dist/types/src/hooks/adapter.d.ts.map +1 -0
- package/dist/types/src/hooks/adapter.test.d.ts +2 -0
- package/dist/types/src/hooks/adapter.test.d.ts.map +1 -0
- package/dist/types/src/hooks/index.d.ts +3 -0
- package/dist/types/src/hooks/index.d.ts.map +1 -0
- package/dist/types/src/hooks/useStoreAdapter.d.ts +11 -0
- package/dist/types/src/hooks/useStoreAdapter.d.ts.map +1 -0
- package/dist/types/src/index.d.ts +4 -0
- package/dist/types/src/index.d.ts.map +1 -0
- package/dist/types/src/meta.d.ts +33 -0
- package/dist/types/src/meta.d.ts.map +1 -0
- package/dist/types/src/migrations/index.d.ts +3 -0
- package/dist/types/src/migrations/index.d.ts.map +1 -0
- package/dist/types/src/migrations/migrate.d.ts +9 -0
- package/dist/types/src/migrations/migrate.d.ts.map +1 -0
- package/dist/types/src/migrations/schema.d.ts +7 -0
- package/dist/types/src/migrations/schema.d.ts.map +1 -0
- package/dist/types/src/model/TldrawBuilder.d.ts +110 -0
- package/dist/types/src/model/TldrawBuilder.d.ts.map +1 -0
- package/dist/types/src/model/TldrawBuilder.test.d.ts +2 -0
- package/dist/types/src/model/TldrawBuilder.test.d.ts.map +1 -0
- package/dist/types/src/model/apply.d.ts +5 -0
- package/dist/types/src/model/apply.d.ts.map +1 -0
- package/dist/types/src/model/builder.d.ts +3 -0
- package/dist/types/src/model/builder.d.ts.map +1 -0
- package/dist/types/src/model/index.d.ts +6 -0
- package/dist/types/src/model/index.d.ts.map +1 -0
- package/dist/types/src/model/read.d.ts +8 -0
- package/dist/types/src/model/read.d.ts.map +1 -0
- package/dist/types/src/model/render.d.ts +27 -0
- package/dist/types/src/model/render.d.ts.map +1 -0
- package/dist/types/src/model/scene.test.d.ts +2 -0
- package/dist/types/src/model/scene.test.d.ts.map +1 -0
- package/dist/types/src/plugin.d.ts +3 -0
- package/dist/types/src/plugin.d.ts.map +1 -0
- package/dist/types/src/testing/data.d.ts +242 -0
- package/dist/types/src/testing/data.d.ts.map +1 -0
- package/dist/types/src/testing/index.d.ts +2 -0
- package/dist/types/src/testing/index.d.ts.map +1 -0
- package/dist/types/src/testing/schema.d.ts +7 -0
- package/dist/types/src/testing/schema.d.ts.map +1 -0
- package/dist/types/src/translations.d.ts +9 -0
- package/dist/types/src/translations.d.ts.map +1 -0
- package/dist/types/src/types/Settings.d.ts +10 -0
- package/dist/types/src/types/Settings.d.ts.map +1 -0
- package/dist/types/src/types/Tldraw.d.ts +25 -0
- package/dist/types/src/types/Tldraw.d.ts.map +1 -0
- package/dist/types/src/types/TldrawCapabilities.d.ts +4 -0
- package/dist/types/src/types/TldrawCapabilities.d.ts.map +1 -0
- package/dist/types/src/types/index.d.ts +4 -0
- package/dist/types/src/types/index.d.ts.map +1 -0
- package/dist/types/src/types/types.d.ts +6 -0
- package/dist/types/src/types/types.d.ts.map +1 -0
- package/dist/types/src/variant.test.d.ts +2 -0
- package/dist/types/src/variant.test.d.ts.map +1 -0
- package/dist/types/tsconfig.tsbuildinfo +1 -0
- package/dx.config.ts +35 -0
- package/package.json +157 -0
- package/src/TldrawPlugin.tsx +31 -0
- package/src/capabilities/index.ts +8 -0
- package/src/capabilities/settings.ts +31 -0
- package/src/capabilities/sketch-variant.ts +29 -0
- package/src/components/Canvas/Canvas.stories.tsx +101 -0
- package/src/components/Canvas/Canvas.tsx +284 -0
- package/src/components/Canvas/index.ts +9 -0
- package/src/components/Canvas/theme.css +23 -0
- package/src/components/actions.ts +42 -0
- package/src/components/custom/CustomGrid.tsx +61 -0
- package/src/components/custom/CustomMenu.tsx +31 -0
- package/src/components/custom/CustomStylePanel.tsx +38 -0
- package/src/components/custom/CustomToolbar.tsx +88 -0
- package/src/components/custom/DottedGrid.tsx +53 -0
- package/src/components/custom/MeshGrid.tsx +47 -0
- package/src/components/custom/index.ts +9 -0
- package/src/components/index.ts +5 -0
- package/src/containers/TldrawArticle/TldrawArticle.tsx +78 -0
- package/src/containers/TldrawArticle/index.ts +5 -0
- package/src/containers/TldrawCard/TldrawCard.stories.tsx +53 -0
- package/src/containers/TldrawCard/TldrawCard.tsx +32 -0
- package/src/containers/TldrawCard/index.ts +5 -0
- package/src/containers/index.ts +8 -0
- package/src/hooks/adapter.test.ts +20 -0
- package/src/hooks/adapter.ts +83 -0
- package/src/hooks/index.ts +6 -0
- package/src/hooks/useStoreAdapter.ts +55 -0
- package/src/index.ts +7 -0
- package/src/meta.ts +9 -0
- package/src/migrations/index.ts +6 -0
- package/src/migrations/migrate.ts +49 -0
- package/src/migrations/schema.ts +98 -0
- package/src/model/TldrawBuilder.test.ts +72 -0
- package/src/model/TldrawBuilder.ts +443 -0
- package/src/model/apply.ts +132 -0
- package/src/model/builder.ts +31 -0
- package/src/model/index.ts +9 -0
- package/src/model/read.ts +203 -0
- package/src/model/render.ts +191 -0
- package/src/model/scene.test.ts +222 -0
- package/src/plugin.ts +9 -0
- package/src/testing/data.ts +238 -0
- package/src/testing/index.ts +5 -0
- package/src/testing/schema.ts +98 -0
- package/src/translations.ts +18 -0
- package/src/types/Settings.ts +32 -0
- package/src/types/Tldraw.ts +38 -0
- package/src/types/TldrawCapabilities.ts +17 -0
- package/src/types/index.ts +8 -0
- package/src/types/types.ts +11 -0
- package/src/typings.d.ts +8 -0
- package/src/variant.test.ts +142 -0
- package/src/vite-env.d.ts +10 -0
package/dx.config.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { Config2 } from '@dxos/app-framework/config';
|
|
6
|
+
import { trim } from '@dxos/util';
|
|
7
|
+
|
|
8
|
+
export default Config2.make({
|
|
9
|
+
plugin: {
|
|
10
|
+
key: 'org.dxos.plugin.tldraw',
|
|
11
|
+
name: 'tldraw',
|
|
12
|
+
author: 'DXOS',
|
|
13
|
+
description: trim`
|
|
14
|
+
tldraw is the canvas renderer that powers collaborative whiteboards in DXOS Composer,
|
|
15
|
+
giving every workspace a full-featured infinite canvas. Draw freehand strokes, place
|
|
16
|
+
geometric shapes, add text and arrows, and organise ideas visually — all within the same
|
|
17
|
+
local-first environment as the rest of your documents.
|
|
18
|
+
|
|
19
|
+
The plugin contributes a sketch variant to the Illustrator plugin: the base \`Sketch\`
|
|
20
|
+
object references a tldraw Canvas whose content is stored as a plain JSON record in ECHO,
|
|
21
|
+
so no binary blobs or external storage are required. Toolbar actions, grid settings, and
|
|
22
|
+
custom tools are wired through the Composer plugin system, and a builder maps the
|
|
23
|
+
Illustrator scene DSL onto tldraw records so agents can draw and edit diagrams.
|
|
24
|
+
|
|
25
|
+
Because Canvas data lives in ECHO, every edit is replicated peer-to-peer in real time.
|
|
26
|
+
Collaborators see each other's strokes as they draw, and changes made offline merge
|
|
27
|
+
automatically when connectivity is restored using ECHO's CRDT semantics.
|
|
28
|
+
`,
|
|
29
|
+
source: 'https://github.com/dxos/dxos/tree/main/packages/plugins/plugin-tldraw',
|
|
30
|
+
dependsOn: ['org.dxos.plugin.illustrator'],
|
|
31
|
+
icon: { key: 'ph--compass-tool--regular', hue: 'indigo' },
|
|
32
|
+
spec: 'PLUGIN.mdl',
|
|
33
|
+
screenshots: [{ dark: 'https://dxos.network/plugin-details-sketch-dark.png' }],
|
|
34
|
+
},
|
|
35
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@dxos/plugin-tldraw",
|
|
3
|
+
"version": "0.10.0",
|
|
4
|
+
"description": "tldraw canvas renderer for the DXOS Illustrator plugin.",
|
|
5
|
+
"homepage": "https://dxos.org",
|
|
6
|
+
"bugs": "https://github.com/dxos/dxos/issues",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/dxos/dxos"
|
|
10
|
+
},
|
|
11
|
+
"license": "FSL-1.1-Apache-2.0",
|
|
12
|
+
"author": "DXOS.org",
|
|
13
|
+
"sideEffects": true,
|
|
14
|
+
"type": "module",
|
|
15
|
+
"imports": {
|
|
16
|
+
"#capabilities": {
|
|
17
|
+
"source": "./src/capabilities/index.ts",
|
|
18
|
+
"types": "./dist/types/src/capabilities/index.d.ts",
|
|
19
|
+
"default": "./dist/lib/capabilities.mjs"
|
|
20
|
+
},
|
|
21
|
+
"#components": {
|
|
22
|
+
"source": "./src/components/index.ts",
|
|
23
|
+
"types": "./dist/types/src/components/index.d.ts",
|
|
24
|
+
"default": "./dist/lib/components.mjs"
|
|
25
|
+
},
|
|
26
|
+
"#containers": {
|
|
27
|
+
"source": "./src/containers/index.ts",
|
|
28
|
+
"types": "./dist/types/src/containers/index.d.ts",
|
|
29
|
+
"default": "./dist/lib/containers.mjs"
|
|
30
|
+
},
|
|
31
|
+
"#hooks": {
|
|
32
|
+
"source": "./src/hooks/index.ts",
|
|
33
|
+
"types": "./dist/types/src/hooks/index.d.ts",
|
|
34
|
+
"default": "./dist/lib/hooks.mjs"
|
|
35
|
+
},
|
|
36
|
+
"#model": {
|
|
37
|
+
"source": "./src/model/index.ts",
|
|
38
|
+
"types": "./dist/types/src/model/index.d.ts",
|
|
39
|
+
"default": "./dist/lib/model.mjs"
|
|
40
|
+
},
|
|
41
|
+
"#meta": {
|
|
42
|
+
"source": "./src/meta.ts",
|
|
43
|
+
"types": "./dist/types/src/meta.d.ts",
|
|
44
|
+
"default": "./dist/lib/meta.mjs"
|
|
45
|
+
},
|
|
46
|
+
"#plugin": {
|
|
47
|
+
"source": "./src/TldrawPlugin.tsx",
|
|
48
|
+
"types": "./dist/types/src/TldrawPlugin.d.ts",
|
|
49
|
+
"default": "./dist/lib/TldrawPlugin.mjs"
|
|
50
|
+
},
|
|
51
|
+
"#testing": "./src/testing/index.ts",
|
|
52
|
+
"#translations": {
|
|
53
|
+
"source": "./src/translations.ts",
|
|
54
|
+
"types": "./dist/types/src/translations.d.ts",
|
|
55
|
+
"default": "./dist/lib/translations.mjs"
|
|
56
|
+
},
|
|
57
|
+
"#types": {
|
|
58
|
+
"source": "./src/types/index.ts",
|
|
59
|
+
"types": "./dist/types/src/types/index.d.ts",
|
|
60
|
+
"default": "./dist/lib/types.mjs"
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
"exports": {
|
|
64
|
+
".": {
|
|
65
|
+
"source": "./src/index.ts",
|
|
66
|
+
"types": "./dist/types/src/index.d.ts",
|
|
67
|
+
"import": "./dist/lib/index.mjs"
|
|
68
|
+
},
|
|
69
|
+
"./assets/PLUGIN.mdl": "./PLUGIN.mdl",
|
|
70
|
+
"./plugin": {
|
|
71
|
+
"source": "./src/plugin.ts",
|
|
72
|
+
"types": "./dist/types/src/plugin.d.ts",
|
|
73
|
+
"import": "./dist/lib/plugin.mjs"
|
|
74
|
+
},
|
|
75
|
+
"./testing": {
|
|
76
|
+
"source": "./src/testing/index.ts",
|
|
77
|
+
"types": "./dist/types/src/testing/index.d.ts",
|
|
78
|
+
"import": "./dist/lib/testing.mjs"
|
|
79
|
+
},
|
|
80
|
+
"./translations": {
|
|
81
|
+
"source": "./src/translations.ts",
|
|
82
|
+
"types": "./dist/types/src/translations.d.ts",
|
|
83
|
+
"import": "./dist/lib/translations.mjs"
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
"types": "dist/types/src/index.d.ts",
|
|
87
|
+
"files": [
|
|
88
|
+
"dist",
|
|
89
|
+
"dx.config.ts",
|
|
90
|
+
"src",
|
|
91
|
+
"PLUGIN.mdl"
|
|
92
|
+
],
|
|
93
|
+
"dependencies": {
|
|
94
|
+
"@automerge/automerge": "3.3.0-fragments.2",
|
|
95
|
+
"@effect-atom/atom-react": "^0.5.0",
|
|
96
|
+
"@tldraw/assets": "^3.0.0",
|
|
97
|
+
"@tldraw/editor": "^3.0.0",
|
|
98
|
+
"@tldraw/state": "^3.0.0",
|
|
99
|
+
"@tldraw/store": "^3.0.0",
|
|
100
|
+
"@tldraw/tldraw": "^3.0.0",
|
|
101
|
+
"@tldraw/tlschema": "^3.0.0",
|
|
102
|
+
"@tldraw/utils": "^3.0.0",
|
|
103
|
+
"effect": "3.21.4",
|
|
104
|
+
"lodash.defaultsdeep": "^4.6.1",
|
|
105
|
+
"react-resize-detector": "^11.0.1",
|
|
106
|
+
"@dxos/app-framework": "0.10.0",
|
|
107
|
+
"@dxos/app-toolkit": "0.10.0",
|
|
108
|
+
"@dxos/async": "0.10.0",
|
|
109
|
+
"@dxos/client": "0.10.0",
|
|
110
|
+
"@dxos/echo": "0.10.0",
|
|
111
|
+
"@dxos/echo-client": "0.10.0",
|
|
112
|
+
"@dxos/invariant": "0.10.0",
|
|
113
|
+
"@dxos/compute": "0.10.0",
|
|
114
|
+
"@dxos/effect": "0.10.0",
|
|
115
|
+
"@dxos/echo-doc": "0.10.0",
|
|
116
|
+
"@dxos/log": "0.10.0",
|
|
117
|
+
"@dxos/plugin-illustrator": "0.10.0",
|
|
118
|
+
"@dxos/keys": "0.10.0",
|
|
119
|
+
"@dxos/react-client": "0.10.0",
|
|
120
|
+
"@dxos/react-ui-attention": "0.10.0",
|
|
121
|
+
"@dxos/react-hooks": "0.10.0",
|
|
122
|
+
"@dxos/plugin-graph": "0.10.0",
|
|
123
|
+
"@dxos/plugin-space": "0.10.0",
|
|
124
|
+
"@dxos/react-ui-form": "0.10.0",
|
|
125
|
+
"@dxos/schema": "0.10.0",
|
|
126
|
+
"@dxos/util": "0.10.0"
|
|
127
|
+
},
|
|
128
|
+
"devDependencies": {
|
|
129
|
+
"@effect/vitest": "0.29.0",
|
|
130
|
+
"@types/lodash.defaultsdeep": "^4.6.6",
|
|
131
|
+
"@types/react": "~19.2.17",
|
|
132
|
+
"@types/react-dom": "~19.2.3",
|
|
133
|
+
"react": "~19.2.7",
|
|
134
|
+
"react-dom": "~19.2.7",
|
|
135
|
+
"vite": "^8.1.4",
|
|
136
|
+
"@dxos/plugin-testing": "0.10.0",
|
|
137
|
+
"@dxos/random": "0.10.0",
|
|
138
|
+
"@dxos/react-ui": "0.10.0",
|
|
139
|
+
"@dxos/react-ui-mosaic": "0.10.0",
|
|
140
|
+
"@dxos/storybook-utils": "0.10.0",
|
|
141
|
+
"@dxos/ui-theme": "0.10.0",
|
|
142
|
+
"@dxos/agent-runtime": "0.10.0"
|
|
143
|
+
},
|
|
144
|
+
"peerDependencies": {
|
|
145
|
+
"effect": "3.21.4",
|
|
146
|
+
"react": "~19.2.7",
|
|
147
|
+
"react-dom": "~19.2.7",
|
|
148
|
+
"@dxos/react-ui": "^0.10.0",
|
|
149
|
+
"@dxos/ui-theme": "^0.10.0"
|
|
150
|
+
},
|
|
151
|
+
"publishConfig": {
|
|
152
|
+
"access": "public"
|
|
153
|
+
},
|
|
154
|
+
"scripts": {
|
|
155
|
+
"copy:assets": "./scripts/copy_assets.sh"
|
|
156
|
+
}
|
|
157
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { Plugin } from '@dxos/app-framework';
|
|
6
|
+
import { AppActivationEvents, AppPlugin } from '@dxos/app-toolkit';
|
|
7
|
+
|
|
8
|
+
import { SketchVariant, TldrawSettings } from '#capabilities';
|
|
9
|
+
import { meta } from '#meta';
|
|
10
|
+
import { translations } from '#translations';
|
|
11
|
+
import { Tldraw } from '#types';
|
|
12
|
+
|
|
13
|
+
// eslint-disable-next-line import/no-relative-packages
|
|
14
|
+
import pluginSpec from '../PLUGIN.mdl?raw';
|
|
15
|
+
|
|
16
|
+
export const TldrawPlugin = Plugin.define(meta).pipe(
|
|
17
|
+
Plugin.addModule({
|
|
18
|
+
id: 'sketch-variant',
|
|
19
|
+
activatesOn: AppActivationEvents.SetupSchema,
|
|
20
|
+
activate: SketchVariant,
|
|
21
|
+
}),
|
|
22
|
+
AppPlugin.addSchemaModule({ schema: [Tldraw.Canvas] }),
|
|
23
|
+
AppPlugin.addSettingsModule({ activate: TldrawSettings }),
|
|
24
|
+
AppPlugin.addTranslationsModule({ translations }),
|
|
25
|
+
AppPlugin.addPluginAssetModule({
|
|
26
|
+
asset: { pluginId: meta.profile.key, path: 'PLUGIN.mdl', content: pluginSpec, mimeType: 'application/x-mdl' },
|
|
27
|
+
}),
|
|
28
|
+
Plugin.make,
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
export default TldrawPlugin;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { Capability } from '@dxos/app-framework';
|
|
6
|
+
|
|
7
|
+
export const SketchVariant = Capability.lazy('SketchVariant', () => import('./sketch-variant'));
|
|
8
|
+
export const TldrawSettings = Capability.lazy('TldrawSettings', () => import('./settings'));
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import * as Effect from 'effect/Effect';
|
|
6
|
+
|
|
7
|
+
import { Capability } from '@dxos/app-framework';
|
|
8
|
+
import { AppCapabilities } from '@dxos/app-toolkit';
|
|
9
|
+
import { createKvsStore } from '@dxos/effect';
|
|
10
|
+
|
|
11
|
+
import { meta } from '#meta';
|
|
12
|
+
import { Settings, TldrawCapabilities } from '#types';
|
|
13
|
+
|
|
14
|
+
export default Capability.makeModule(() =>
|
|
15
|
+
Effect.sync(() => {
|
|
16
|
+
const settingsAtom = createKvsStore({
|
|
17
|
+
key: meta.profile.key,
|
|
18
|
+
schema: Settings.Settings,
|
|
19
|
+
defaultValue: () => ({}),
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
return [
|
|
23
|
+
Capability.contributes(TldrawCapabilities.Settings, settingsAtom),
|
|
24
|
+
Capability.contributes(AppCapabilities.Settings, {
|
|
25
|
+
prefix: meta.profile.key,
|
|
26
|
+
schema: Settings.Settings,
|
|
27
|
+
atom: settingsAtom,
|
|
28
|
+
}),
|
|
29
|
+
];
|
|
30
|
+
}),
|
|
31
|
+
);
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2026 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import * as Effect from 'effect/Effect';
|
|
6
|
+
|
|
7
|
+
import { Capability } from '@dxos/app-framework';
|
|
8
|
+
import { Type } from '@dxos/echo';
|
|
9
|
+
import { IllustratorCapabilities, type SketchVariant } from '@dxos/plugin-illustrator/types';
|
|
10
|
+
|
|
11
|
+
import { TldrawArticle, TldrawCard } from '#containers';
|
|
12
|
+
import { tldrawBuilder } from '#model';
|
|
13
|
+
|
|
14
|
+
import { Tldraw } from '../types';
|
|
15
|
+
|
|
16
|
+
const variant: SketchVariant = {
|
|
17
|
+
id: Type.getTypename(Tldraw.Canvas),
|
|
18
|
+
label: 'tldraw',
|
|
19
|
+
icon: 'ph--compass-tool--regular',
|
|
20
|
+
canvasType: Tldraw.Canvas,
|
|
21
|
+
createCanvas: () => Effect.sync(() => Tldraw.makeCanvas()),
|
|
22
|
+
builder: tldrawBuilder,
|
|
23
|
+
card: TldrawCard,
|
|
24
|
+
article: TldrawArticle,
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export default Capability.makeModule(() =>
|
|
28
|
+
Effect.succeed(Capability.contributes(IllustratorCapabilities.VariantProvider, variant)),
|
|
29
|
+
);
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
6
|
+
import React, { useState } from 'react';
|
|
7
|
+
|
|
8
|
+
import { createObject } from '@dxos/echo-client';
|
|
9
|
+
import { Button, Panel, Toolbar } from '@dxos/react-ui';
|
|
10
|
+
import { withLayout, withTheme } from '@dxos/react-ui/testing';
|
|
11
|
+
|
|
12
|
+
import { TldrawBuilder } from '#model';
|
|
13
|
+
import { data } from '#testing';
|
|
14
|
+
import { Tldraw } from '#types';
|
|
15
|
+
|
|
16
|
+
import { migrateCanvas } from '../../migrations';
|
|
17
|
+
import { CanvasComponent } from './Canvas';
|
|
18
|
+
|
|
19
|
+
const DefaultStory = () => {
|
|
20
|
+
const [canvas, setCanvas] = useState(createObject(Tldraw.makeCanvas({ content: data.v2 })));
|
|
21
|
+
|
|
22
|
+
const handleClear = () => {
|
|
23
|
+
setCanvas(createObject(Tldraw.makeCanvas()));
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const handleCreate = () => {
|
|
27
|
+
const canvas = createObject(Tldraw.makeCanvas({ content: data.v2 }));
|
|
28
|
+
console.log(JSON.stringify(canvas, undefined, 2));
|
|
29
|
+
setCanvas(canvas);
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const handleMigrate = async () => {
|
|
33
|
+
const content = await migrateCanvas(data.v1);
|
|
34
|
+
setCanvas(createObject(Tldraw.makeCanvas({ content })));
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<Panel.Root>
|
|
39
|
+
<Panel.Toolbar asChild>
|
|
40
|
+
<Toolbar.Root>
|
|
41
|
+
<Button variant='primary' onClick={handleClear}>
|
|
42
|
+
Clear
|
|
43
|
+
</Button>
|
|
44
|
+
<Button variant='ghost' onClick={handleCreate}>
|
|
45
|
+
Create
|
|
46
|
+
</Button>
|
|
47
|
+
<Button variant='ghost' onClick={handleMigrate}>
|
|
48
|
+
Load V1 Sample
|
|
49
|
+
</Button>
|
|
50
|
+
</Toolbar.Root>
|
|
51
|
+
</Panel.Toolbar>
|
|
52
|
+
<Panel.Content asChild>
|
|
53
|
+
<CanvasComponent classNames='dx-attention-surface' canvas={canvas} assetsBaseUrl={null} autoCenter />
|
|
54
|
+
</Panel.Content>
|
|
55
|
+
</Panel.Root>
|
|
56
|
+
);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const meta = {
|
|
60
|
+
title: 'plugins/plugin-tldraw/components/Canvas',
|
|
61
|
+
render: DefaultStory,
|
|
62
|
+
decorators: [withTheme(), withLayout({ layout: 'fullscreen' })],
|
|
63
|
+
parameters: {
|
|
64
|
+
layout: 'fullscreen',
|
|
65
|
+
},
|
|
66
|
+
} satisfies Meta<typeof DefaultStory>;
|
|
67
|
+
|
|
68
|
+
export default meta;
|
|
69
|
+
|
|
70
|
+
type Story = StoryObj<typeof meta>;
|
|
71
|
+
|
|
72
|
+
export const Default: Story = {};
|
|
73
|
+
|
|
74
|
+
const BuilderStory = () => {
|
|
75
|
+
const [canvas] = useState(() =>
|
|
76
|
+
createObject(
|
|
77
|
+
Tldraw.makeCanvas({
|
|
78
|
+
content: new TldrawBuilder()
|
|
79
|
+
.rectangle({ id: 'a', x: 0, y: 0, text: 'DXOS', color: 'blue', fill: 'solid' })
|
|
80
|
+
.ellipse({ id: 'b', x: 360, y: 0, text: 'ECHO', color: 'green' })
|
|
81
|
+
.geo('star', { id: 'c', x: 180, y: 280, text: 'EDGE', color: 'yellow' })
|
|
82
|
+
.text({ x: 0, y: 480, text: 'Built with TldrawBuilder', font: 'mono' })
|
|
83
|
+
.arrow({ from: 'a', to: 'b', text: 'syncs' })
|
|
84
|
+
.arrow({ from: 'b', to: 'c' })
|
|
85
|
+
.build(),
|
|
86
|
+
}),
|
|
87
|
+
),
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
return (
|
|
91
|
+
<Panel.Root>
|
|
92
|
+
<Panel.Content asChild>
|
|
93
|
+
<CanvasComponent classNames='dx-attention-surface' canvas={canvas} assetsBaseUrl={null} autoCenter />
|
|
94
|
+
</Panel.Content>
|
|
95
|
+
</Panel.Root>
|
|
96
|
+
);
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
export const Builder: Story = {
|
|
100
|
+
render: BuilderStory,
|
|
101
|
+
};
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import '@tldraw/tldraw/tldraw.css';
|
|
6
|
+
import './theme.css';
|
|
7
|
+
|
|
8
|
+
import { getAssetUrls } from '@tldraw/assets/selfHosted';
|
|
9
|
+
import { type TLEventInfo, type TLGridProps, type TLInstance } from '@tldraw/editor';
|
|
10
|
+
import { DefaultToolbar, type Editor, Tldraw as TldrawEditor, type TLUiAssetUrlOverrides } from '@tldraw/tldraw';
|
|
11
|
+
import defaultsDeep from 'lodash.defaultsdeep';
|
|
12
|
+
import React, { type FC, useEffect, useMemo, useState } from 'react';
|
|
13
|
+
import { useResizeDetector } from 'react-resize-detector';
|
|
14
|
+
|
|
15
|
+
import { Obj } from '@dxos/echo';
|
|
16
|
+
import { useMergeRefs } from '@dxos/react-hooks';
|
|
17
|
+
import { composable, composableProps } from '@dxos/react-ui';
|
|
18
|
+
|
|
19
|
+
import { useStoreAdapter } from '#hooks';
|
|
20
|
+
import { type Settings, type Tldraw } from '#types';
|
|
21
|
+
|
|
22
|
+
import { handleSnap } from '../actions';
|
|
23
|
+
import { CustomMenu, CustomStylePanel, DefaultToolbarContent, DottedGrid, MeshGrid } from '../custom';
|
|
24
|
+
|
|
25
|
+
const threadToolId = 'thread';
|
|
26
|
+
|
|
27
|
+
const gridComponents: Record<Settings.GridType, FC<TLGridProps>> = {
|
|
28
|
+
mesh: MeshGrid,
|
|
29
|
+
dotted: DottedGrid,
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export type CanvasProps = {
|
|
33
|
+
canvas: Tldraw.Canvas;
|
|
34
|
+
readonly?: boolean;
|
|
35
|
+
autoCenter?: boolean;
|
|
36
|
+
hideUi?: boolean;
|
|
37
|
+
assetsBaseUrl?: string | null;
|
|
38
|
+
settings?: Settings.Settings;
|
|
39
|
+
onThreadCreate?: () => void;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export const CanvasComponent = composable<HTMLDivElement, CanvasProps>(
|
|
43
|
+
(
|
|
44
|
+
{
|
|
45
|
+
canvas,
|
|
46
|
+
readonly = false,
|
|
47
|
+
autoCenter = true,
|
|
48
|
+
hideUi = false,
|
|
49
|
+
assetsBaseUrl = '/assets/plugin-tldraw',
|
|
50
|
+
settings,
|
|
51
|
+
onThreadCreate,
|
|
52
|
+
...props
|
|
53
|
+
},
|
|
54
|
+
forwardedRef,
|
|
55
|
+
) => {
|
|
56
|
+
const adapter = useStoreAdapter(canvas);
|
|
57
|
+
const [editor, setEditor] = useState<Editor>();
|
|
58
|
+
|
|
59
|
+
// Focus.
|
|
60
|
+
useEffect(() => {
|
|
61
|
+
hideUi ? editor?.blur() : editor?.focus();
|
|
62
|
+
}, [hideUi, editor]);
|
|
63
|
+
|
|
64
|
+
// Editor State.
|
|
65
|
+
useEffect(() => {
|
|
66
|
+
if (!settings) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const cleanup = editor?.store.listen(
|
|
71
|
+
({ changes: { updated } }) => {
|
|
72
|
+
for (const [id, [from, to]] of Object.entries(updated)) {
|
|
73
|
+
if (id === 'instance:instance') {
|
|
74
|
+
const fromInstance = from as TLInstance;
|
|
75
|
+
const toInstance = to as TLInstance;
|
|
76
|
+
if (fromInstance.isGridMode !== toInstance.isGridMode) {
|
|
77
|
+
settings.showGrid = toInstance.isGridMode;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
{ source: 'user', scope: 'all' },
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
// TODO(burdon): Combine.
|
|
86
|
+
return () => cleanup?.();
|
|
87
|
+
}, [settings, editor]);
|
|
88
|
+
|
|
89
|
+
// Editor events.
|
|
90
|
+
useEffect(() => {
|
|
91
|
+
// https://tldraw.dev/examples/editor-api/canvas-events
|
|
92
|
+
const handleEvent = ({ type, name }: TLEventInfo) => {
|
|
93
|
+
if (type === 'pointer' && name === 'pointer_up') {
|
|
94
|
+
// Defer: the 'event' stream fires before the active tool commits its shape to the
|
|
95
|
+
// store, so an immediate save flushes one gesture behind (losing the final stroke).
|
|
96
|
+
setTimeout(() => adapter.save());
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
editor?.on('event', handleEvent);
|
|
101
|
+
return () => {
|
|
102
|
+
editor?.off('event', handleEvent);
|
|
103
|
+
};
|
|
104
|
+
}, [adapter, editor]);
|
|
105
|
+
|
|
106
|
+
// UI state.
|
|
107
|
+
useEffect(() => {
|
|
108
|
+
if (editor) {
|
|
109
|
+
editor.user.updateUserPreferences({
|
|
110
|
+
// TODO(burdon): Adjust snap threshold.
|
|
111
|
+
isSnapMode: true,
|
|
112
|
+
});
|
|
113
|
+
editor.updateInstanceState({
|
|
114
|
+
isGridMode: settings?.showGrid !== false && !hideUi,
|
|
115
|
+
isReadonly: readonly || hideUi,
|
|
116
|
+
});
|
|
117
|
+
if (readonly || hideUi) {
|
|
118
|
+
editor.setCurrentTool('hand');
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}, [editor, settings, hideUi, readonly]);
|
|
122
|
+
|
|
123
|
+
// Zoom to fit.
|
|
124
|
+
const { ref: resizeRef, width = 0, height } = useResizeDetector();
|
|
125
|
+
const containerRef = useMergeRefs([forwardedRef, resizeRef]);
|
|
126
|
+
const [ready, setReady] = useState(!autoCenter);
|
|
127
|
+
useEffect(() => {
|
|
128
|
+
if (!editor || !adapter || width === undefined || height === undefined) {
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const isLocked = readonly || hideUi;
|
|
133
|
+
|
|
134
|
+
// Unlock to allow programmatic camera positioning.
|
|
135
|
+
editor.setCameraOptions({ isLocked: false });
|
|
136
|
+
|
|
137
|
+
// Set frame so that top left of grid is inset with our border (if no content).
|
|
138
|
+
editor.setCamera({ x: -1, y: -1, z: 1 }, { animation: { duration: 0 } });
|
|
139
|
+
editor.resetZoom();
|
|
140
|
+
|
|
141
|
+
setReady(true);
|
|
142
|
+
if (!autoCenter) {
|
|
143
|
+
editor.setCameraOptions({ isLocked: isLocked });
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const zoom = () => {
|
|
148
|
+
zoomToFit(editor, width, height, false);
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
zoom();
|
|
152
|
+
editor.setCameraOptions({ isLocked: isLocked });
|
|
153
|
+
|
|
154
|
+
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
155
|
+
const onUpdate = () => {
|
|
156
|
+
clearTimeout(timer);
|
|
157
|
+
timer = setTimeout(() => {
|
|
158
|
+
editor.setCameraOptions({ isLocked: false });
|
|
159
|
+
zoom();
|
|
160
|
+
editor.setCameraOptions({ isLocked: isLocked });
|
|
161
|
+
}, 200);
|
|
162
|
+
};
|
|
163
|
+
const subscription = isLocked ? adapter.store?.listen(() => onUpdate(), { scope: 'document' }) : undefined;
|
|
164
|
+
return () => {
|
|
165
|
+
clearTimeout(timer);
|
|
166
|
+
subscription?.();
|
|
167
|
+
};
|
|
168
|
+
}, [editor, adapter, width, height, autoCenter, readonly, hideUi]);
|
|
169
|
+
|
|
170
|
+
// NOTE: Currently copying assets to composer-app public/assets/tldraw.
|
|
171
|
+
// https://tldraw.dev/installation#Self-hosting-static-assets
|
|
172
|
+
const assetUrls: TLUiAssetUrlOverrides = useMemo(() => {
|
|
173
|
+
if (!assetsBaseUrl) {
|
|
174
|
+
return undefined;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return defaultsDeep(
|
|
178
|
+
{
|
|
179
|
+
// Change default draw font.
|
|
180
|
+
// TODO(burdon): Change icon to match font.
|
|
181
|
+
fonts: { draw: `${assetsBaseUrl}/fonts/Montserrat-Regular.woff2` },
|
|
182
|
+
icons: {
|
|
183
|
+
'thread-icon': `${assetsBaseUrl}/icons/icon/chat-teardrop-text.svg`,
|
|
184
|
+
},
|
|
185
|
+
},
|
|
186
|
+
getAssetUrls({ baseUrl: assetsBaseUrl }),
|
|
187
|
+
);
|
|
188
|
+
}, [assetsBaseUrl]);
|
|
189
|
+
|
|
190
|
+
const overrides = useMemo(
|
|
191
|
+
() => ({
|
|
192
|
+
actions: (_editor: Editor, actions: any, _helpers: any) => ({
|
|
193
|
+
...actions,
|
|
194
|
+
snap: {
|
|
195
|
+
id: 'grid-snap',
|
|
196
|
+
label: 'Snap',
|
|
197
|
+
kbd: 's',
|
|
198
|
+
icon: 'horizontal-align-middle',
|
|
199
|
+
onSelect: () => {
|
|
200
|
+
void handleSnap(canvas);
|
|
201
|
+
},
|
|
202
|
+
},
|
|
203
|
+
}),
|
|
204
|
+
tools: (_editor: Editor, tools: any) => {
|
|
205
|
+
const newTools = { ...tools };
|
|
206
|
+
newTools[threadToolId] = {
|
|
207
|
+
id: threadToolId,
|
|
208
|
+
icon: 'thread-icon',
|
|
209
|
+
label: 'Comment', // TODO(Zan): Use translation lookup here.
|
|
210
|
+
onSelect: () => onThreadCreate?.(),
|
|
211
|
+
};
|
|
212
|
+
return newTools;
|
|
213
|
+
},
|
|
214
|
+
}),
|
|
215
|
+
[canvas, onThreadCreate],
|
|
216
|
+
);
|
|
217
|
+
|
|
218
|
+
const components = useMemo(
|
|
219
|
+
() => ({
|
|
220
|
+
DebugPanel: null,
|
|
221
|
+
Grid: gridComponents[settings?.gridType ?? 'mesh'],
|
|
222
|
+
HelpMenu: null,
|
|
223
|
+
MenuPanel: CustomMenu,
|
|
224
|
+
NavigationPanel: null,
|
|
225
|
+
StylePanel: CustomStylePanel,
|
|
226
|
+
TopPanel: null,
|
|
227
|
+
ZoomMenu: null,
|
|
228
|
+
Toolbar: (props: any) => (
|
|
229
|
+
<DefaultToolbar {...props}>
|
|
230
|
+
<DefaultToolbarContent toolsToSplice={[{ toolId: 'thread', position: 8 }]} />
|
|
231
|
+
</DefaultToolbar>
|
|
232
|
+
),
|
|
233
|
+
}),
|
|
234
|
+
[settings],
|
|
235
|
+
);
|
|
236
|
+
|
|
237
|
+
if (!adapter.store) {
|
|
238
|
+
return null;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
return (
|
|
242
|
+
<div
|
|
243
|
+
{...composableProps(props, { classNames: 'dx-expander' })}
|
|
244
|
+
style={{ visibility: ready ? 'visible' : 'hidden' }}
|
|
245
|
+
ref={containerRef}
|
|
246
|
+
>
|
|
247
|
+
<TldrawEditor
|
|
248
|
+
// Rebind when the adapter recreates its store (e.g. an effect-churn reopen) — a stale
|
|
249
|
+
// binding draws into an orphaned store whose change listeners are gone, so saves no-op.
|
|
250
|
+
key={`${Obj.getURI(canvas)}:${adapter.store.id}`}
|
|
251
|
+
store={adapter.store}
|
|
252
|
+
hideUi={hideUi}
|
|
253
|
+
inferDarkMode
|
|
254
|
+
className='outline-hidden!'
|
|
255
|
+
maxAssetSize={1024 * 1024}
|
|
256
|
+
assetUrls={assetUrls}
|
|
257
|
+
overrides={overrides}
|
|
258
|
+
components={components}
|
|
259
|
+
onMount={setEditor}
|
|
260
|
+
/>
|
|
261
|
+
</div>
|
|
262
|
+
);
|
|
263
|
+
},
|
|
264
|
+
);
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Fit content within the viewport and center it, zooming out for oversized content but never
|
|
268
|
+
* magnifying past 100% (zoom is capped at 1).
|
|
269
|
+
*/
|
|
270
|
+
const zoomToFit = (editor: Editor, width: number, height: number, animate = true) => {
|
|
271
|
+
const commonBounds = editor.getCurrentPageBounds();
|
|
272
|
+
if (width && height && commonBounds?.width && commonBounds?.height) {
|
|
273
|
+
const padding = 60;
|
|
274
|
+
// Zoom out to fit oversized content; cap at 1 so small content is never zoomed in.
|
|
275
|
+
const zoom = Math.min(1, (width - padding) / commonBounds.width, (height - padding) / commonBounds.height);
|
|
276
|
+
const center = {
|
|
277
|
+
x: (width - commonBounds.width * zoom) / 2 / zoom - commonBounds.minX,
|
|
278
|
+
y: (height - commonBounds.height * zoom) / 2 / zoom - commonBounds.minY,
|
|
279
|
+
z: zoom,
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
editor.setCamera(center, animate ? { animation: { duration: 250 } } : undefined);
|
|
283
|
+
}
|
|
284
|
+
};
|