@dxos/plugin-tldraw 0.10.0 → 0.11.1

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 (88) hide show
  1. package/dist/lib/TldrawPlugin.mjs +4 -5
  2. package/dist/lib/TldrawPlugin.mjs.map +1 -1
  3. package/dist/lib/capabilities.mjs +2 -2
  4. package/dist/lib/capabilities.mjs.map +1 -1
  5. package/dist/lib/chunk-TldrawArticle.mjs +10 -9
  6. package/dist/lib/chunk-TldrawArticle.mjs.map +1 -1
  7. package/dist/lib/chunk-TldrawCard.mjs +5 -5
  8. package/dist/lib/chunk-TldrawCard.mjs.map +1 -1
  9. package/dist/lib/chunk-drawing-variant.mjs +20 -0
  10. package/dist/lib/chunk-drawing-variant.mjs.map +1 -0
  11. package/dist/lib/chunk-meta.mjs +1 -0
  12. package/dist/lib/chunk-meta.mjs.map +1 -1
  13. package/dist/lib/chunk-types.mjs +5 -31
  14. package/dist/lib/chunk-types.mjs.map +1 -1
  15. package/dist/lib/components.mjs +1 -1
  16. package/dist/lib/components.mjs.map +1 -1
  17. package/dist/lib/hooks.mjs +5 -5
  18. package/dist/lib/hooks.mjs.map +1 -1
  19. package/dist/lib/index.mjs +1 -1
  20. package/dist/lib/model.mjs +58 -163
  21. package/dist/lib/model.mjs.map +1 -1
  22. package/dist/lib/types.mjs +1 -1
  23. package/dist/types/src/TldrawPlugin.d.ts.map +1 -1
  24. package/dist/types/src/capabilities/{sketch-variant.d.ts → drawing-variant.d.ts} +1 -1
  25. package/dist/types/src/capabilities/drawing-variant.d.ts.map +1 -0
  26. package/dist/types/src/capabilities/index.d.ts +1 -1
  27. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  28. package/dist/types/src/components/Canvas/Canvas.d.ts +4 -3
  29. package/dist/types/src/components/Canvas/Canvas.d.ts.map +1 -1
  30. package/dist/types/src/components/Canvas/Canvas.stories.d.ts.map +1 -1
  31. package/dist/types/src/components/Canvas/Mermaid.stories.d.ts +14 -0
  32. package/dist/types/src/components/Canvas/Mermaid.stories.d.ts.map +1 -0
  33. package/dist/types/src/components/actions.d.ts +2 -2
  34. package/dist/types/src/components/actions.d.ts.map +1 -1
  35. package/dist/types/src/containers/TldrawArticle/TldrawArticle.d.ts +3 -3
  36. package/dist/types/src/containers/TldrawArticle/TldrawArticle.d.ts.map +1 -1
  37. package/dist/types/src/containers/TldrawCard/TldrawCard.d.ts +2 -2
  38. package/dist/types/src/containers/TldrawCard/TldrawCard.d.ts.map +1 -1
  39. package/dist/types/src/containers/TldrawCard/TldrawCard.stories.d.ts.map +1 -1
  40. package/dist/types/src/hooks/useStoreAdapter.d.ts +2 -2
  41. package/dist/types/src/hooks/useStoreAdapter.d.ts.map +1 -1
  42. package/dist/types/src/model/{TldrawBuilder.d.ts → RecordBuilder.d.ts} +3 -3
  43. package/dist/types/src/model/{TldrawBuilder.d.ts.map → RecordBuilder.d.ts.map} +1 -1
  44. package/dist/types/src/model/RecordBuilder.test.d.ts +2 -0
  45. package/dist/types/src/model/RecordBuilder.test.d.ts.map +1 -0
  46. package/dist/types/src/model/builder.d.ts +7 -2
  47. package/dist/types/src/model/builder.d.ts.map +1 -1
  48. package/dist/types/src/model/index.d.ts +1 -2
  49. package/dist/types/src/model/index.d.ts.map +1 -1
  50. package/dist/types/src/model/read.d.ts +1 -1
  51. package/dist/types/src/model/read.d.ts.map +1 -1
  52. package/dist/types/src/model/render.d.ts +1 -1
  53. package/dist/types/src/types/Tldraw.d.ts +3 -22
  54. package/dist/types/src/types/Tldraw.d.ts.map +1 -1
  55. package/dist/types/src/types/TldrawCapabilities.d.ts +1 -1
  56. package/dist/types/src/types/TldrawCapabilities.d.ts.map +1 -1
  57. package/dist/types/tsconfig.tsbuildinfo +1 -1
  58. package/dx.config.ts +1 -0
  59. package/package.json +33 -32
  60. package/src/TldrawPlugin.tsx +3 -5
  61. package/src/capabilities/{sketch-variant.ts → drawing-variant.ts} +7 -8
  62. package/src/capabilities/index.ts +1 -1
  63. package/src/components/Canvas/Canvas.stories.tsx +11 -7
  64. package/src/components/Canvas/Canvas.tsx +3 -2
  65. package/src/components/Canvas/Mermaid.stories.tsx +68 -0
  66. package/src/components/actions.ts +2 -3
  67. package/src/containers/TldrawArticle/TldrawArticle.tsx +8 -7
  68. package/src/containers/TldrawCard/TldrawCard.stories.tsx +8 -7
  69. package/src/containers/TldrawCard/TldrawCard.tsx +4 -4
  70. package/src/hooks/useStoreAdapter.ts +2 -1
  71. package/src/model/{TldrawBuilder.test.ts → RecordBuilder.test.ts} +8 -8
  72. package/src/model/{TldrawBuilder.ts → RecordBuilder.ts} +2 -2
  73. package/src/model/builder.ts +55 -18
  74. package/src/model/index.ts +1 -2
  75. package/src/model/read.ts +1 -1
  76. package/src/model/render.ts +3 -3
  77. package/src/model/scene.test.ts +1 -1
  78. package/src/types/Tldraw.ts +3 -30
  79. package/src/types/TldrawCapabilities.ts +1 -1
  80. package/src/variant.test.ts +19 -21
  81. package/dist/lib/chunk-sketch-variant.mjs +0 -23
  82. package/dist/lib/chunk-sketch-variant.mjs.map +0 -1
  83. package/dist/types/src/capabilities/sketch-variant.d.ts.map +0 -1
  84. package/dist/types/src/model/TldrawBuilder.test.d.ts +0 -2
  85. package/dist/types/src/model/TldrawBuilder.test.d.ts.map +0 -1
  86. package/dist/types/src/model/apply.d.ts +0 -5
  87. package/dist/types/src/model/apply.d.ts.map +0 -1
  88. package/src/model/apply.ts +0 -132
