@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.
Files changed (401) hide show
  1. package/LICENSE +105 -0
  2. package/PLUGIN.mdl +412 -0
  3. package/README.md +3 -0
  4. package/dist/assets/embed-icons/codepen.png +0 -0
  5. package/dist/assets/embed-icons/codesandbox.png +0 -0
  6. package/dist/assets/embed-icons/desmos.png +0 -0
  7. package/dist/assets/embed-icons/excalidraw.png +0 -0
  8. package/dist/assets/embed-icons/felt.png +0 -0
  9. package/dist/assets/embed-icons/figma.png +0 -0
  10. package/dist/assets/embed-icons/github_gist.png +0 -0
  11. package/dist/assets/embed-icons/google_calendar.png +0 -0
  12. package/dist/assets/embed-icons/google_maps.png +0 -0
  13. package/dist/assets/embed-icons/google_slides.png +0 -0
  14. package/dist/assets/embed-icons/observable.png +0 -0
  15. package/dist/assets/embed-icons/replit.png +0 -0
  16. package/dist/assets/embed-icons/scratch.png +0 -0
  17. package/dist/assets/embed-icons/spotify.png +0 -0
  18. package/dist/assets/embed-icons/tldraw.png +0 -0
  19. package/dist/assets/embed-icons/val_town.png +0 -0
  20. package/dist/assets/embed-icons/vimeo.png +0 -0
  21. package/dist/assets/embed-icons/youtube.png +0 -0
  22. package/dist/assets/fonts/IBMPlexMono-Medium.woff2 +0 -0
  23. package/dist/assets/fonts/IBMPlexSans-Medium.woff2 +0 -0
  24. package/dist/assets/fonts/IBMPlexSerif-Medium.woff2 +0 -0
  25. package/dist/assets/fonts/Montserrat-Regular.woff2 +0 -0
  26. package/dist/assets/fonts/Shantell_Sans-Tldrawish.woff2 +0 -0
  27. package/dist/assets/icons/icon/0_merged.svg +1 -0
  28. package/dist/assets/icons/icon/align-bottom.svg +1 -0
  29. package/dist/assets/icons/icon/align-center-horizontal.svg +1 -0
  30. package/dist/assets/icons/icon/align-center-vertical.svg +1 -0
  31. package/dist/assets/icons/icon/align-left.svg +1 -0
  32. package/dist/assets/icons/icon/align-right.svg +1 -0
  33. package/dist/assets/icons/icon/align-top.svg +1 -0
  34. package/dist/assets/icons/icon/arrow-left.svg +1 -0
  35. package/dist/assets/icons/icon/arrowhead-arrow.svg +1 -0
  36. package/dist/assets/icons/icon/arrowhead-bar.svg +1 -0
  37. package/dist/assets/icons/icon/arrowhead-diamond.svg +1 -0
  38. package/dist/assets/icons/icon/arrowhead-dot.svg +1 -0
  39. package/dist/assets/icons/icon/arrowhead-none.svg +1 -0
  40. package/dist/assets/icons/icon/arrowhead-square.svg +1 -0
  41. package/dist/assets/icons/icon/arrowhead-triangle-inverted.svg +1 -0
  42. package/dist/assets/icons/icon/arrowhead-triangle.svg +1 -0
  43. package/dist/assets/icons/icon/blob.svg +1 -0
  44. package/dist/assets/icons/icon/bring-forward.svg +1 -0
  45. package/dist/assets/icons/icon/bring-to-front.svg +1 -0
  46. package/dist/assets/icons/icon/broken.svg +1 -0
  47. package/dist/assets/icons/icon/chat-teardrop-text.svg +1 -0
  48. package/dist/assets/icons/icon/check-circle.svg +1 -0
  49. package/dist/assets/icons/icon/check.svg +1 -0
  50. package/dist/assets/icons/icon/chevron-down.svg +1 -0
  51. package/dist/assets/icons/icon/chevron-left.svg +1 -0
  52. package/dist/assets/icons/icon/chevron-right.svg +1 -0
  53. package/dist/assets/icons/icon/chevron-up.svg +1 -0
  54. package/dist/assets/icons/icon/chevrons-ne.svg +1 -0
  55. package/dist/assets/icons/icon/chevrons-sw.svg +1 -0
  56. package/dist/assets/icons/icon/clipboard-copied.svg +1 -0
  57. package/dist/assets/icons/icon/clipboard-copy.svg +1 -0
  58. package/dist/assets/icons/icon/color.svg +1 -0
  59. package/dist/assets/icons/icon/cross-2.svg +1 -0
  60. package/dist/assets/icons/icon/cross-circle.svg +1 -0
  61. package/dist/assets/icons/icon/dash-dashed.svg +1 -0
  62. package/dist/assets/icons/icon/dash-dotted.svg +1 -0
  63. package/dist/assets/icons/icon/dash-draw.svg +1 -0
  64. package/dist/assets/icons/icon/dash-solid.svg +1 -0
  65. package/dist/assets/icons/icon/disconnected.svg +1 -0
  66. package/dist/assets/icons/icon/discord.svg +1 -0
  67. package/dist/assets/icons/icon/distribute-horizontal.svg +1 -0
  68. package/dist/assets/icons/icon/distribute-vertical.svg +1 -0
  69. package/dist/assets/icons/icon/dot.svg +1 -0
  70. package/dist/assets/icons/icon/dots-horizontal.svg +1 -0
  71. package/dist/assets/icons/icon/dots-vertical.svg +1 -0
  72. package/dist/assets/icons/icon/drag-handle-dots.svg +1 -0
  73. package/dist/assets/icons/icon/duplicate.svg +1 -0
  74. package/dist/assets/icons/icon/edit.svg +1 -0
  75. package/dist/assets/icons/icon/external-link.svg +1 -0
  76. package/dist/assets/icons/icon/fill-fill.svg +1 -0
  77. package/dist/assets/icons/icon/fill-none.svg +1 -0
  78. package/dist/assets/icons/icon/fill-pattern.svg +1 -0
  79. package/dist/assets/icons/icon/fill-semi.svg +1 -0
  80. package/dist/assets/icons/icon/fill-solid.svg +1 -0
  81. package/dist/assets/icons/icon/follow.svg +1 -0
  82. package/dist/assets/icons/icon/following.svg +1 -0
  83. package/dist/assets/icons/icon/font-draw.svg +8 -0
  84. package/dist/assets/icons/icon/font-mono.svg +1 -0
  85. package/dist/assets/icons/icon/font-sans.svg +1 -0
  86. package/dist/assets/icons/icon/font-serif.svg +1 -0
  87. package/dist/assets/icons/icon/geo-arrow-down.svg +1 -0
  88. package/dist/assets/icons/icon/geo-arrow-left.svg +1 -0
  89. package/dist/assets/icons/icon/geo-arrow-right.svg +1 -0
  90. package/dist/assets/icons/icon/geo-arrow-up.svg +1 -0
  91. package/dist/assets/icons/icon/geo-check-box.svg +1 -0
  92. package/dist/assets/icons/icon/geo-cloud.svg +1 -0
  93. package/dist/assets/icons/icon/geo-diamond.svg +1 -0
  94. package/dist/assets/icons/icon/geo-ellipse.svg +1 -0
  95. package/dist/assets/icons/icon/geo-heart.svg +1 -0
  96. package/dist/assets/icons/icon/geo-hexagon.svg +1 -0
  97. package/dist/assets/icons/icon/geo-octagon.svg +1 -0
  98. package/dist/assets/icons/icon/geo-oval.svg +1 -0
  99. package/dist/assets/icons/icon/geo-pentagon.svg +1 -0
  100. package/dist/assets/icons/icon/geo-rectangle.svg +1 -0
  101. package/dist/assets/icons/icon/geo-rhombus-2.svg +1 -0
  102. package/dist/assets/icons/icon/geo-rhombus.svg +1 -0
  103. package/dist/assets/icons/icon/geo-star.svg +1 -0
  104. package/dist/assets/icons/icon/geo-trapezoid.svg +1 -0
  105. package/dist/assets/icons/icon/geo-triangle.svg +1 -0
  106. package/dist/assets/icons/icon/geo-x-box.svg +1 -0
  107. package/dist/assets/icons/icon/github.svg +1 -0
  108. package/dist/assets/icons/icon/group.svg +1 -0
  109. package/dist/assets/icons/icon/horizontal-align-end.svg +1 -0
  110. package/dist/assets/icons/icon/horizontal-align-middle.svg +1 -0
  111. package/dist/assets/icons/icon/horizontal-align-start.svg +1 -0
  112. package/dist/assets/icons/icon/icon-names.json +143 -0
  113. package/dist/assets/icons/icon/info-circle.svg +1 -0
  114. package/dist/assets/icons/icon/leading.svg +1 -0
  115. package/dist/assets/icons/icon/link.svg +1 -0
  116. package/dist/assets/icons/icon/lock.svg +1 -0
  117. package/dist/assets/icons/icon/menu.svg +1 -0
  118. package/dist/assets/icons/icon/minus.svg +1 -0
  119. package/dist/assets/icons/icon/mixed.svg +1 -0
  120. package/dist/assets/icons/icon/pack.svg +1 -0
  121. package/dist/assets/icons/icon/plus.svg +1 -0
  122. package/dist/assets/icons/icon/question-mark-circle.svg +1 -0
  123. package/dist/assets/icons/icon/question-mark.svg +1 -0
  124. package/dist/assets/icons/icon/redo.svg +1 -0
  125. package/dist/assets/icons/icon/reset-zoom.svg +1 -0
  126. package/dist/assets/icons/icon/rotate-ccw.svg +1 -0
  127. package/dist/assets/icons/icon/rotate-cw.svg +1 -0
  128. package/dist/assets/icons/icon/send-backward.svg +1 -0
  129. package/dist/assets/icons/icon/send-to-back.svg +1 -0
  130. package/dist/assets/icons/icon/share-1.svg +1 -0
  131. package/dist/assets/icons/icon/size-extra-large.svg +1 -0
  132. package/dist/assets/icons/icon/size-large.svg +1 -0
  133. package/dist/assets/icons/icon/size-medium.svg +1 -0
  134. package/dist/assets/icons/icon/size-small.svg +1 -0
  135. package/dist/assets/icons/icon/spline-cubic.svg +1 -0
  136. package/dist/assets/icons/icon/spline-line.svg +1 -0
  137. package/dist/assets/icons/icon/stack-horizontal.svg +1 -0
  138. package/dist/assets/icons/icon/stack-vertical.svg +1 -0
  139. package/dist/assets/icons/icon/status-offline.svg +1 -0
  140. package/dist/assets/icons/icon/stretch-horizontal.svg +1 -0
  141. package/dist/assets/icons/icon/stretch-vertical.svg +1 -0
  142. package/dist/assets/icons/icon/text-align-center.svg +1 -0
  143. package/dist/assets/icons/icon/text-align-left.svg +1 -0
  144. package/dist/assets/icons/icon/text-align-right.svg +1 -0
  145. package/dist/assets/icons/icon/toggle-off.svg +1 -0
  146. package/dist/assets/icons/icon/toggle-on.svg +1 -0
  147. package/dist/assets/icons/icon/tool-arrow.svg +1 -0
  148. package/dist/assets/icons/icon/tool-eraser.svg +1 -0
  149. package/dist/assets/icons/icon/tool-frame.svg +1 -0
  150. package/dist/assets/icons/icon/tool-hand.svg +1 -0
  151. package/dist/assets/icons/icon/tool-highlight.svg +1 -0
  152. package/dist/assets/icons/icon/tool-laser.svg +1 -0
  153. package/dist/assets/icons/icon/tool-line.svg +1 -0
  154. package/dist/assets/icons/icon/tool-media.svg +1 -0
  155. package/dist/assets/icons/icon/tool-note.svg +1 -0
  156. package/dist/assets/icons/icon/tool-pencil.svg +1 -0
  157. package/dist/assets/icons/icon/tool-pointer.svg +1 -0
  158. package/dist/assets/icons/icon/tool-screenshot.svg +1 -0
  159. package/dist/assets/icons/icon/tool-text.svg +1 -0
  160. package/dist/assets/icons/icon/trash.svg +1 -0
  161. package/dist/assets/icons/icon/twitter.svg +1 -0
  162. package/dist/assets/icons/icon/undo.svg +1 -0
  163. package/dist/assets/icons/icon/ungroup.svg +1 -0
  164. package/dist/assets/icons/icon/unlock.svg +1 -0
  165. package/dist/assets/icons/icon/vertical-align-end.svg +1 -0
  166. package/dist/assets/icons/icon/vertical-align-middle.svg +1 -0
  167. package/dist/assets/icons/icon/vertical-align-start.svg +1 -0
  168. package/dist/assets/icons/icon/warning-triangle.svg +1 -0
  169. package/dist/assets/icons/icon/zoom-in.svg +1 -0
  170. package/dist/assets/icons/icon/zoom-out.svg +1 -0
  171. package/dist/assets/translations/ar.json +295 -0
  172. package/dist/assets/translations/ca.json +295 -0
  173. package/dist/assets/translations/cs.json +352 -0
  174. package/dist/assets/translations/da.json +160 -0
  175. package/dist/assets/translations/de.json +310 -0
  176. package/dist/assets/translations/en.json +1 -0
  177. package/dist/assets/translations/es.json +299 -0
  178. package/dist/assets/translations/fa.json +384 -0
  179. package/dist/assets/translations/fi.json +312 -0
  180. package/dist/assets/translations/fr.json +372 -0
  181. package/dist/assets/translations/gl.json +349 -0
  182. package/dist/assets/translations/he.json +86 -0
  183. package/dist/assets/translations/hi-in.json +290 -0
  184. package/dist/assets/translations/hr.json +358 -0
  185. package/dist/assets/translations/hu.json +365 -0
  186. package/dist/assets/translations/id.json +373 -0
  187. package/dist/assets/translations/it.json +330 -0
  188. package/dist/assets/translations/ja.json +351 -0
  189. package/dist/assets/translations/ko-kr.json +396 -0
  190. package/dist/assets/translations/ku.json +93 -0
  191. package/dist/assets/translations/languages.json +150 -0
  192. package/dist/assets/translations/main.json +396 -0
  193. package/dist/assets/translations/my.json +112 -0
  194. package/dist/assets/translations/ne.json +295 -0
  195. package/dist/assets/translations/no.json +62 -0
  196. package/dist/assets/translations/pl.json +113 -0
  197. package/dist/assets/translations/pt-br.json +333 -0
  198. package/dist/assets/translations/pt-pt.json +86 -0
  199. package/dist/assets/translations/ro.json +371 -0
  200. package/dist/assets/translations/ru.json +375 -0
  201. package/dist/assets/translations/sl.json +373 -0
  202. package/dist/assets/translations/sv.json +96 -0
  203. package/dist/assets/translations/te.json +112 -0
  204. package/dist/assets/translations/th.json +295 -0
  205. package/dist/assets/translations/tr.json +355 -0
  206. package/dist/assets/translations/uk.json +375 -0
  207. package/dist/assets/translations/vi.json +295 -0
  208. package/dist/assets/translations/zh-cn.json +386 -0
  209. package/dist/assets/translations/zh-tw.json +340 -0
  210. package/dist/lib/TldrawPlugin.mjs +22 -0
  211. package/dist/lib/TldrawPlugin.mjs.map +1 -0
  212. package/dist/lib/assets/plugin-tldraw-BDrn_mrV.css +24 -0
  213. package/dist/lib/capabilities.mjs +8 -0
  214. package/dist/lib/capabilities.mjs.map +1 -0
  215. package/dist/lib/chunk-TldrawArticle.mjs +60 -0
  216. package/dist/lib/chunk-TldrawArticle.mjs.map +1 -0
  217. package/dist/lib/chunk-TldrawCard.mjs +35 -0
  218. package/dist/lib/chunk-TldrawCard.mjs.map +1 -0
  219. package/dist/lib/chunk-meta.mjs +40 -0
  220. package/dist/lib/chunk-meta.mjs.map +1 -0
  221. package/dist/lib/chunk-settings.mjs +23 -0
  222. package/dist/lib/chunk-settings.mjs.map +1 -0
  223. package/dist/lib/chunk-sketch-variant.mjs +23 -0
  224. package/dist/lib/chunk-sketch-variant.mjs.map +1 -0
  225. package/dist/lib/chunk-types.mjs +73 -0
  226. package/dist/lib/chunk-types.mjs.map +1 -0
  227. package/dist/lib/components.mjs +443 -0
  228. package/dist/lib/components.mjs.map +1 -0
  229. package/dist/lib/containers.mjs +8 -0
  230. package/dist/lib/containers.mjs.map +1 -0
  231. package/dist/lib/hooks.mjs +116 -0
  232. package/dist/lib/hooks.mjs.map +1 -0
  233. package/dist/lib/index.mjs +4 -0
  234. package/dist/lib/meta.mjs +2 -0
  235. package/dist/lib/model.mjs +905 -0
  236. package/dist/lib/model.mjs.map +1 -0
  237. package/dist/lib/plugin.mjs +8 -0
  238. package/dist/lib/plugin.mjs.map +1 -0
  239. package/dist/lib/testing.mjs +246 -0
  240. package/dist/lib/testing.mjs.map +1 -0
  241. package/dist/lib/translations.mjs +10 -0
  242. package/dist/lib/translations.mjs.map +1 -0
  243. package/dist/lib/types.mjs +2 -0
  244. package/dist/types/dx.config.d.ts +28 -0
  245. package/dist/types/dx.config.d.ts.map +1 -0
  246. package/dist/types/src/TldrawPlugin.d.ts +4 -0
  247. package/dist/types/src/TldrawPlugin.d.ts.map +1 -0
  248. package/dist/types/src/capabilities/index.d.ts +4 -0
  249. package/dist/types/src/capabilities/index.d.ts.map +1 -0
  250. package/dist/types/src/capabilities/settings.d.ts +5 -0
  251. package/dist/types/src/capabilities/settings.d.ts.map +1 -0
  252. package/dist/types/src/capabilities/sketch-variant.d.ts +5 -0
  253. package/dist/types/src/capabilities/sketch-variant.d.ts.map +1 -0
  254. package/dist/types/src/components/Canvas/Canvas.d.ts +17 -0
  255. package/dist/types/src/components/Canvas/Canvas.d.ts.map +1 -0
  256. package/dist/types/src/components/Canvas/Canvas.stories.d.ts +15 -0
  257. package/dist/types/src/components/Canvas/Canvas.stories.d.ts.map +1 -0
  258. package/dist/types/src/components/Canvas/index.d.ts +4 -0
  259. package/dist/types/src/components/Canvas/index.d.ts.map +1 -0
  260. package/dist/types/src/components/actions.d.ts +6 -0
  261. package/dist/types/src/components/actions.d.ts.map +1 -0
  262. package/dist/types/src/components/custom/CustomGrid.d.ts +12 -0
  263. package/dist/types/src/components/custom/CustomGrid.d.ts.map +1 -0
  264. package/dist/types/src/components/custom/CustomMenu.d.ts +4 -0
  265. package/dist/types/src/components/custom/CustomMenu.d.ts.map +1 -0
  266. package/dist/types/src/components/custom/CustomStylePanel.d.ts +4 -0
  267. package/dist/types/src/components/custom/CustomStylePanel.d.ts.map +1 -0
  268. package/dist/types/src/components/custom/CustomToolbar.d.ts +10 -0
  269. package/dist/types/src/components/custom/CustomToolbar.d.ts.map +1 -0
  270. package/dist/types/src/components/custom/DottedGrid.d.ts +4 -0
  271. package/dist/types/src/components/custom/DottedGrid.d.ts.map +1 -0
  272. package/dist/types/src/components/custom/MeshGrid.d.ts +4 -0
  273. package/dist/types/src/components/custom/MeshGrid.d.ts.map +1 -0
  274. package/dist/types/src/components/custom/index.d.ts +6 -0
  275. package/dist/types/src/components/custom/index.d.ts.map +1 -0
  276. package/dist/types/src/components/index.d.ts +2 -0
  277. package/dist/types/src/components/index.d.ts.map +1 -0
  278. package/dist/types/src/containers/TldrawArticle/TldrawArticle.d.ts +8 -0
  279. package/dist/types/src/containers/TldrawArticle/TldrawArticle.d.ts.map +1 -0
  280. package/dist/types/src/containers/TldrawArticle/index.d.ts +2 -0
  281. package/dist/types/src/containers/TldrawArticle/index.d.ts.map +1 -0
  282. package/dist/types/src/containers/TldrawCard/TldrawCard.d.ts +9 -0
  283. package/dist/types/src/containers/TldrawCard/TldrawCard.d.ts.map +1 -0
  284. package/dist/types/src/containers/TldrawCard/TldrawCard.stories.d.ts +15 -0
  285. package/dist/types/src/containers/TldrawCard/TldrawCard.stories.d.ts.map +1 -0
  286. package/dist/types/src/containers/TldrawCard/index.d.ts +2 -0
  287. package/dist/types/src/containers/TldrawCard/index.d.ts.map +1 -0
  288. package/dist/types/src/containers/index.d.ts +4 -0
  289. package/dist/types/src/containers/index.d.ts.map +1 -0
  290. package/dist/types/src/hooks/adapter.d.ts +15 -0
  291. package/dist/types/src/hooks/adapter.d.ts.map +1 -0
  292. package/dist/types/src/hooks/adapter.test.d.ts +2 -0
  293. package/dist/types/src/hooks/adapter.test.d.ts.map +1 -0
  294. package/dist/types/src/hooks/index.d.ts +3 -0
  295. package/dist/types/src/hooks/index.d.ts.map +1 -0
  296. package/dist/types/src/hooks/useStoreAdapter.d.ts +11 -0
  297. package/dist/types/src/hooks/useStoreAdapter.d.ts.map +1 -0
  298. package/dist/types/src/index.d.ts +4 -0
  299. package/dist/types/src/index.d.ts.map +1 -0
  300. package/dist/types/src/meta.d.ts +33 -0
  301. package/dist/types/src/meta.d.ts.map +1 -0
  302. package/dist/types/src/migrations/index.d.ts +3 -0
  303. package/dist/types/src/migrations/index.d.ts.map +1 -0
  304. package/dist/types/src/migrations/migrate.d.ts +9 -0
  305. package/dist/types/src/migrations/migrate.d.ts.map +1 -0
  306. package/dist/types/src/migrations/schema.d.ts +7 -0
  307. package/dist/types/src/migrations/schema.d.ts.map +1 -0
  308. package/dist/types/src/model/TldrawBuilder.d.ts +110 -0
  309. package/dist/types/src/model/TldrawBuilder.d.ts.map +1 -0
  310. package/dist/types/src/model/TldrawBuilder.test.d.ts +2 -0
  311. package/dist/types/src/model/TldrawBuilder.test.d.ts.map +1 -0
  312. package/dist/types/src/model/apply.d.ts +5 -0
  313. package/dist/types/src/model/apply.d.ts.map +1 -0
  314. package/dist/types/src/model/builder.d.ts +3 -0
  315. package/dist/types/src/model/builder.d.ts.map +1 -0
  316. package/dist/types/src/model/index.d.ts +6 -0
  317. package/dist/types/src/model/index.d.ts.map +1 -0
  318. package/dist/types/src/model/read.d.ts +8 -0
  319. package/dist/types/src/model/read.d.ts.map +1 -0
  320. package/dist/types/src/model/render.d.ts +27 -0
  321. package/dist/types/src/model/render.d.ts.map +1 -0
  322. package/dist/types/src/model/scene.test.d.ts +2 -0
  323. package/dist/types/src/model/scene.test.d.ts.map +1 -0
  324. package/dist/types/src/plugin.d.ts +3 -0
  325. package/dist/types/src/plugin.d.ts.map +1 -0
  326. package/dist/types/src/testing/data.d.ts +242 -0
  327. package/dist/types/src/testing/data.d.ts.map +1 -0
  328. package/dist/types/src/testing/index.d.ts +2 -0
  329. package/dist/types/src/testing/index.d.ts.map +1 -0
  330. package/dist/types/src/testing/schema.d.ts +7 -0
  331. package/dist/types/src/testing/schema.d.ts.map +1 -0
  332. package/dist/types/src/translations.d.ts +9 -0
  333. package/dist/types/src/translations.d.ts.map +1 -0
  334. package/dist/types/src/types/Settings.d.ts +10 -0
  335. package/dist/types/src/types/Settings.d.ts.map +1 -0
  336. package/dist/types/src/types/Tldraw.d.ts +25 -0
  337. package/dist/types/src/types/Tldraw.d.ts.map +1 -0
  338. package/dist/types/src/types/TldrawCapabilities.d.ts +4 -0
  339. package/dist/types/src/types/TldrawCapabilities.d.ts.map +1 -0
  340. package/dist/types/src/types/index.d.ts +4 -0
  341. package/dist/types/src/types/index.d.ts.map +1 -0
  342. package/dist/types/src/types/types.d.ts +6 -0
  343. package/dist/types/src/types/types.d.ts.map +1 -0
  344. package/dist/types/src/variant.test.d.ts +2 -0
  345. package/dist/types/src/variant.test.d.ts.map +1 -0
  346. package/dist/types/tsconfig.tsbuildinfo +1 -0
  347. package/dx.config.ts +35 -0
  348. package/package.json +157 -0
  349. package/src/TldrawPlugin.tsx +31 -0
  350. package/src/capabilities/index.ts +8 -0
  351. package/src/capabilities/settings.ts +31 -0
  352. package/src/capabilities/sketch-variant.ts +29 -0
  353. package/src/components/Canvas/Canvas.stories.tsx +101 -0
  354. package/src/components/Canvas/Canvas.tsx +284 -0
  355. package/src/components/Canvas/index.ts +9 -0
  356. package/src/components/Canvas/theme.css +23 -0
  357. package/src/components/actions.ts +42 -0
  358. package/src/components/custom/CustomGrid.tsx +61 -0
  359. package/src/components/custom/CustomMenu.tsx +31 -0
  360. package/src/components/custom/CustomStylePanel.tsx +38 -0
  361. package/src/components/custom/CustomToolbar.tsx +88 -0
  362. package/src/components/custom/DottedGrid.tsx +53 -0
  363. package/src/components/custom/MeshGrid.tsx +47 -0
  364. package/src/components/custom/index.ts +9 -0
  365. package/src/components/index.ts +5 -0
  366. package/src/containers/TldrawArticle/TldrawArticle.tsx +78 -0
  367. package/src/containers/TldrawArticle/index.ts +5 -0
  368. package/src/containers/TldrawCard/TldrawCard.stories.tsx +53 -0
  369. package/src/containers/TldrawCard/TldrawCard.tsx +32 -0
  370. package/src/containers/TldrawCard/index.ts +5 -0
  371. package/src/containers/index.ts +8 -0
  372. package/src/hooks/adapter.test.ts +20 -0
  373. package/src/hooks/adapter.ts +83 -0
  374. package/src/hooks/index.ts +6 -0
  375. package/src/hooks/useStoreAdapter.ts +55 -0
  376. package/src/index.ts +7 -0
  377. package/src/meta.ts +9 -0
  378. package/src/migrations/index.ts +6 -0
  379. package/src/migrations/migrate.ts +49 -0
  380. package/src/migrations/schema.ts +98 -0
  381. package/src/model/TldrawBuilder.test.ts +72 -0
  382. package/src/model/TldrawBuilder.ts +443 -0
  383. package/src/model/apply.ts +132 -0
  384. package/src/model/builder.ts +31 -0
  385. package/src/model/index.ts +9 -0
  386. package/src/model/read.ts +203 -0
  387. package/src/model/render.ts +191 -0
  388. package/src/model/scene.test.ts +222 -0
  389. package/src/plugin.ts +9 -0
  390. package/src/testing/data.ts +238 -0
  391. package/src/testing/index.ts +5 -0
  392. package/src/testing/schema.ts +98 -0
  393. package/src/translations.ts +18 -0
  394. package/src/types/Settings.ts +32 -0
  395. package/src/types/Tldraw.ts +38 -0
  396. package/src/types/TldrawCapabilities.ts +17 -0
  397. package/src/types/index.ts +8 -0
  398. package/src/types/types.ts +11 -0
  399. package/src/typings.d.ts +8 -0
  400. package/src/variant.test.ts +142 -0
  401. package/src/vite-env.d.ts +10 -0
