viteboard 0.1.1 → 0.1.3

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 (83) hide show
  1. package/bin/content-api-plugin.mjs +722 -0
  2. package/bin/viteboard.mjs +3 -7
  3. package/content/docs/index.md +1 -1
  4. package/content/docs/product-roadmap.board.json +4 -4
  5. package/content/machine/board-v1.schema.json +138 -0
  6. package/content/machine/example.board.json +217 -0
  7. package/content/machine/guide.md +270 -0
  8. package/content/machine/llms.txt +31 -0
  9. package/content/machine/viteboard.json +62 -0
  10. package/dist/assets/index-D2W76uu9.js +96 -0
  11. package/dist/assets/index-rMkUjGXP.css +1 -0
  12. package/dist/index.html +4 -2
  13. package/package.json +10 -12
  14. package/vite.config.ts +619 -38
  15. package/dist/assets/index-C30zRPsW.js +0 -60
  16. package/dist/assets/index-e5aqtN8W.css +0 -1
  17. package/index.html +0 -13
  18. package/src/app/AppController.ts +0 -962
  19. package/src/app/BoardState.ts +0 -130
  20. package/src/app/ClipboardService.ts +0 -159
  21. package/src/app/CommandManager.ts +0 -66
  22. package/src/app/ElementActions.ts +0 -152
  23. package/src/app/EmbedRegionSelector.ts +0 -103
  24. package/src/app/ExportPng.ts +0 -107
  25. package/src/app/ImageInsertService.ts +0 -141
  26. package/src/app/KeyboardShortcuts.ts +0 -124
  27. package/src/app/main.ts +0 -6
  28. package/src/board/BoardPreview.ts +0 -189
  29. package/src/board/BoardService.ts +0 -222
  30. package/src/canvas/CanvasRenderer.ts +0 -253
  31. package/src/canvas/CanvasSurface.ts +0 -70
  32. package/src/canvas/HitTester.ts +0 -110
  33. package/src/canvas/ImageCache.ts +0 -123
  34. package/src/canvas/PerformanceMonitor.ts +0 -31
  35. package/src/canvas/RenderScheduler.ts +0 -26
  36. package/src/canvas/Viewport.ts +0 -77
  37. package/src/content/ContentApi.ts +0 -258
  38. package/src/content/Markdown.ts +0 -431
  39. package/src/content/MarkdownView.ts +0 -70
  40. package/src/editor/DocEditor.ts +0 -799
  41. package/src/editor/htmlToMarkdown.ts +0 -333
  42. package/src/elements/renderElement.ts +0 -598
  43. package/src/elements/types.ts +0 -119
  44. package/src/shell/Shell.ts +0 -2948
  45. package/src/shell/Sidebar.ts +0 -1651
  46. package/src/storage/AssetStore.ts +0 -86
  47. package/src/storage/BoardSerializer.ts +0 -114
  48. package/src/storage/ImportExportService.ts +0 -153
  49. package/src/storage/IndexedDbStore.ts +0 -92
  50. package/src/storage/LocalBoardStore.ts +0 -104
  51. package/src/styles.css +0 -3332
  52. package/src/templates/handoffTemplate.ts +0 -34
  53. package/src/templates/helpers.ts +0 -132
  54. package/src/templates/index.ts +0 -79
  55. package/src/templates/journeyMapTemplate.ts +0 -52
  56. package/src/templates/opportunityTreeTemplate.ts +0 -45
  57. package/src/templates/personaTemplate.ts +0 -41
  58. package/src/templates/prioritizationMatrixTemplate.ts +0 -44
  59. package/src/templates/requirementsFlowTemplate.ts +0 -45
  60. package/src/templates/screenshotReviewTemplate.ts +0 -52
  61. package/src/templates/systemDiagramTemplate.ts +0 -41
  62. package/src/templates/workflowTemplate.ts +0 -35
  63. package/src/testing/StressTestGenerator.ts +0 -134
  64. package/src/testing/StressTestPanel.ts +0 -64
  65. package/src/tools/ArrowTool.ts +0 -97
  66. package/src/tools/ImageTool.ts +0 -39
  67. package/src/tools/PanTool.ts +0 -34
  68. package/src/tools/SelectTool.ts +0 -377
  69. package/src/tools/ShapeTool.ts +0 -106
  70. package/src/tools/StickyTool.ts +0 -69
  71. package/src/tools/TaskTool.ts +0 -52
  72. package/src/tools/TextTool.ts +0 -45
  73. package/src/tools/ToolContext.ts +0 -51
  74. package/src/tools/ToolController.ts +0 -178
  75. package/src/ui/Modal.ts +0 -58
  76. package/src/ui/PerformanceOverlay.ts +0 -75
  77. package/src/ui/StorageManager.ts +0 -94
  78. package/src/ui/TemplatePicker.ts +0 -67
  79. package/src/ui/TextEditorOverlay.ts +0 -262
  80. package/src/ui/Toolbar.ts +0 -189
  81. package/src/ui/TopControls.ts +0 -137
  82. package/src/ui/icons.ts +0 -56
  83. package/tsconfig.json +0 -19