package/dx.config.ts CHANGED
@@ -30,6 +30,7 @@ export default Config2.make({
30
30
  dependsOn: ['org.dxos.plugin.illustrator'],
31
31
  icon: { key: 'ph--compass-tool--regular', hue: 'indigo' },
32
32
  spec: 'PLUGIN.mdl',
33
+ tags: ['alpha'],
33
34
  screenshots: [{ dark: 'https://dxos.network/plugin-details-sketch-dark.png' }],
34
35
  },
35
36
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dxos/plugin-tldraw",
3
- "version": "0.10.0",
3
+ "version": "0.11.1",
4
4
  "description": "tldraw canvas renderer for the DXOS Illustrator plugin.",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
@@ -92,7 +92,7 @@
92
92
  ],
93
93
  "dependencies": {
94
94
  "@automerge/automerge": "3.3.0-fragments.2",
95
- "@effect-atom/atom-react": "^0.5.0",
95
+ "@effect-atom/atom": "^0.5.3",
96
96
  "@tldraw/assets": "^3.0.0",
97
97
  "@tldraw/editor": "^3.0.0",
98
98
  "@tldraw/state": "^3.0.0",
@@ -103,29 +103,30 @@
103
103
  "effect": "3.21.4",
104
104
  "lodash.defaultsdeep": "^4.6.1",
105
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"
106
+ "@dxos/app-framework": "0.11.1",
107
+ "@dxos/async": "0.11.1",
108
+ "@dxos/client": "0.11.1",
109
+ "@dxos/compute": "0.11.1",
110
+ "@dxos/app-toolkit": "0.11.1",
111
+ "@dxos/echo-doc": "0.11.1",
112
+ "@dxos/effect": "0.11.1",
113
+ "@dxos/invariant": "0.11.1",
114
+ "@dxos/keys": "0.11.1",
115
+ "@dxos/log": "0.11.1",
116
+ "@dxos/plugin-graph": "0.11.1",
117
+ "@dxos/plugin-illustrator": "0.11.1",
118
+ "@dxos/plugin-space": "0.11.1",
119
+ "@dxos/echo-client": "0.11.1",
120
+ "@dxos/react-client": "0.11.1",
121
+ "@dxos/react-hooks": "0.11.1",
122
+ "@dxos/react-ui-attention": "0.11.1",
123
+ "@dxos/echo": "0.11.1",
124
+ "@dxos/react-ui-form": "0.11.1",
125
+ "@dxos/schema": "0.11.1",
126
+ "@dxos/util": "0.11.1"
127
127
  },