@@ -0,0 +1,443 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ //
6
+ // A minimal, chainable builder for tldraw (`tldraw.com/2`) canvas records.
7
+ // Produces the record map stored in `Sketch.Canvas.content`. Used directly to seed
8
+ // stories/tests (`build()`), and by the scene renderer (`render.ts`) to compile the
9
+ // neutral scene DSL into records merged into an existing canvas (`records()`).
10
+ //
11
+
12
+ import { invariant } from '@dxos/invariant';
13
+
14
+ export type Color =
15
+ | 'black'
16
+ | 'grey'
17
+ | 'light-violet'
18
+ | 'violet'
19
+ | 'blue'
20
+ | 'light-blue'
21
+ | 'yellow'
22
+ | 'orange'
23
+ | 'green'
24
+ | 'light-green'
25
+ | 'light-red'
26
+ | 'red'
27
+ | 'white';
28
+
29
+ export type Fill = 'none' | 'semi' | 'solid' | 'pattern' | 'fill';
30
+ export type Dash = 'draw' | 'solid' | 'dashed' | 'dotted';
31
+ export type Size = 's' | 'm' | 'l' | 'xl';
32
+ export type Font = 'draw' | 'sans' | 'serif' | 'mono';
33
+ export type Align = 'start' | 'middle' | 'end';
34
+ export type Geo =
35
+ | 'rectangle'
36
+ | 'ellipse'
37
+ | 'triangle'
38
+ | 'diamond'
39
+ | 'pentagon'
40
+ | 'hexagon'
41
+ | 'octagon'
42
+ | 'star'
43
+ | 'rhombus'
44
+ | 'oval'
45
+ | 'cloud'
46
+ | 'heart';
47
+
48
+ /** Common style props shared by all shapes. */
49
+ export type StyleProps = {
50
+ color?: Color;
51
+ fill?: Fill;
52
+ dash?: Dash;
53
+ size?: Size;
54
+ font?: Font;
55
+ text?: string;
56
+ align?: Align;
57
+ };
58
+
59
+ /** Geometric shape (rectangle, ellipse, etc.). */
60
+ export type ShapeProps = StyleProps & {
61
+ /** Friendly handle used to link shapes (e.g. as an arrow target). */
62
+ id?: string;
63
+ x?: number;
64
+ y?: number;
65
+ w?: number;
66
+ h?: number;
67
+ rotation?: number;
68
+ };
69
+
70
+ /** Free-standing text label. */
71
+ export type TextProps = Omit<ShapeProps, 'h' | 'fill' | 'dash'>;
72
+
73
+ /** Connector (arrow/line) between two shapes (or between explicit points). */
74
+ export type ConnectorProps = Omit<StyleProps, 'align'> & {
75
+ id?: string;
76
+ /** Handle of the source shape. */
77
+ from?: string;
78
+ /** Handle of the target shape. */
79
+ to?: string;
80
+ /** Explicit start point (used when `from` is omitted). */
81
+ start?: Point;
82
+ /** Explicit end point (used when `to` is omitted). */
83
+ end?: Point;
84
+ };
85
+
86
+ /** Open or closed path through explicit points. */
87
+ export type PathProps = Omit<StyleProps, 'align'> & {
88
+ id?: string;
89
+ points: readonly Point[];
90
+ /** Smooth (cubic spline) instead of straight segments. */
91
+ smooth?: boolean;
92
+ /** Close the path back to the first point. */
93
+ closed?: boolean;
94
+ };
95
+
96
+ export type Point = { x: number; y: number };
97
+
98
+ export type CanvasContent = Record<string, any>;
99
+
100
+ export const PAGE_ID = 'page:page';
101
+ export const DOCUMENT_ID = 'document:document';
102
+
103
+ export type BuilderOptions = {
104
+ /** Fractional-index counter seed; pass a value past existing content when merging. */
105
+ indexStart?: number;
106
+ /** Per-shape meta, keyed by handle; merged into each generated record's `meta`. */
107
+ metaFor?: (handle: string) => Record<string, any>;
108
+ };
109
+
110
+ const DEFAULTS: {
111
+ color: Color;
112
+ fill: Fill;
113
+ dash: Dash;
114
+ size: Size;
115
+ font: Font;
116
+ align: Align;
117
+ w: number;
118
+ h: number;
119
+ } = {
120
+ color: 'black',
121
+ fill: 'none',
122
+ dash: 'draw',
123
+ size: 'm',
124
+ font: 'draw',
125
+ align: 'middle',
126
+ w: 160,
127
+ h: 120,
128
+ };
129
+
130
+ type Box = { id: string; x: number; y: number; w: number; h: number };
131
+
132
+ /**
133
+ * Chainable builder for sketch (tldraw) canvas content.
134
+ *
135
+ * @example
136
+ * ```ts
137
+ * const content = new TldrawBuilder()
138
+ * .rectangle({ id: 'a', x: 0, y: 0, text: 'DXOS', color: 'blue' })
139
+ * .ellipse({ id: 'b', x: 320, y: 0, text: 'ECHO', color: 'green' })
140
+ * .arrow({ from: 'a', to: 'b' })
141
+ * .build();
142
+ * ```
143
+ */
144
+ export class TldrawBuilder {
145
+ readonly #records: CanvasContent = {};
146
+ readonly #boxes = new Map<string, Box>();
147
+ readonly #metaFor?: (handle: string) => Record<string, any>;
148
+ #shapeCount = 0;
149
+ #bindingCount = 0;
150
+ #indexCount: number;
151
+
152
+ constructor(options: BuilderOptions = {}) {
153
+ this.#indexCount = options.indexStart ?? 0;
154
+ this.#metaFor = options.metaFor;
155
+ }
156
+
157
+ /**
158
+ * Register a shape that already exists on the canvas (its bounding box in canvas px)
159
+ * so connectors added by this builder can bind to it.
160
+ */
161
+ external(handle: string, box: { x: number; y: number; w: number; h: number }, shapeId?: string): this {
162
+ invariant(!this.#boxes.has(handle), `duplicate shape id: ${handle}`);
163
+ this.#boxes.set(handle, { id: shapeId ?? `shape:${handle}`, ...box });
164
+ return this;
165
+ }
166
+
167
+ /** Add a rectangle. */
168
+ rectangle(props: ShapeProps = {}): this {
169
+ return this.geo('rectangle', props);
170
+ }
171
+
172
+ /** Add an ellipse. */
173
+ ellipse(props: ShapeProps = {}): this {
174
+ return this.geo('ellipse', props);
175
+ }
176
+
177
+ /** Add a circle (an ellipse with equal width and height). */
178
+ circle(props: ShapeProps = {}): this {
179
+ const size = props.w ?? props.h ?? DEFAULTS.w;
180
+ return this.geo('ellipse', { ...props, w: size, h: size });
181
+ }
182
+
183
+ /** Add an arbitrary geometric shape. */
184
+ geo(geo: Geo, props: ShapeProps = {}): this {
185
+ const { id, x = 0, y = 0, w = DEFAULTS.w, h = DEFAULTS.h, rotation = 0 } = props;
186
+ const shapeId = this.#registerShape({ id, x, y, w, h });
187
+ this.#records[shapeId] = {
188
+ id: shapeId,
189
+ typeName: 'shape',
190
+ type: 'geo',
191
+ x,
192
+ y,
193
+ rotation,
194
+ index: this.#nextIndex(),
195
+ parentId: PAGE_ID,
196
+ isLocked: false,
197
+ opacity: 1,
198
+ meta: this.#meta(id),
199
+ props: {
200
+ geo,
201
+ w,
202
+ h,
203
+ color: props.color ?? DEFAULTS.color,
204
+ labelColor: DEFAULTS.color,
205
+ fill: props.fill ?? DEFAULTS.fill,
206
+ dash: props.dash ?? DEFAULTS.dash,
207
+ size: props.size ?? DEFAULTS.size,
208
+ font: props.font ?? DEFAULTS.font,
209
+ align: props.align ?? DEFAULTS.align,
210
+ verticalAlign: 'middle',
211
+ url: '',
212
+ growY: 0,
213
+ text: props.text ?? '',
214
+ scale: 1,
215
+ },
216
+ };
217
+
218
+ return this;
219
+ }
220
+
221
+ /** Add a free-standing text label. */
222
+ text(props: TextProps = {}): this {
223
+ const { id, x = 0, y = 0, w = DEFAULTS.w, rotation = 0 } = props;
224
+ const shapeId = this.#registerShape({ id, x, y, w, h: 0 });
225
+ this.#records[shapeId] = {
226
+ id: shapeId,
227
+ typeName: 'shape',
228
+ type: 'text',
229
+ x,
230
+ y,
231
+ rotation,
232
+ index: this.#nextIndex(),
233
+ parentId: PAGE_ID,
234
+ isLocked: false,
235
+ opacity: 1,
236
+ meta: this.#meta(id),
237
+ props: {
238
+ color: props.color ?? DEFAULTS.color,
239
+ size: props.size ?? DEFAULTS.size,
240
+ font: props.font ?? DEFAULTS.font,
241
+ textAlign: props.align ?? DEFAULTS.align,
242
+ w,
243
+ text: props.text ?? '',
244
+ scale: 1,
245
+ autoSize: true,
246
+ },
247
+ };
248
+
249
+ return this;
250
+ }
251
+
252
+ /** Add an arrow, optionally bound to source/target shapes so it tracks them. */
253
+ arrow(props: ConnectorProps = {}): this {
254
+ return this.#connector('arrow', props);
255
+ }
256
+
257
+ /** Add a line between two shapes (or explicit points). */
258
+ line(props: ConnectorProps = {}): this {
259
+ return this.#connector('line', props);
260
+ }
261
+
262
+ /** Add a path (open or closed, straight or smooth) through explicit points. */
263
+ path(props: PathProps): this {
264
+ const { id, smooth, closed } = props;
265
+ const source = closed && props.points.length > 1 ? [...props.points, props.points[0]] : [...props.points];
266
+ invariant(source.length >= 2, 'path requires at least two points');
267
+ const origin = source[0];
268
+ const xs = source.map((point) => point.x);
269
+ const ys = source.map((point) => point.y);
270
+ const shapeId = this.#registerShape({
271
+ id,
272
+ x: Math.min(...xs),
273
+ y: Math.min(...ys),
274
+ w: Math.max(...xs) - Math.min(...xs),
275
+ h: Math.max(...ys) - Math.min(...ys),
276
+ });
277
+ this.#records[shapeId] = {
278
+ id: shapeId,
279
+ typeName: 'shape',
280
+ type: 'line',
281
+ x: origin.x,
282
+ y: origin.y,
283
+ rotation: 0,
284
+ index: this.#nextIndex(),
285
+ parentId: PAGE_ID,
286
+ isLocked: false,
287
+ opacity: 1,
288
+ meta: this.#meta(id),
289
+ props: {
290
+ color: props.color ?? DEFAULTS.color,
291
+ dash: props.dash ?? DEFAULTS.dash,
292
+ size: props.size ?? DEFAULTS.size,
293
+ spline: smooth ? 'cubic' : 'line',
294
+ points: Object.fromEntries(
295
+ source.map((point, i) => [
296
+ `a${i + 1}`,
297
+ { id: `a${i + 1}`, index: `a${i + 1}`, x: point.x - origin.x, y: point.y - origin.y },
298
+ ]),
299
+ ),
300
+ scale: 1,
301
+ },
302
+ };
303
+
304
+ return this;
305
+ }
306
+
307
+ /** Build the full canvas snapshot (document + page + records). */
308
+ build(): CanvasContent {
309
+ return {
310
+ [DOCUMENT_ID]: { gridSize: 10, name: '', meta: {}, id: DOCUMENT_ID, typeName: 'document' },
311
+ [PAGE_ID]: { meta: {}, id: PAGE_ID, name: 'Page 1', index: 'a1', typeName: 'page' },
312
+ ...this.#records,
313
+ };
314
+ }
315
+
316
+ /** Return only the generated records (for merging into an existing canvas). */
317
+ records(): CanvasContent {
318
+ return { ...this.#records };
319
+ }
320
+
321
+ #connector(type: 'arrow' | 'line', props: ConnectorProps): this {
322
+ const from = props.from ? this.#boxes.get(props.from) : undefined;
323
+ const to = props.to ? this.#boxes.get(props.to) : undefined;
324
+ // A referenced handle that resolves to nothing is a typo; fail rather than emit a misplaced connector.
325
+ invariant(!props.from || from, `unknown source shape: ${props.from}`);
326
+ invariant(!props.to || to, `unknown target shape: ${props.to}`);
327
+ const start = from ? center(from) : (props.start ?? { x: 0, y: 0 });
328
+ const end = to ? center(to) : (props.end ?? { x: DEFAULTS.w, y: 0 });
329
+
330
+ const shapeId = this.#registerShape({ id: props.id, x: start.x, y: start.y, w: 0, h: 0 });
331
+ const relativeEnd = { x: end.x - start.x, y: end.y - start.y };
332
+ const style = {
333
+ color: props.color ?? DEFAULTS.color,
334
+ dash: props.dash ?? DEFAULTS.dash,
335
+ size: props.size ?? DEFAULTS.size,
336
+ };
337
+
338
+ if (type === 'arrow') {
339
+ this.#records[shapeId] = {
340
+ id: shapeId,
341
+ typeName: 'shape',
342
+ type: 'arrow',
343
+ x: start.x,
344
+ y: start.y,
345
+ rotation: 0,
346
+ index: this.#nextIndex(),
347
+ parentId: PAGE_ID,
348
+ isLocked: false,
349
+ opacity: 1,
350
+ meta: this.#meta(props.id),
351
+ props: {
352
+ ...style,
353
+ labelColor: DEFAULTS.color,
354
+ fill: props.fill ?? DEFAULTS.fill,
355
+ font: props.font ?? DEFAULTS.font,
356
+ arrowheadStart: 'none',
357
+ arrowheadEnd: 'arrow',
358
+ start: { x: 0, y: 0 },
359
+ end: relativeEnd,
360
+ bend: 0,
361
+ text: props.text ?? '',
362
+ labelPosition: 0.5,
363
+ scale: 1,
364
+ },
365
+ };
366
+
367
+ // Bindings make the arrow track its bound shapes (tldraw recomputes terminals).
368
+ if (from) {
369
+ this.#binding(shapeId, from.id, 'start');
370
+ }
371
+ if (to) {
372
+ this.#binding(shapeId, to.id, 'end');
373
+ }
374
+ } else {
375
+ this.#records[shapeId] = {
376
+ id: shapeId,
377
+ typeName: 'shape',
378
+ type: 'line',
379
+ x: start.x,
380
+ y: start.y,
381
+ rotation: 0,
382
+ index: this.#nextIndex(),
383
+ parentId: PAGE_ID,
384
+ isLocked: false,
385
+ opacity: 1,
386
+ meta: this.#meta(props.id),
387
+ props: {
388
+ ...style,
389
+ spline: 'line',
390
+ points: {
391
+ a1: { id: 'a1', index: 'a1', x: 0, y: 0 },
392
+ a2: { id: 'a2', index: 'a2', x: relativeEnd.x, y: relativeEnd.y },
393
+ },
394
+ scale: 1,
395
+ },
396
+ };
397
+ }
398
+
399
+ return this;
400
+ }
401
+
402
+ #binding(arrowId: string, shapeId: string, terminal: 'start' | 'end'): void {
403
+ // Deterministic per-terminal id so re-rendering an arrow overwrites its own bindings.
404
+ const bindingId = `binding:${arrowId.slice('shape:'.length)}/${terminal}-${++this.#bindingCount}`;
405
+ this.#records[bindingId] = {
406
+ id: bindingId,
407
+ typeName: 'binding',
408
+ type: 'arrow',
409
+ fromId: arrowId,
410
+ toId: shapeId,
411
+ meta: {},
412
+ props: {
413
+ terminal,
414
+ normalizedAnchor: { x: 0.5, y: 0.5 },
415
+ isExact: false,
416
+ isPrecise: false,
417
+ },
418
+ };
419
+ }
420
+
421
+ #meta(handle: string | undefined): Record<string, any> {
422
+ return handle && this.#metaFor ? this.#metaFor(handle) : {};
423
+ }
424
+
425
+ #registerShape(box: { id?: string; x: number; y: number; w: number; h: number }): string {
426
+ // Skip autogenerated ids that collide with an explicit one (e.g. a user-supplied `s1`).
427
+ let shapeId = box.id ? `shape:${box.id}` : `shape:s${++this.#shapeCount}`;
428
+ while (!box.id && this.#records[shapeId]) {
429
+ shapeId = `shape:s${++this.#shapeCount}`;
430
+ }
431
+ const handle = box.id ?? shapeId;
432
+ // The id is the record-map key; reusing one would silently overwrite the earlier shape.
433
+ invariant(!this.#records[shapeId] && !this.#boxes.has(handle), `duplicate shape id: ${handle}`);
434
+ this.#boxes.set(handle, { id: shapeId, x: box.x, y: box.y, w: box.w, h: box.h });
435
+ return shapeId;
436
+ }
437
+
438
+ #nextIndex(): string {
439
+ return `a${++this.#indexCount}`;
440
+ }
441
+ }
442
+
443
+ const center = (box: Box): Point => ({ x: box.x + box.w / 2, y: box.y + box.h / 2 });
@@ -0,0 +1,132 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ //
6
+ // Applies scene DSL edit commands to canvas content by mutating the record map in place
7
+ // (callers wrap the call in `Obj.update` so a batch commits atomically).
8
+ //
9
+
10
+ import { invariant } from '@dxos/invariant';
11
+ import { type ApplyResult, type Scene } from '@dxos/plugin-illustrator/model';
12
+
13
+ import { elementBoxes, readScene } from './read';
14
+ import { renderObject } from './render';
15
+ import { type CanvasContent, DOCUMENT_ID, PAGE_ID } from './TldrawBuilder';
16
+
17
+ /** Apply a batch of edit commands to the canvas content (mutates `content`). */
18
+ export const applyCommands = (content: CanvasContent, commands: readonly Scene.Command[]): ApplyResult => {
19
+ ensurePage(content);
20
+ const upserted = new Set<string>();
21
+ let removed = 0;
22
+
23
+ for (const command of commands) {
24
+ switch (command.op) {
25
+ case 'upsert-object': {
26
+ const previous = readScene(content).scene.objects.find((object) => object.id === command.object.id);
27
+ removed += remove(content, (record) => record.meta?.object === command.object.id);
28
+ merge(content, command.object, {
29
+ origin: command.object.origin ?? previous?.origin ?? { x: 0, y: 0 },
30
+ scale: command.object.scale ?? previous?.scale ?? 1,
31
+ });
32
+ upserted.add(command.object.id);
33
+ break;
34
+ }
35
+ case 'upsert-elements': {
36
+ const previous = readScene(content).scene.objects.find((object) => object.id === command.objectId);
37
+ invariant(previous, `unknown object: ${command.objectId} (use upsert-object to create it)`);
38
+ const ids = new Set(command.elements.map((element) => element.id));
39
+ removed += remove(
40
+ content,
41
+ (record) => record.meta?.object === command.objectId && ids.has(record.meta?.element),
42
+ );
43
+ merge(
44
+ content,
45
+ { id: command.objectId, elements: command.elements },
46
+ { origin: previous.origin, scale: previous.scale },
47
+ );
48
+ upserted.add(command.objectId);
49
+ break;
50
+ }
51
+ case 'remove-elements': {
52
+ const ids = new Set(command.elementIds);
53
+ removed += remove(
54
+ content,
55
+ (record) => record.meta?.object === command.objectId && ids.has(record.meta?.element),
56
+ );
57
+ break;
58
+ }
59
+ case 'remove-object': {
60
+ removed += remove(content, (record) => record.meta?.object === command.objectId);
61
+ break;
62
+ }
63
+ case 'move-object': {
64
+ const previous = readScene(content).scene.objects.find((object) => object.id === command.objectId);
65
+ invariant(previous, `unknown object: ${command.objectId}`);
66
+ const delta = { x: command.origin.x - previous.origin.x, y: command.origin.y - previous.origin.y };
67
+ for (const record of Object.values(content)) {
68
+ if (record?.typeName === 'shape' && record.meta?.object === command.objectId) {
69
+ record.x += delta.x;
70
+ record.y += delta.y;
71
+ }
72
+ }
73
+ upserted.add(command.objectId);
74
+ break;
75
+ }
76
+ }
77
+ }
78
+
79
+ return { upserted: [...upserted], removed };
80
+ };
81
+
82
+ const merge = (
83
+ content: CanvasContent,
84
+ object: Scene.WorldObject | { id: string; elements: readonly Scene.Element[] },
85
+ placement: { origin: Scene.Point; scale: number },
86
+ ): void => {
87
+ const records = renderObject(object, {
88
+ ...placement,
89
+ indexStart: nextIndex(content),
90
+ external: elementBoxes(content),
91
+ });
92
+ Object.assign(content, records);
93
+ };
94
+
95
+ /** Delete matching shape records, then any bindings left dangling. */
96
+ const remove = (content: CanvasContent, predicate: (record: any) => boolean): number => {
97
+ let removed = 0;
98
+ for (const [key, record] of Object.entries(content)) {
99
+ if (record?.typeName === 'shape' && predicate(record)) {
100
+ delete content[key];
101
+ removed++;
102
+ }
103
+ }
104
+ for (const [key, record] of Object.entries(content)) {
105
+ if (record?.typeName === 'binding' && (!content[record.fromId] || !content[record.toId])) {
106
+ delete content[key];
107
+ }
108
+ }
109
+ return removed;
110
+ };
111
+
112
+ /** Seed the fractional-index counter past any existing `a<n>` indexes. */
113
+ const nextIndex = (content: CanvasContent): number => {
114
+ let max = 0;
115
+ let shapes = 0;
116
+ for (const record of Object.values(content)) {
117
+ if (record?.typeName !== 'shape') {
118
+ continue;
119
+ }
120
+ shapes++;
121
+ const match = /^a(\d+)$/.exec(record.index ?? '');
122
+ if (match) {
123
+ max = Math.max(max, Number(match[1]));
124
+ }
125
+ }
126
+ return Math.max(max, shapes);
127
+ };
128
+
129
+ const ensurePage = (content: CanvasContent): void => {
130
+ content[DOCUMENT_ID] ??= { gridSize: 10, name: '', meta: {}, id: DOCUMENT_ID, typeName: 'document' };
131
+ content[PAGE_ID] ??= { meta: {}, id: PAGE_ID, name: 'Page 1', index: 'a1', typeName: 'page' };
132
+ };
@@ -0,0 +1,31 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ //
6
+ // tldraw implementation of the illustrator `SketchBuilder` contract: maps the scene DSL
7
+ // onto the tldraw record map stored in `Tldraw.Canvas.content`.
8
+ //
9
+
10
+ import { Obj } from '@dxos/echo';
11
+ import { invariant } from '@dxos/invariant';
12
+ import { type ApplyResult, type SketchBuilder } from '@dxos/plugin-illustrator/model';
13
+
14
+ import { Tldraw } from '../types';
15
+ import { applyCommands } from './apply';
16
+ import { readScene } from './read';
17
+
18
+ export const tldrawBuilder: SketchBuilder = {
19
+ read: (canvas) => {
20
+ invariant(Obj.instanceOf(Tldraw.Canvas, canvas));
21
+ return readScene(canvas.content);
22
+ },
23
+ apply: (canvas, commands) => {
24
+ invariant(Obj.instanceOf(Tldraw.Canvas, canvas));
25
+ let result: ApplyResult = { upserted: [], removed: 0 };
26
+ Obj.update(canvas, (canvas) => {
27
+ result = applyCommands(canvas.content as Obj.Mutable<typeof canvas.content>, commands);
28
+ });
29
+ return result;
30
+ },
31
+ };
@@ -0,0 +1,9 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ export * from './apply';
6
+ export * from './builder';
7
+ export * from './read';
8
+ export * from './render';
9
+ export * from './TldrawBuilder';