package/bin/viteboard.mjs CHANGED
@@ -4,6 +4,7 @@ import { createServer } from "vite";
4
4
  import { fileURLToPath } from "node:url";
5
5
  import path from "node:path";
6
6
  import fs from "node:fs";
7
+ import { createContentApiPlugin } from "./content-api-plugin.mjs";
7
8
 
8
9
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
9
10
  const packageRoot = path.resolve(__dirname, "..");
@@ -29,15 +30,10 @@ async function main() {
29
30
  await server.listen();
30
31
  server.printUrls();
31
32
  } else if (hasDist) {
32
- // Installed as a package — serve the pre-built dist with the API plugin
33
- // We need to dynamically import the config since it may be .ts
34
- const { default: configFn } = await import(path.join(packageRoot, "vite.config.ts"));
35
- const config = typeof configFn === "function" ? configFn({}) : configFn;
36
- const plugins = Array.isArray(config.plugins) ? config.plugins : [];
37
-
33
+ // Installed as a package — serve the pre-built dist with the runtime JS plugin.
38
34
  const server = await createServer({
39
35
  root: path.join(packageRoot, "dist"),
40
- plugins,
36
+ plugins: [createContentApiPlugin()],
41
37
  server: { port, host, open: true },
42
38
  });
43
39
  await server.listen();
@@ -25,7 +25,7 @@ ViteBoard discovers `docs/` folders inside your projects and gives you a workspa
25
25
  ## Keyboard Shortcuts
26
26
 
27
27
  | Shortcut | Action |
28
- |----------|--------|
28
+ | --- | --- |
29
29
  | `Cmd/Ctrl K` | Open search |
30
30
  | `Escape` | Close modals and search |
31
31
 
@@ -3,11 +3,11 @@
3
3
  "id": "product-roadmap",
4
4
  "title": "Product Roadmap",
5
5
  "createdAt": 1718000000000,
6
- "updatedAt": 1784600810590,
6
+ "updatedAt": 1785447846332,
7
7
  "viewport": {
8
- "offsetX": -3547.7083035349933,
9
- "offsetY": -2049.683417968947,
10
- "scale": 0.7351121006160558
8
+ "offsetX": -2867.916693773289,
9
+ "offsetY": -1155.98783153076,
10
+ "scale": 0.6195352130520436
11
11
  },
12
12
  "theme": "light",
13
13
  "elements": [
@@ -0,0 +1,138 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://viteboard.local/board-v1.schema.json",
4
+ "title": "ViteBoard Board v1",
5
+ "type": "object",
6
+ "required": [
7
+ "version",
8
+ "id",
9
+ "title",
10
+ "createdAt",
11
+ "updatedAt",
12
+ "viewport",
13
+ "theme",
14
+ "elements",
15
+ "assetIds"
16
+ ],
17
+ "properties": {
18
+ "$schema": {
19
+ "type": "string"
20
+ },
21
+ "version": {
22
+ "const": 1
23
+ },
24
+ "id": {
25
+ "type": "string",
26
+ "minLength": 1
27
+ },
28
+ "title": {
29
+ "type": "string"
30
+ },
31
+ "createdAt": {
32
+ "type": "number"
33
+ },
34
+ "updatedAt": {
35
+ "type": "number"
36
+ },
37
+ "viewport": {
38
+ "type": "object",
39
+ "required": ["offsetX", "offsetY", "scale"],
40
+ "properties": {
41
+ "offsetX": { "type": "number" },
42
+ "offsetY": { "type": "number" },
43
+ "scale": { "type": "number" }
44
+ },
45
+ "additionalProperties": false
46
+ },
47
+ "theme": {
48
+ "enum": ["light", "dark"]
49
+ },
50
+ "elements": {
51
+ "type": "array",
52
+ "items": { "$ref": "#/$defs/element" }
53
+ },
54
+ "assetIds": {
55
+ "type": "array",
56
+ "items": { "type": "string" }
57
+ }
58
+ },
59
+ "additionalProperties": false,
60
+ "$defs": {
61
+ "elementStyle": {
62
+ "type": "object",
63
+ "properties": {
64
+ "fill": { "type": "string" },
65
+ "stroke": { "type": "string" },
66
+ "strokeWidth": { "type": "number" },
67
+ "opacity": { "type": "number" },
68
+ "borderRadius": { "type": "number" },
69
+ "shadow": { "type": "boolean" },
70
+ "fontSize": { "type": "number" },
71
+ "fontFamily": { "type": "string" },
72
+ "fontWeight": { "type": "string" },
73
+ "textColor": { "type": "string" },
74
+ "arrowStart": { "type": "boolean" },
75
+ "arrowEnd": { "type": "boolean" },
76
+ "strokeDash": {
77
+ "type": "array",
78
+ "items": { "type": "number" }
79
+ }
80
+ },
81
+ "additionalProperties": true
82
+ },
83
+ "element": {
84
+ "type": "object",
85
+ "required": [
86
+ "id",
87
+ "type",
88
+ "x",
89
+ "y",
90
+ "width",
91
+ "height",
92
+ "rotation",
93
+ "zIndex",
94
+ "locked",
95
+ "hidden",
96
+ "createdAt",
97
+ "updatedAt",
98
+ "style",
99
+ "data"
100
+ ],
101
+ "properties": {
102
+ "id": { "type": "string", "minLength": 1 },
103
+ "type": {
104
+ "enum": [
105
+ "sticky",
106
+ "task",
107
+ "rectangle",
108
+ "ellipse",
109
+ "text",
110
+ "line",
111
+ "arrow",
112
+ "connector",
113
+ "image",
114
+ "screenshot",
115
+ "frame",
116
+ "templateGroup"
117
+ ]
118
+ },
119
+ "x": { "type": "number" },
120
+ "y": { "type": "number" },
121
+ "width": { "type": "number" },
122
+ "height": { "type": "number" },
123
+ "rotation": { "type": "number" },
124
+ "zIndex": { "type": "number" },
125
+ "locked": { "type": "boolean" },
126
+ "hidden": { "type": "boolean" },
127
+ "createdAt": { "type": "number" },
128
+ "updatedAt": { "type": "number" },
129
+ "style": { "$ref": "#/$defs/elementStyle" },
130
+ "data": {
131
+ "type": "object",
132
+ "additionalProperties": true
133
+ }
134
+ },
135
+ "additionalProperties": false
136
+ }
137
+ }
138
+ }
@@ -0,0 +1,217 @@
1
+ {
2
+ "$schema": "/board-v1.schema.json",
3
+ "version": 1,
4
+ "id": "examples/system-flow",
5
+ "title": "System Flow Example",
6
+ "createdAt": 1784635200000,
7
+ "updatedAt": 1784635200000,
8
+ "viewport": {
9
+ "offsetX": 80,
10
+ "offsetY": 80,
11
+ "scale": 0.58
12
+ },
13
+ "theme": "light",
14
+ "elements": [
15
+ {
16
+ "id": "frame-example",
17
+ "type": "frame",
18
+ "x": 0,
19
+ "y": 0,
20
+ "width": 1240,
21
+ "height": 840,
22
+ "rotation": 0,
23
+ "zIndex": 1,
24
+ "locked": false,
25
+ "hidden": false,
26
+ "createdAt": 1784635200000,
27
+ "updatedAt": 1784635200000,
28
+ "style": {
29
+ "fill": "#ffffff",
30
+ "stroke": "#e5e7eb",
31
+ "strokeWidth": 1.5,
32
+ "borderRadius": 20,
33
+ "shadow": true
34
+ },
35
+ "data": {
36
+ "text": "",
37
+ "shapeKind": "rectangle"
38
+ }
39
+ },
40
+ {
41
+ "id": "title-example",
42
+ "type": "text",
43
+ "x": 40,
44
+ "y": 50,
45
+ "width": 500,
46
+ "height": 40,
47
+ "rotation": 0,
48
+ "zIndex": 2,
49
+ "locked": false,
50
+ "hidden": false,
51
+ "createdAt": 1784635200000,
52
+ "updatedAt": 1784635200000,
53
+ "style": {
54
+ "fontSize": 24,
55
+ "fontWeight": "700",
56
+ "textColor": "#1f2933"
57
+ },
58
+ "data": {
59
+ "text": "System flow"
60
+ }
61
+ },
62
+ {
63
+ "id": "actor-user",
64
+ "type": "ellipse",
65
+ "x": 120,
66
+ "y": 320,
67
+ "width": 190,
68
+ "height": 110,
69
+ "rotation": 0,
70
+ "zIndex": 3,
71
+ "locked": false,
72
+ "hidden": false,
73
+ "createdAt": 1784635200000,
74
+ "updatedAt": 1784635200000,
75
+ "style": {
76
+ "fill": "#dcfce7",
77
+ "stroke": "#16a34a",
78
+ "strokeWidth": 1.5,
79
+ "shadow": true,
80
+ "fontSize": 14,
81
+ "fontWeight": "650",
82
+ "textColor": "#1f2933"
83
+ },
84
+ "data": {
85
+ "text": "User"
86
+ }
87
+ },
88
+ {
89
+ "id": "surface-ui",
90
+ "type": "rectangle",
91
+ "x": 430,
92
+ "y": 330,
93
+ "width": 220,
94
+ "height": 90,
95
+ "rotation": 0,
96
+ "zIndex": 4,
97
+ "locked": false,
98
+ "hidden": false,
99
+ "createdAt": 1784635200000,
100
+ "updatedAt": 1784635200000,
101
+ "style": {
102
+ "fill": "#4dabf7",
103
+ "stroke": "#1d4ed8",
104
+ "strokeWidth": 1.5,
105
+ "borderRadius": 999,
106
+ "shadow": true,
107
+ "fontSize": 14,
108
+ "fontWeight": "650",
109
+ "textColor": "#ffffff"
110
+ },
111
+ "data": {
112
+ "text": "ViteBoard UI",
113
+ "shapeKind": "pill"
114
+ }
115
+ },
116
+ {
117
+ "id": "store-docs",
118
+ "type": "rectangle",
119
+ "x": 840,
120
+ "y": 320,
121
+ "width": 220,
122
+ "height": 110,
123
+ "rotation": 0,
124
+ "zIndex": 5,
125
+ "locked": false,
126
+ "hidden": false,
127
+ "createdAt": 1784635200000,
128
+ "updatedAt": 1784635200000,
129
+ "style": {
130
+ "fill": "#86efac",
131
+ "stroke": "#15803d",
132
+ "strokeWidth": 1.5,
133
+ "shadow": true,
134
+ "fontSize": 14,
135
+ "fontWeight": "650",
136
+ "textColor": "#1f2933"
137
+ },
138
+ "data": {
139
+ "text": "Docs root\nfiles on disk",
140
+ "shapeKind": "cylinder"
141
+ }
142
+ },
143
+ {
144
+ "id": "edge-user-ui",
145
+ "type": "arrow",
146
+ "x": 310,
147
+ "y": 375,
148
+ "width": 120,
149
+ "height": 0,
150
+ "rotation": 0,
151
+ "zIndex": 6,
152
+ "locked": false,
153
+ "hidden": false,
154
+ "createdAt": 1784635200000,
155
+ "updatedAt": 1784635200000,
156
+ "style": {
157
+ "stroke": "#64748b",
158
+ "strokeWidth": 2,
159
+ "arrowEnd": true,
160
+ "arrowStart": false
161
+ },
162
+ "data": {
163
+ "text": "edits",
164
+ "labelPosition": "center"
165
+ }
166
+ },
167
+ {
168
+ "id": "edge-ui-store",
169
+ "type": "arrow",
170
+ "x": 650,
171
+ "y": 375,
172
+ "width": 190,
173
+ "height": 0,
174
+ "rotation": 0,
175
+ "zIndex": 7,
176
+ "locked": false,
177
+ "hidden": false,
178
+ "createdAt": 1784635200000,
179
+ "updatedAt": 1784635200000,
180
+ "style": {
181
+ "stroke": "#64748b",
182
+ "strokeWidth": 2,
183
+ "arrowEnd": true,
184
+ "arrowStart": false
185
+ },
186
+ "data": {
187
+ "text": "persists",
188
+ "labelPosition": "center"
189
+ }
190
+ },
191
+ {
192
+ "id": "note-example",
193
+ "type": "sticky",
194
+ "x": 140,
195
+ "y": 650,
196
+ "width": 360,
197
+ "height": 90,
198
+ "rotation": 0,
199
+ "zIndex": 8,
200
+ "locked": false,
201
+ "hidden": false,
202
+ "createdAt": 1784635200000,
203
+ "updatedAt": 1784635200000,
204
+ "style": {
205
+ "fill": "#fef3c7",
206
+ "borderRadius": 12,
207
+ "shadow": true,
208
+ "fontSize": 11,
209
+ "textColor": "#1f2933"
210
+ },
211
+ "data": {
212
+ "text": "Keep embed crops inside the frame and leave enough space around labeled arrows."
213
+ }
214
+ }
215
+ ],
216
+ "assetIds": []
217
+ }
@@ -0,0 +1,270 @@
1
+ # ViteBoard Authoring Guide
2
+
3
+ ViteBoard is a local-first documentation workspace. Humans browse it through the web UI. Machines can use the same origin to discover how the app is structured, then use normal filesystem tools to create and update the underlying files.
4
+
5
+ This guide is read-only. It explains how to work with ViteBoard content and how to cooperate with a human who may be using the app at the same time.
6
+
7
+ ## Connection rule
8
+
9
+ When a user gives you a ViteBoard URL, keep using that exact origin.
10
+
11
+ Example:
12
+
13
+ - if the user says `http://localhost:5173`, keep using `http://localhost:5173`
14
+ - do not replace it with `http://127.0.0.1:5173`
15
+
16
+ Some local ViteBoard sessions bind only one loopback interface, so `localhost` and `127.0.0.1` are not always equivalent.
17
+
18
+ ## 1. Workspace model
19
+
20
+ - Projects are registered in the local workspace.
21
+ - Every descendant directory named `docs` becomes a docs root.
22
+ - Markdown documents, capabilities, boards, and assets live inside a docs root.
23
+
24
+ Important discovery endpoints:
25
+
26
+ - `GET /__api/workspace`
27
+ - `GET /__api/project-tree?projectId={projectId}`
28
+ - `GET /__api/tree?rootId={rootId}`
29
+
30
+ The machine can navigate the same app surfaces a human uses:
31
+
32
+ - workspace home: `#/`
33
+ - project wiki: `#/project/{projectId}`
34
+ - project board index: `#/project/{projectId}/boards`
35
+ - document view: `#/root/{rootId}/docs/{path}`
36
+ - root board list: `#/root/{rootId}/boards`
37
+ - board editor: `#/root/{rootId}/boards/{boardId}`
38
+ - capability index: `#/root/{rootId}/capabilities`
39
+ - capability document: `#/root/{rootId}/capabilities/{path}`
40
+ - directory wiki: `#/root/{rootId}/directory/{path}`
41
+
42
+ Open these routes directly when you want the human UI to focus a specific page, document, board, or wiki surface.
43
+
44
+ If a target project is not in the workspace yet, register it with:
45
+
46
+ ```http
47
+ POST /__api/workspace/project
48
+ Content-Type: application/json
49
+
50
+ {
51
+ "rootPath": "/absolute/project/path",
52
+ "label": "optional label"
53
+ }
54
+ ```
55
+
56
+ If the project has no docs root yet, create one with:
57
+
58
+ ```http
59
+ POST /__api/workspace/project/docs-root
60
+ Content-Type: application/json
61
+
62
+ { "projectId": "project-..." }
63
+ ```
64
+
65
+ ## 2. Files and IDs
66
+
67
+ ### Markdown documents
68
+
69
+ - Stored as `*.md` beneath a docs root.
70
+ - Read and write them directly as normal Markdown files.
71
+
72
+ ### Boards
73
+
74
+ - Stored as `*.board.json` anywhere beneath a docs root.
75
+ - Board ID = path relative to the docs root, without `.board.json`.
76
+
77
+ Example:
78
+
79
+ ```text
80
+ docs/architecture/system-overview.board.json
81
+ ```
82
+
83
+ Board ID:
84
+
85
+ ```text
86
+ architecture/system-overview
87
+ ```
88
+
89
+ ### Capabilities
90
+
91
+ - Stored as managed directory branches beneath `capabilities/` inside a docs root.
92
+ - A capability branch is the unit, not just one markdown file.
93
+ - Human entrypoint: `index.md`
94
+ - Machine entrypoint: `llms.txt`
95
+
96
+ Example:
97
+
98
+ ```text
99
+ docs/capabilities/automation/docs-refresh/
100
+ index.md
101
+ llms.txt
102
+ scripts/
103
+ ```
104
+
105
+ Capability route path:
106
+
107
+ ```text
108
+ automation/docs-refresh
109
+ ```
110
+
111
+ Machines should treat the whole branch as the capability context and use `llms.txt` as the first machine-facing file when it exists.
112
+
113
+ ## 3. Markdown board embeds
114
+
115
+ Use fenced `board` blocks inside Markdown:
116
+
117
+ ````markdown
118
+ ```board
119
+ src: ./system-overview
120
+ region: 20, 20, 1200, 760
121
+ height: 500
122
+ title: System overview
123
+ ```
124
+ ````
125
+
126
+ Fields:
127
+
128
+ - `src` required
129
+ - `region` optional, `x, y, width, height`
130
+ - `height` optional, minimum `120`, default `360`
131
+ - `title` optional
132
+
133
+ Relative `src` paths resolve from the Markdown document directory.
134
+
135
+ ## 4. Board document model
136
+
137
+ Every board should contain:
138
+
139
+ - `version: 1`
140
+ - `id`
141
+ - `title`
142
+ - `createdAt`
143
+ - `updatedAt`
144
+ - `viewport`
145
+ - `theme`
146
+ - `elements`
147
+ - `assetIds`
148
+
149
+ Preferred form:
150
+
151
+ ```json
152
+ {
153
+ "$schema": "/board-v1.schema.json",
154
+ "version": 1,
155
+ "id": "architecture/system-overview",
156
+ "title": "System Overview",
157
+ "createdAt": 1784635200000,
158
+ "updatedAt": 1784635200000,
159
+ "viewport": { "offsetX": 80, "offsetY": 80, "scale": 0.24 },
160
+ "theme": "light",
161
+ "elements": [],
162
+ "assetIds": []
163
+ }
164
+ ```
165
+
166
+ Use the schema at `/board-v1.schema.json` for the full contract.
167
+
168
+ ## 5. Element families
169
+
170
+ Supported element types:
171
+
172
+ - `sticky`
173
+ - `task`
174
+ - `rectangle`
175
+ - `ellipse`
176
+ - `text`
177
+ - `line`
178
+ - `arrow`
179
+ - `connector`
180
+ - `image`
181
+ - `screenshot`
182
+ - `frame`
183
+ - `templateGroup`
184
+
185
+ Important shape semantics:
186
+
187
+ - Rectangle: general system or concept
188
+ - Ellipse: actor or user group
189
+ - Diamond: decision or routing question
190
+ - Pill: stable platform or product identity
191
+ - Parallelogram: interface or input/output boundary
192
+ - Hexagon: platform or partner capability
193
+ - Cylinder: authoritative data store
194
+ - Sticky: caveat or interpretation
195
+
196
+ Non-ellipse graphic shapes usually use:
197
+
198
+ ```json
199
+ {
200
+ "type": "rectangle",
201
+ "data": { "shapeKind": "diamond" }
202
+ }
203
+ ```
204
+
205
+ ## 6. Diagram composition
206
+
207
+ - One board should answer one coherent family of questions.
208
+ - One white frame should contain one diagram or visual cluster.
209
+ - Choose one dominant reading direction.
210
+ - Keep enough whitespace above and below the main node bounds.
211
+ - Use dashed arrows for optional or weaker relationships.
212
+ - Remove arrow labels when the relationship is already obvious.
213
+ - Keep Markdown embed crops fully inside a white frame.
214
+
215
+ ## 7. Updating existing content
216
+
217
+ When changing a hand-edited board:
218
+
219
+ 1. Preserve the board ID.
220
+ 2. Preserve unrelated elements.
221
+ 3. Preserve `createdAt`.
222
+ 4. Update `updatedAt`.
223
+ 5. Keep stable element IDs where possible.
224
+ 6. Recalculate arrow geometry if nodes moved.
225
+ 7. Adjust any embed crop if the layout changed.
226
+
227
+ When changing a Markdown document:
228
+
229
+ 1. Preserve unrelated sections.
230
+ 2. Preserve semantic frontmatter such as `title`, `summary`, and `tags`.
231
+ 3. Update fenced `board` embeds if board location or crop changes.
232
+
233
+ ## 8. Cooperative live use
234
+
235
+ If you want the app to reflect that an agent is connected and editing:
236
+
237
+ 1. Register an actor session.
238
+ 2. Heartbeat while active.
239
+ 3. Acquire a lock before writing a board or Markdown file.
240
+ 4. Write your file changes.
241
+ 5. Release the lock.
242
+
243
+ Actor sessions can include the current route, current doc or board target, and for boards the currently selected element IDs. That makes prompts like “update the section I have selected” possible when the actor and the app are cooperating on the same live session.
244
+
245
+ The lock protocol is cooperative. While your lock is active, the human UI becomes read-only for that target.
246
+
247
+ Session endpoints:
248
+
249
+ - `POST /__api/actors/session`
250
+ - `POST /__api/actors/heartbeat`
251
+ - `DELETE /__api/actors/session?actorId=...`
252
+
253
+ Lock endpoints:
254
+
255
+ - `POST /__api/locks/acquire`
256
+ - `POST /__api/locks/release`
257
+
258
+ Live events:
259
+
260
+ - `GET /__api/events`
261
+
262
+ ## 9. Completion checklist
263
+
264
+ - Board JSON parses.
265
+ - Board ID matches its path from the docs root.
266
+ - Element IDs are unique.
267
+ - Geometry is finite.
268
+ - Embeds resolve by relative path.
269
+ - Embed crops stay inside the intended frame.
270
+ - The resulting layout is readable to a human.