128
128
  "devDependencies": {
129
+ "@effect-atom/atom-react": "^0.5.0",
129
130
  "@effect/vitest": "0.29.0",
130
131
  "@types/lodash.defaultsdeep": "^4.6.6",
131
132
  "@types/react": "~19.2.17",
@@ -133,20 +134,20 @@
133
134
  "react": "~19.2.7",
134
135
  "react-dom": "~19.2.7",
135
136
  "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"
137
+ "@dxos/agent-runtime": "0.11.1",
138
+ "@dxos/plugin-testing": "0.11.1",
139
+ "@dxos/react-ui-mosaic": "0.11.1",
140
+ "@dxos/storybook-utils": "0.11.1",
141
+ "@dxos/ui-theme": "0.11.1",
142
+ "@dxos/random": "0.11.1",
143
+ "@dxos/react-ui": "0.11.1"
143
144
  },
144
145
  "peerDependencies": {
145
146
  "effect": "3.21.4",
146
147
  "react": "~19.2.7",
147
148
  "react-dom": "~19.2.7",
148
- "@dxos/react-ui": "^0.10.0",
149
- "@dxos/ui-theme": "^0.10.0"
149
+ "@dxos/react-ui": "^0.11.1",
150
+ "@dxos/ui-theme": "^0.11.1"
150
151
  },
151
152
  "publishConfig": {
152
153
  "access": "public"
@@ -5,21 +5,19 @@
5
5
  import { Plugin } from '@dxos/app-framework';
6
6
  import { AppActivationEvents, AppPlugin } from '@dxos/app-toolkit';
7
7
 
8
- import { SketchVariant, TldrawSettings } from '#capabilities';
8
+ import { DrawingVariant, TldrawSettings } from '#capabilities';
9
9
  import { meta } from '#meta';
10
10
  import { translations } from '#translations';
11
- import { Tldraw } from '#types';
12
11
 
13
12
  // eslint-disable-next-line import/no-relative-packages
14
13
  import pluginSpec from '../PLUGIN.mdl?raw';
15
14
 
16
15
  export const TldrawPlugin = Plugin.define(meta).pipe(
17
16
  Plugin.addModule({
18
- id: 'sketch-variant',
17
+ id: 'drawing-variant',
19
18
  activatesOn: AppActivationEvents.SetupSchema,
20
- activate: SketchVariant,
19
+ activate: DrawingVariant,
21
20
  }),
22
- AppPlugin.addSchemaModule({ schema: [Tldraw.Canvas] }),
23
21
  AppPlugin.addSettingsModule({ activate: TldrawSettings }),
24
22
  AppPlugin.addTranslationsModule({ translations }),
25
23
  AppPlugin.addPluginAssetModule({
@@ -5,21 +5,20 @@
5
5
  import * as Effect from 'effect/Effect';
6
6
 
7
7
  import { Capability } from '@dxos/app-framework';
8
- import { Type } from '@dxos/echo';
9
- import { IllustratorCapabilities, type SketchVariant } from '@dxos/plugin-illustrator/types';
8
+ import { type DrawingVariant, IllustratorCapabilities } from '@dxos/plugin-illustrator/types';
10
9
 
11
10
  import { TldrawArticle, TldrawCard } from '#containers';
12
- import { tldrawBuilder } from '#model';
11
+ import { TldrawBuilder } from '#model';
13
12
 
14
13
  import { Tldraw } from '../types';
15
14
 
16
- const variant: SketchVariant = {
17
- id: Type.getTypename(Tldraw.Canvas),
15
+ // No `canvasType`/`createCanvas`: tldraw stores its records in the base `Drawing.Canvas`,
16
+ // discriminated by `schema`.
17
+ const variant: DrawingVariant = {
18
+ id: Tldraw.TLDRAW_SCHEMA,
18
19
  label: 'tldraw',
19
20
  icon: 'ph--compass-tool--regular',
20
- canvasType: Tldraw.Canvas,
21
- createCanvas: () => Effect.sync(() => Tldraw.makeCanvas()),
22
- builder: tldrawBuilder,
21
+ builder: TldrawBuilder,
23
22
  card: TldrawCard,
24
23
  article: TldrawArticle,
25
24
  };
@@ -4,5 +4,5 @@
4
4
 
5
5
  import { Capability } from '@dxos/app-framework';
6
6
 
7
- export const SketchVariant = Capability.lazy('SketchVariant', () => import('./sketch-variant'));
7
+ export const DrawingVariant = Capability.lazy('DrawingVariant', () => import('./drawing-variant'));
8
8
  export const TldrawSettings = Capability.lazy('TldrawSettings', () => import('./settings'));
@@ -6,10 +6,11 @@ import { type Meta, type StoryObj } from '@storybook/react-vite';
6
6
  import React, { useState } from 'react';
7
7
 
8
8
  import { createObject } from '@dxos/echo-client';
9
+ import { Drawing } from '@dxos/plugin-illustrator/types';
9
10
  import { Button, Panel, Toolbar } from '@dxos/react-ui';
10
11
  import { withLayout, withTheme } from '@dxos/react-ui/testing';
11
12
 
12
- import { TldrawBuilder } from '#model';
13
+ import { RecordBuilder } from '#model';
13
14
  import { data } from '#testing';
14
15
  import { Tldraw } from '#types';
15
16
 
@@ -17,21 +18,23 @@ import { migrateCanvas } from '../../migrations';
17
18
  import { CanvasComponent } from './Canvas';
18
19
 
19
20
  const DefaultStory = () => {
20
- const [canvas, setCanvas] = useState(createObject(Tldraw.makeCanvas({ content: data.v2 })));
21
+ const [canvas, setCanvas] = useState(
22
+ createObject(Drawing.makeCanvas({ schema: Tldraw.TLDRAW_SCHEMA, content: data.v2 })),
23
+ );
21
24
 
22
25
  const handleClear = () => {
23
- setCanvas(createObject(Tldraw.makeCanvas()));
26
+ setCanvas(createObject(Drawing.makeCanvas({ schema: Tldraw.TLDRAW_SCHEMA })));
24
27
  };
25
28
 
26
29
  const handleCreate = () => {
27
- const canvas = createObject(Tldraw.makeCanvas({ content: data.v2 }));
30
+ const canvas = createObject(Drawing.makeCanvas({ schema: Tldraw.TLDRAW_SCHEMA, content: data.v2 }));
28
31
  console.log(JSON.stringify(canvas, undefined, 2));
29
32
  setCanvas(canvas);
30
33
  };
31
34
 
32
35
  const handleMigrate = async () => {
33
36
  const content = await migrateCanvas(data.v1);
34
- setCanvas(createObject(Tldraw.makeCanvas({ content })));
37
+ setCanvas(createObject(Drawing.makeCanvas({ schema: Tldraw.TLDRAW_SCHEMA, content })));
35
38
  };
36
39
 
37
40
  return (
@@ -74,8 +77,9 @@ export const Default: Story = {};
74
77
  const BuilderStory = () => {
75
78
  const [canvas] = useState(() =>
76
79
  createObject(
77
- Tldraw.makeCanvas({
78
- content: new TldrawBuilder()
80
+ Drawing.makeCanvas({
81
+ schema: Tldraw.TLDRAW_SCHEMA,
82
+ content: new RecordBuilder()
79
83
  .rectangle({ id: 'a', x: 0, y: 0, text: 'DXOS', color: 'blue', fill: 'solid' })
80
84
  .ellipse({ id: 'b', x: 360, y: 0, text: 'ECHO', color: 'green' })
81
85
  .geo('star', { id: 'c', x: 180, y: 280, text: 'EDGE', color: 'yellow' })
@@ -13,11 +13,12 @@ import React, { type FC, useEffect, useMemo, useState } from 'react';
13
13
  import { useResizeDetector } from 'react-resize-detector';
14
14
 
15
15
  import { Obj } from '@dxos/echo';
16
+ import { Drawing } from '@dxos/plugin-illustrator/types';
16
17
  import { useMergeRefs } from '@dxos/react-hooks';
17
18
  import { composable, composableProps } from '@dxos/react-ui';
18
19
 
19
20
  import { useStoreAdapter } from '#hooks';
20
- import { type Settings, type Tldraw } from '#types';
21
+ import { type Settings } from '#types';
21
22
 
22
23
  import { handleSnap } from '../actions';
23
24
  import { CustomMenu, CustomStylePanel, DefaultToolbarContent, DottedGrid, MeshGrid } from '../custom';
@@ -30,7 +31,7 @@ const gridComponents: Record<Settings.GridType, FC<TLGridProps>> = {
30
31
  };
31
32
 
32
33
  export type CanvasProps = {
33
- canvas: Tldraw.Canvas;
34
+ canvas: Drawing.Canvas;
34
35
  readonly?: boolean;
35
36
  autoCenter?: boolean;
36
37
  hideUi?: boolean;
@@ -0,0 +1,68 @@
1
+ //
2
+ // Copyright 2026 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 { type ContentMap, Mermaid } from '@dxos/plugin-illustrator/model';
10
+ import { Drawing } from '@dxos/plugin-illustrator/types';
11
+ import { Panel } from '@dxos/react-ui';
12
+ import { withLayout, withTheme } from '@dxos/react-ui/testing';
13
+
14
+ import { applyCommands } from '#model';
15
+ import { Tldraw } from '#types';
16
+
17
+ import { CanvasComponent } from './Canvas';
18
+
19
+ const FLOWCHART = `
20
+ flowchart TB
21
+ X[X]
22
+
23
+ subgraph CORE[" "]
24
+ A[A]
25
+ B[B]
26
+ C[C]
27
+
28
+ A --> B
29
+ A --> C
30
+ end
31
+
32
+ Y[Y]
33
+
34
+ X --> A
35
+ X --> B
36
+ X --> C
37
+ C --> Y
38
+ Y --> C
39
+ `;
40
+
41
+ const DefaultStory = () => {
42
+ const [canvas] = useState(() => {
43
+ const content: ContentMap = {};
44
+ applyCommands(content, Mermaid.compile(FLOWCHART, { scale: 2 }));
45
+ return createObject(Drawing.makeCanvas({ schema: Tldraw.TLDRAW_SCHEMA, content }));
46
+ });
47
+
48
+ return (
49
+ <Panel.Root>
50
+ <Panel.Content asChild>
51
+ <CanvasComponent classNames='dx-attention-surface' canvas={canvas} assetsBaseUrl={null} autoCenter />
52
+ </Panel.Content>
53
+ </Panel.Root>
54
+ );
55
+ };
56
+
57
+ const meta = {
58
+ title: 'plugins/plugin-tldraw/components/Mermaid',
59
+ render: DefaultStory,
60
+ decorators: [withTheme(), withLayout({ layout: 'fullscreen' })],
61
+ parameters: { layout: 'fullscreen' },
62
+ } satisfies Meta<typeof DefaultStory>;
63
+
64
+ export default meta;
65
+
66
+ type Story = StoryObj<typeof meta>;
67
+
68
+ export const Default: Story = {};
@@ -6,14 +6,13 @@ import type { TLRecord } from '@tldraw/tldraw';
6
6
  import { isShape } from '@tldraw/tlschema';
7
7
 
8
8
  import { Doc } from '@dxos/echo-doc';
9
+ import { Drawing } from '@dxos/plugin-illustrator/types';
9
10
  import { getDeep } from '@dxos/util';
10
11
 
11
- import type { Tldraw } from '#types';
12
-
13
12
  /**
14
13
  * Snap to grid.
15
14
  */
16
- export const handleSnap = async (canvas: Tldraw.Canvas) => {
15
+ export const handleSnap = async (canvas: Drawing.Canvas) => {
17
16
  const snap = (value: number, tolerance = 40) => {
18
17
  return Math.round(value / tolerance) * tolerance;
19
18
  };
@@ -8,22 +8,23 @@ import { useAtomCapability } from '@dxos/app-framework/ui';
8
8
  import { AppSurface, useAppGraph } from '@dxos/app-toolkit/ui';
9
9
  import { Obj } from '@dxos/echo';
10
10
  import { invariant } from '@dxos/invariant';
11
- import { useActions } from '@dxos/plugin-graph';
12
- import { type SketchVariantSurfaceProps } from '@dxos/plugin-illustrator/types';
11
+ import { useActions } from '@dxos/plugin-graph/hooks';
12
+ import { type DrawingVariantSurfaceProps } from '@dxos/plugin-illustrator/types';
13
+ import { Drawing } from '@dxos/plugin-illustrator/types';
13
14
  import { Flex, Panel } from '@dxos/react-ui';
14
15
  import { composable, composableProps } from '@dxos/react-ui';
15
16
  import { useAttention } from '@dxos/react-ui-attention';
16
17
  import { isTauri } from '@dxos/util';
17
18
 
18
19
  import { CanvasComponent } from '#components';
19
- import { Tldraw, TldrawCapabilities } from '#types';
20
+ import { TldrawCapabilities } from '#types';
20
21
 
21
- export type TldrawArticleProps = SketchVariantSurfaceProps;
22
+ export type TldrawArticleProps = DrawingVariantSurfaceProps;
22
23
 
23
- export const TldrawArticle = ({ role, attendableId, sketch, canvas, extrinsic }: TldrawArticleProps) => {
24
- invariant(Obj.instanceOf(Tldraw.Canvas, canvas));
24
+ export const TldrawArticle = ({ role, attendableId, drawing, canvas, extrinsic }: TldrawArticleProps) => {
25
+ invariant(Obj.instanceOf(Drawing.Canvas, canvas));
25
26
  const settings = useAtomCapability(TldrawCapabilities.Settings);
26
- const id = Obj.getURI(sketch as Obj.Any);
27
+ const id = Obj.getURI(drawing as Obj.Any);
27
28
  const { hasAttention } = useAttention(attendableId);
28
29
  const section = role === AppSurface.Section.role;
29
30
 
@@ -5,20 +5,21 @@
5
5
  import { type Meta, type StoryObj } from '@storybook/react-vite';
6
6
  import React, { useMemo } from 'react';
7
7
 
8
- import { Sketch } from '@dxos/plugin-illustrator/types';
8
+ import { Drawing } from '@dxos/plugin-illustrator/types';
9
9
  import { CardContainer } from '@dxos/react-ui-mosaic/testing';
10
10
  import { withLayout, withTheme } from '@dxos/react-ui/testing';
11
11
 
12
12
  import { meta as pluginMeta } from '#meta';
13
- import { TldrawBuilder } from '#model';
13
+ import { RecordBuilder } from '#model';
14
14
  import { Tldraw } from '#types';
15
15
 
16
16
  import { TldrawCard } from './TldrawCard';
17
17
 
18
18
  const CardStory = () => {
19
- const { sketch, canvas } = useMemo(() => {
20
- const canvas = Tldraw.makeCanvas({
21
- content: new TldrawBuilder()
19
+ const { drawing, canvas } = useMemo(() => {
20
+ const canvas = Drawing.makeCanvas({
21
+ schema: Tldraw.TLDRAW_SCHEMA,
22
+ content: new RecordBuilder()
22
23
  .rectangle({ id: 'a', x: 0, y: 0, text: 'DXOS', color: 'blue', fill: 'solid' })
23
24
  .ellipse({ id: 'b', x: 360, y: 0, text: 'ECHO', color: 'green' })
24
25
  .geo('star', { id: 'c', x: 180, y: 280, text: 'EDGE', color: 'yellow' })
@@ -26,12 +27,12 @@ const CardStory = () => {
26
27
  .arrow({ from: 'b', to: 'c' })
27
28
  .build(),
28
29
  });
29
- return { sketch: Sketch.make({ canvas }), canvas };
30
+ return { drawing: Drawing.make({ canvas }), canvas };
30
31
  }, []);
31
32
 
32
33
  return (
33
34
  <CardContainer role='popover' icon={pluginMeta.profile.icon?.key}>
34
- <TldrawCard role='card--content' sketch={sketch} canvas={canvas} />
35
+ <TldrawCard role='card--content' drawing={drawing} canvas={canvas} />
35
36
  </CardContainer>
36
37
  );
37
38
  };
@@ -6,16 +6,16 @@ import React from 'react';
6
6
 
7
7
  import { Obj } from '@dxos/echo';
8
8
  import { invariant } from '@dxos/invariant';
9
- import { type SketchVariantSurfaceProps } from '@dxos/plugin-illustrator/types';
9
+ import { type DrawingVariantSurfaceProps } from '@dxos/plugin-illustrator/types';
10
+ import { Drawing } from '@dxos/plugin-illustrator/types';
10
11
  import { Card } from '@dxos/react-ui';
11
12
 
12
13
  import { CanvasComponent } from '#components';
13
- import { Tldraw } from '#types';
14
14
 
15
- export type TldrawCardProps = SketchVariantSurfaceProps;
15
+ export type TldrawCardProps = DrawingVariantSurfaceProps;
16
16
 
17
17
  export const TldrawCard = ({ canvas, editable = false }: TldrawCardProps) => {
18
- invariant(Obj.instanceOf(Tldraw.Canvas, canvas));
18
+ invariant(Obj.instanceOf(Drawing.Canvas, canvas));
19
19
  return (
20
20
  <Card.Body>
21
21
  <Card.Section classNames='aspect-square'>
@@ -6,6 +6,7 @@ import { useState } from 'react';
6
6
 
7
7
  import { Doc } from '@dxos/echo-doc';
8
8
  import { log } from '@dxos/log';
9
+ import { Drawing } from '@dxos/plugin-illustrator/types';
9
10
  import { useAsyncEffect } from '@dxos/react-ui';
10
11
 
11
12
  import { Tldraw } from '#types';
@@ -19,7 +20,7 @@ import { TLDrawStoreAdapter } from './adapter';
19
20
  * @param canvas - Optional canvas whose content will be synced.
20
21
  * @returns The TLDrawStoreAdapter instance managing the tldraw store.
21
22
  */
22
- export const useStoreAdapter = (canvas?: Tldraw.Canvas) => {
23
+ export const useStoreAdapter = (canvas?: Drawing.Canvas) => {
23
24
  const [adapter] = useState(new TLDrawStoreAdapter());
24
25
  const [_, forceUpdate] = useState({});
25
26
  useAsyncEffect(
@@ -5,17 +5,17 @@
5
5
  import { createTLSchema } from '@tldraw/tlschema';
6
6
  import { describe, test } from 'vitest';
7
7
 
8
- import { TldrawBuilder } from './TldrawBuilder';
8
+ import { RecordBuilder } from './RecordBuilder';
9
9
 
10
- describe('TldrawBuilder', () => {
10
+ describe('RecordBuilder', () => {
11
11
  test('builds an empty canvas with document and page', ({ expect }) => {
12
- const content = new TldrawBuilder().build();
12
+ const content = new RecordBuilder().build();
13
13
  expect(content['document:document'].typeName).to.eq('document');
14
14
  expect(content['page:page'].typeName).to.eq('page');
15
15
  });
16
16
 
17
17
  test('chains shapes and connectors', ({ expect }) => {
18
- const content = new TldrawBuilder()
18
+ const content = new RecordBuilder()
19
19
  .rectangle({ id: 'a', x: 0, y: 0, text: 'DXOS', color: 'blue' })
20
20
  .ellipse({ id: 'b', x: 320, y: 0, text: 'ECHO', color: 'green' })
21
21
  .text({ x: 0, y: 240, text: 'Hello', font: 'mono' })
@@ -30,7 +30,7 @@ describe('TldrawBuilder', () => {
30
30
  });
31
31
 
32
32
  test('connects via explicit points when shapes are not referenced', ({ expect }) => {
33
- const content = new TldrawBuilder()
33
+ const content = new RecordBuilder()
34
34
  .arrow({ id: 'arrow', start: { x: 0, y: 0 }, end: { x: 200, y: 100 } })
35
35
  .line({ id: 'line', start: { x: 0, y: 0 }, end: { x: 100, y: 0 } })
36
36
  .build();
@@ -44,14 +44,14 @@ describe('TldrawBuilder', () => {
44
44
  });
45
45
 
46
46
  test('throws on unknown connector handles and duplicate shape ids', ({ expect }) => {
47
- expect(() => new TldrawBuilder().rectangle({ id: 'a' }).arrow({ from: 'a', to: 'missing' })).to.throw(
47
+ expect(() => new RecordBuilder().rectangle({ id: 'a' }).arrow({ from: 'a', to: 'missing' })).to.throw(
48
48
  /unknown target shape/,
49
49
  );
50
- expect(() => new TldrawBuilder().rectangle({ id: 'dup' }).ellipse({ id: 'dup' })).to.throw(/duplicate shape id/);
50
+ expect(() => new RecordBuilder().rectangle({ id: 'dup' }).ellipse({ id: 'dup' })).to.throw(/duplicate shape id/);
51
51
  });
52
52
 
53
53
  test('produces records that pass tldraw schema validation', ({ expect }) => {
54
- const content = new TldrawBuilder()
54
+ const content = new RecordBuilder()
55
55
  .rectangle({ id: 'a', x: 0, y: 0, text: 'A' })
56
56
  .circle({ id: 'b', x: 320, y: 0, text: 'B' })
57
57
  .geo('star', { id: 'c', x: 160, y: 240, color: 'yellow' })
@@ -134,14 +134,14 @@ type Box = { id: string; x: number; y: number; w: number; h: number };
134
134
  *
135
135
  * @example
136
136
  * ```ts
137
- * const content = new TldrawBuilder()
137
+ * const content = new RecordBuilder()
138
138
  * .rectangle({ id: 'a', x: 0, y: 0, text: 'DXOS', color: 'blue' })
139
139
  * .ellipse({ id: 'b', x: 320, y: 0, text: 'ECHO', color: 'green' })
140
140
  * .arrow({ from: 'a', to: 'b' })
141
141
  * .build();
142
142
  * ```
143
143
  */
144
- export class TldrawBuilder {
144
+ export class RecordBuilder {
145
145
  readonly #records: CanvasContent = {};
146
146
  readonly #boxes = new Map<string, Box>();
147
147
  readonly #metaFor?: (handle: string) => Record<string, any>;
@@ -3,29 +3,66 @@
3
3
  //
4
4
 
5
5
  //
6
- // tldraw implementation of the illustrator `SketchBuilder` contract: maps the scene DSL
7
- // onto the tldraw record map stored in `Tldraw.Canvas.content`.
6
+ // tldraw implementation of the illustrator `DrawingBuilder` contract: supplies the record
7
+ // encoding (identity in `meta`, page scaffold, binding cleanup) and inherits the command
8
+ // semantics from the shared content handler.
8
9
  //
9
10
 
10
- import { Obj } from '@dxos/echo';
11
- import { invariant } from '@dxos/invariant';
12
- import { type ApplyResult, type SketchBuilder } from '@dxos/plugin-illustrator/model';
11
+ import {
12
+ type ContentHandler,
13
+ type ContentMap,
14
+ type Identity,
15
+ type Scene,
16
+ applyCommands as applyContentCommands,
17
+ makeBuilder,
18
+ nextIndex,
19
+ } from '@dxos/plugin-illustrator/model';
13
20
 
14
21
  import { Tldraw } from '../types';
15
- import { applyCommands } from './apply';
16
- import { readScene } from './read';
22
+ import { elementBoxes, readScene } from './read';
23
+ import { DOCUMENT_ID, PAGE_ID } from './RecordBuilder';
24
+ import { renderObject } from './render';
17
25
 
18
- export const tldrawBuilder: SketchBuilder = {
19
- read: (canvas) => {
20
- invariant(Obj.instanceOf(Tldraw.Canvas, canvas));
21
- return readScene(canvas.content);
26
+ const isShape = (record: any) => record?.typeName === 'shape';
27
+
28
+ const handler: ContentHandler = {
29
+ identify: (record) => {
30
+ if (!isShape(record) || typeof record.meta?.object !== 'string' || typeof record.meta?.element !== 'string') {
31
+ return undefined;
32
+ }
33
+ return { object: record.meta.object, element: record.meta.element } satisfies Identity;
34
+ },
35
+
36
+ render: (object, placement, content) =>
37
+ renderObject(object, { ...placement, indexStart: nextIndex(content, isShape), external: elementBoxes(content) }),
38
+
39
+ read: (content) => readScene(content),
40
+
41
+ translate: (record, delta) => {
42
+ record.x += delta.x;
43
+ record.y += delta.y;
22
44
  },
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;
45
+
46
+ scaffold: (content: ContentMap) => {
47
+ content[DOCUMENT_ID] ??= { gridSize: 10, name: '', meta: {}, id: DOCUMENT_ID, typeName: 'document' };
48
+ content[PAGE_ID] ??= { meta: {}, id: PAGE_ID, name: 'Page 1', index: 'a1', typeName: 'page' };
49
+ },
50
+
51
+ // Bindings reference shapes by id; drop any left dangling by a deletion.
52
+ prune: (content: ContentMap) => {
53
+ for (const [key, record] of Object.entries(content)) {
54
+ if (record?.typeName === 'binding' && (!content[record.fromId] || !content[record.toId])) {
55
+ delete content[key];
56
+ }
57
+ }
30
58
  },
31
59
  };
60
+
61
+ export const TldrawBuilder = makeBuilder({ schema: Tldraw.TLDRAW_SCHEMA, handler });
62
+
63
+ /**
64
+ * Apply commands straight to a content map, bypassing ECHO — for tests and tooling that hold
65
+ * raw tldraw records. The object-aware path is {@link TldrawBuilder}.
66
+ */
67
+ export const applyCommands = (content: ContentMap, commands: readonly Scene.Command[]) =>
68
+ applyContentCommands(content, commands, handler);
@@ -2,8 +2,7 @@
2
2
  // Copyright 2026 DXOS.org
3
3
  //
4
4
 
5
- export * from './apply';
6
5
  export * from './builder';
7
6
  export * from './read';
8
7
  export * from './render';
9
- export * from './TldrawBuilder';
8
+ export * from './RecordBuilder';
package/src/model/read.ts CHANGED
@@ -10,8 +10,8 @@
10
10
 
11
11
  import { type ReadResult, type ReadWorldObject, type Scene } from '@dxos/plugin-illustrator/model';
12
12
 
13
+ import { type CanvasContent } from './RecordBuilder';
13
14
  import { type ExternalBox } from './render';
14
- import { type CanvasContent } from './TldrawBuilder';
15
15
 
16
16
  type ShapeRecord = Record<string, any>;
17
17
 
@@ -17,9 +17,9 @@ import {
17
17
  type Fill,
18
18
  type PathProps,
19
19
  type Point,
20
+ RecordBuilder,
20
21
  type ShapeProps,
21
- TldrawBuilder,
22
- } from './TldrawBuilder';
22
+ } from './RecordBuilder';
23
23
 
24
24
  export const DEFAULT_SCALE = 1;
25
25
 
@@ -79,7 +79,7 @@ export const renderObject = (object: Scene.WorldObject, options: RenderOptions):
79
79
  const length = (units: number) => units * scale;
80
80
 
81
81
  const kinds = new Map<string, string>();
82
- const builder = new TldrawBuilder({
82
+ const builder = new RecordBuilder({
83
83
  indexStart,
84
84
  metaFor: (handle) => {
85
85
  const [objectId, elementId] = splitHandle(handle);