viteboard 0.1.0 → 0.1.2

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 (90) hide show
  1. package/README.md +110 -61
  2. package/content/docs/guide/boards-in-markdown.md +28 -26
  3. package/content/docs/guide/boards.md +44 -82
  4. package/content/docs/guide/getting-started.md +36 -48
  5. package/content/docs/index.md +21 -32
  6. package/content/docs/product-roadmap.board.json +4 -4
  7. package/content/machine/board-v1.schema.json +138 -0
  8. package/content/machine/example.board.json +217 -0
  9. package/content/machine/guide.md +270 -0
  10. package/content/machine/llms.txt +31 -0
  11. package/content/machine/viteboard.json +62 -0
  12. package/dist/assets/index-D2W76uu9.js +96 -0
  13. package/dist/assets/index-rMkUjGXP.css +1 -0
  14. package/dist/index.html +4 -2
  15. package/package.json +10 -12
  16. package/vite.config.ts +635 -48
  17. package/content/docs/capabilities.md +0 -89
  18. package/content/docs/docs.md +0 -84
  19. package/content/docs/guide/test.md +0 -6
  20. package/content/docs/test.md +0 -219
  21. package/content/docs/tetst-dir/test-board.board.json +0 -15
  22. package/content/docs/tetst-dir/test-test.md +0 -1
  23. package/content/docs/workspace.md +0 -73
  24. package/dist/assets/index-CEpxLM2o.css +0 -1
  25. package/dist/assets/index-D4xvJdEQ.js +0 -60
  26. package/index.html +0 -13
  27. package/src/app/AppController.ts +0 -955
  28. package/src/app/BoardState.ts +0 -130
  29. package/src/app/ClipboardService.ts +0 -159
  30. package/src/app/CommandManager.ts +0 -66
  31. package/src/app/ElementActions.ts +0 -152
  32. package/src/app/EmbedRegionSelector.ts +0 -103
  33. package/src/app/ExportPng.ts +0 -107
  34. package/src/app/ImageInsertService.ts +0 -141
  35. package/src/app/KeyboardShortcuts.ts +0 -124
  36. package/src/app/main.ts +0 -6
  37. package/src/board/BoardPreview.ts +0 -189
  38. package/src/board/BoardService.ts +0 -222
  39. package/src/canvas/CanvasRenderer.ts +0 -253
  40. package/src/canvas/CanvasSurface.ts +0 -70
  41. package/src/canvas/HitTester.ts +0 -110
  42. package/src/canvas/ImageCache.ts +0 -123
  43. package/src/canvas/PerformanceMonitor.ts +0 -31
  44. package/src/canvas/RenderScheduler.ts +0 -26
  45. package/src/canvas/Viewport.ts +0 -77
  46. package/src/content/ContentApi.ts +0 -258
  47. package/src/content/Markdown.ts +0 -431
  48. package/src/content/MarkdownView.ts +0 -70
  49. package/src/editor/DocEditor.ts +0 -799
  50. package/src/editor/htmlToMarkdown.ts +0 -333
  51. package/src/elements/renderElement.ts +0 -509
  52. package/src/elements/types.ts +0 -118
  53. package/src/shell/Shell.ts +0 -2950
  54. package/src/shell/Sidebar.ts +0 -1352
  55. package/src/storage/AssetStore.ts +0 -86
  56. package/src/storage/BoardSerializer.ts +0 -114
  57. package/src/storage/ImportExportService.ts +0 -153
  58. package/src/storage/IndexedDbStore.ts +0 -92
  59. package/src/storage/LocalBoardStore.ts +0 -104
  60. package/src/styles.css +0 -3257
  61. package/src/templates/helpers.ts +0 -124
  62. package/src/templates/index.ts +0 -65
  63. package/src/templates/journeyMapTemplate.ts +0 -52
  64. package/src/templates/opportunityTreeTemplate.ts +0 -45
  65. package/src/templates/personaTemplate.ts +0 -41
  66. package/src/templates/prioritizationMatrixTemplate.ts +0 -44
  67. package/src/templates/requirementsFlowTemplate.ts +0 -45
  68. package/src/templates/screenshotReviewTemplate.ts +0 -52
  69. package/src/templates/systemDiagramTemplate.ts +0 -41
  70. package/src/testing/StressTestGenerator.ts +0 -134
  71. package/src/testing/StressTestPanel.ts +0 -64
  72. package/src/tools/ArrowTool.ts +0 -87
  73. package/src/tools/ImageTool.ts +0 -39
  74. package/src/tools/PanTool.ts +0 -34
  75. package/src/tools/SelectTool.ts +0 -377
  76. package/src/tools/ShapeTool.ts +0 -106
  77. package/src/tools/StickyTool.ts +0 -69
  78. package/src/tools/TaskTool.ts +0 -52
  79. package/src/tools/TextTool.ts +0 -45
  80. package/src/tools/ToolContext.ts +0 -44
  81. package/src/tools/ToolController.ts +0 -178
  82. package/src/ui/Modal.ts +0 -58
  83. package/src/ui/PerformanceOverlay.ts +0 -75
  84. package/src/ui/StorageManager.ts +0 -94
  85. package/src/ui/TemplatePicker.ts +0 -67
  86. package/src/ui/TextEditorOverlay.ts +0 -137
  87. package/src/ui/Toolbar.ts +0 -151
  88. package/src/ui/TopControls.ts +0 -137
  89. package/src/ui/icons.ts +0 -50
  90. package/tsconfig.json +0 -19
@@ -1,333 +0,0 @@
1
- /**
2
- * Serializes the DocEditor's contenteditable DOM back into clean markdown.
3
- *
4
- * It understands exactly the HTML that `renderMarkdown` produces (plus the
5
- * `<p>`/`<div>`/`<br>` that browsers create while editing), so a doc can make a
6
- * full round-trip: markdown -> HTML -> edit -> markdown without drifting.
7
- */
8
-
9
- const ASSET_PREFIX = "/__api/asset?path=";
10
-
11
- const BLOCK_TAGS = new Set([
12
- "P",
13
- "DIV",
14
- "UL",
15
- "OL",
16
- "H1",
17
- "H2",
18
- "H3",
19
- "H4",
20
- "H5",
21
- "H6",
22
- "BLOCKQUOTE",
23
- "PRE",
24
- "TABLE",
25
- "FIGURE",
26
- "HR",
27
- ]);
28
-
29
- export function htmlToMarkdown(root: HTMLElement): string {
30
- const blocks: string[] = [];
31
- for (const node of Array.from(root.childNodes)) {
32
- pushBlock(blocks, node);
33
- }
34
- return (
35
- blocks
36
- .map((b) => b.replace(/[ \t]+$/gm, ""))
37
- .join("\n\n")
38
- .replace(/\n{3,}/g, "\n\n")
39
- .trim() + "\n"
40
- );
41
- }
42
-
43
- function pushBlock(out: string[], node: Node): void {
44
- const md = serializeBlock(node);
45
- if (md !== null && md.trim() !== "") out.push(md);
46
- }
47
-
48
- function serializeBlock(node: Node): string | null {
49
- if (node.nodeType === Node.TEXT_NODE) {
50
- const text = (node.textContent ?? "").trim();
51
- return text ? text : null;
52
- }
53
- if (node.nodeType !== Node.ELEMENT_NODE) return null;
54
-
55
- const el = node as HTMLElement;
56
- const tag = el.tagName;
57
-
58
- switch (tag) {
59
- case "H1":
60
- case "H2":
61
- case "H3":
62
- case "H4":
63
- case "H5":
64
- case "H6":
65
- return "#".repeat(Number(tag[1])) + " " + inlineOf(el).trim();
66
-
67
- case "HR":
68
- return "---";
69
-
70
- case "P":
71
- if (el.classList.contains("embed-caret-spacer") && !hasMeaningfulParagraphContent(el)) return null;
72
- if (hasBlockChild(el)) return serializeMixedBlockContainer(el);
73
- return paragraphMarkdown(inlineOf(el).trim());
74
-
75
- case "BLOCKQUOTE": {
76
- const inner = blocksOf(el);
77
- return inner
78
- .split("\n")
79
- .map((l) => (l ? `> ${l}` : ">"))
80
- .join("\n");
81
- }
82
-
83
- case "UL":
84
- return serializeList(el, false, 0);
85
- case "OL":
86
- return serializeList(el, true, 0);
87
-
88
- case "PRE": {
89
- const code = el.querySelector("code");
90
- const lang = langFromClass(code ?? el);
91
- const text = (code ?? el).textContent ?? "";
92
- return "```" + lang + "\n" + text.replace(/\n$/, "") + "\n```";
93
- }
94
-
95
- case "TABLE":
96
- return serializeTable(el as HTMLTableElement);
97
-
98
- case "FIGURE":
99
- if (el.classList.contains("board-embed")) return boardBlock(el);
100
- return blocksOf(el) || null;
101
-
102
- case "DIV": {
103
- // Browsers wrap edits in <div>. If it holds blocks, recurse; else treat
104
- // the whole thing as a paragraph.
105
- if (hasBlockChild(el)) {
106
- return serializeMixedBlockContainer(el);
107
- }
108
- return paragraphMarkdown(inlineOf(el).trim());
109
- }
110
-
111
- case "BR":
112
- return null;
113
-
114
- default:
115
- return inlineOf(el).trim() || null;
116
- }
117
- }
118
-
119
- /** Serialize the block-level children of a container into joined markdown. */
120
- function blocksOf(el: HTMLElement): string {
121
- const acc: string[] = [];
122
- for (const child of Array.from(el.childNodes)) pushBlock(acc, child);
123
- return acc.join("\n\n");
124
- }
125
-
126
- function hasBlockChild(el: HTMLElement): boolean {
127
- for (const child of Array.from(el.children)) {
128
- if (BLOCK_TAGS.has(child.tagName)) return true;
129
- }
130
- return false;
131
- }
132
-
133
- function serializeMixedBlockContainer(el: HTMLElement): string | null {
134
- const acc: string[] = [];
135
- let inlineBuffer = "";
136
-
137
- const flushInline = () => {
138
- const paragraph = paragraphMarkdown(inlineBuffer.trim());
139
- if (paragraph) acc.push(paragraph);
140
- inlineBuffer = "";
141
- };
142
-
143
- for (const child of Array.from(el.childNodes)) {
144
- if (isBlockNode(child)) {
145
- flushInline();
146
- pushBlock(acc, child);
147
- continue;
148
- }
149
- inlineBuffer += inlineOfNode(child);
150
- }
151
-
152
- flushInline();
153
- return acc.join("\n\n") || null;
154
- }
155
-
156
- function isBlockNode(node: Node): boolean {
157
- return node.nodeType === Node.ELEMENT_NODE && BLOCK_TAGS.has((node as HTMLElement).tagName);
158
- }
159
-
160
- function hasMeaningfulParagraphContent(el: HTMLElement): boolean {
161
- for (const child of Array.from(el.childNodes)) {
162
- if (child.nodeType === Node.TEXT_NODE && (child.textContent ?? "").trim()) return true;
163
- if (child.nodeType !== Node.ELEMENT_NODE) continue;
164
- const node = child as HTMLElement;
165
- if (node.tagName === "BR") continue;
166
- if ((node.textContent ?? "").trim()) return true;
167
- if (["IMG", "FIGURE", "HR"].includes(node.tagName)) return true;
168
- }
169
- return false;
170
- }
171
-
172
- function serializeList(listEl: HTMLElement, ordered: boolean, depth: number): string {
173
- const lines: string[] = [];
174
- const indent = " ".repeat(depth);
175
- let index = 0;
176
- for (const li of Array.from(listEl.children)) {
177
- if (li.tagName !== "LI") continue;
178
- index++;
179
- const marker = ordered ? `${index}. ` : "- ";
180
-
181
- // Split direct content from nested lists.
182
- let inlineText = "";
183
- const nested: string[] = [];
184
- let task: string | null = null;
185
- for (const child of Array.from(li.childNodes)) {
186
- if (child.nodeType === Node.ELEMENT_NODE) {
187
- const ce = child as HTMLElement;
188
- if (ce.tagName === "UL") {
189
- nested.push(serializeList(ce, false, depth + 1));
190
- continue;
191
- }
192
- if (ce.tagName === "OL") {
193
- nested.push(serializeList(ce, true, depth + 1));
194
- continue;
195
- }
196
- if (ce.tagName === "INPUT") {
197
- task = (ce as HTMLInputElement).checked ? "[x] " : "[ ] ";
198
- continue;
199
- }
200
- }
201
- inlineText += inlineOfNode(child);
202
- }
203
-
204
- lines.push(indent + marker + (task ?? "") + inlineText.trim());
205
- for (const n of nested) if (n) lines.push(n);
206
- }
207
- return lines.join("\n");
208
- }
209
-
210
- function serializeTable(table: HTMLTableElement): string {
211
- const rows = Array.from(table.querySelectorAll("tr"));
212
- if (rows.length === 0) return "";
213
- const toCells = (tr: Element) =>
214
- Array.from(tr.querySelectorAll("th,td")).map((c) => inlineOf(c as HTMLElement).trim());
215
-
216
- const header = toCells(rows[0]);
217
- const out: string[] = [];
218
- out.push("| " + header.join(" | ") + " |");
219
- out.push("| " + header.map(() => "---").join(" | ") + " |");
220
- for (let i = 1; i < rows.length; i++) {
221
- out.push("| " + toCells(rows[i]).join(" | ") + " |");
222
- }
223
- return out.join("\n");
224
- }
225
-
226
- function boardBlock(figure: HTMLElement): string {
227
- const lines = ["```board", `src: ${figure.dataset.src ?? ""}`];
228
- if (figure.dataset.region) {
229
- lines.push(`region: ${figure.dataset.region.split(",").map((s) => s.trim()).join(", ")}`);
230
- }
231
- if (figure.dataset.height) lines.push(`height: ${figure.dataset.height}`);
232
- if (figure.dataset.title) lines.push(`title: ${figure.dataset.title}`);
233
- lines.push("```");
234
- return lines.join("\n");
235
- }
236
-
237
- // ---------- inline ----------
238
-
239
- function inlineOf(el: HTMLElement): string {
240
- let s = "";
241
- for (const child of Array.from(el.childNodes)) s += inlineOfNode(child);
242
- return s;
243
- }
244
-
245
- function inlineOfNode(node: Node): string {
246
- if (node.nodeType === Node.TEXT_NODE) return escapeMarkdownText(node.textContent ?? "");
247
- if (node.nodeType !== Node.ELEMENT_NODE) return "";
248
-
249
- const el = node as HTMLElement;
250
- switch (el.tagName) {
251
- case "STRONG":
252
- case "B":
253
- return wrapInline("**", inlineOf(el));
254
- case "EM":
255
- case "I":
256
- return wrapInline("*", inlineOf(el));
257
- case "CODE":
258
- return inlineCode(el.textContent ?? "");
259
- case "A": {
260
- const href = (el.getAttribute("data-md-href") ?? el.getAttribute("href") ?? "").trim();
261
- const label = inlineOf(el).trim() || escapeMarkdownText(href);
262
- if (!href) return label;
263
- return `[${escapeLinkLabel(label)}](${escapeUrl(href)})`;
264
- }
265
- case "IMG":
266
- return `![${escapeLinkLabel((el as HTMLImageElement).alt)}](${escapeUrl(
267
- imgMarkdownSrc(el as HTMLImageElement)
268
- )})`;
269
- case "BR":
270
- return "\n";
271
- default:
272
- return inlineOf(el);
273
- }
274
- }
275
-
276
- function imgMarkdownSrc(img: HTMLImageElement): string {
277
- const dataSrc = img.getAttribute("data-md-src");
278
- if (dataSrc) return dataSrc;
279
- const src = img.getAttribute("src") ?? "";
280
- if (src.startsWith(ASSET_PREFIX)) return decodeURIComponent(src.slice(ASSET_PREFIX.length));
281
- return src;
282
- }
283
-
284
- function langFromClass(el: Element): string {
285
- const cls = el.getAttribute("class") ?? "";
286
- const m = cls.match(/language-(\w+)/);
287
- return m ? m[1] : "";
288
- }
289
-
290
- function wrapInline(marker: string, value: string): string {
291
- const trimmed = value.trim();
292
- if (!trimmed) return "";
293
- return `${marker}${trimmed}${marker}`;
294
- }
295
-
296
- function inlineCode(value: string): string {
297
- if (!value.includes("`")) return "`" + value + "`";
298
- const longest = Math.max(...(value.match(/`+/g) ?? [""]).map((run) => run.length));
299
- const fence = "`".repeat(longest + 1);
300
- return `${fence} ${value} ${fence}`;
301
- }
302
-
303
- function escapeMarkdownText(value: string): string {
304
- return value
305
- .replace(/\\/g, "\\\\")
306
- .replace(/\[/g, "\\[")
307
- .replace(/\]/g, "\\]")
308
- .replace(/\*/g, "\\*")
309
- .replace(/_/g, "\\_")
310
- .replace(/`/g, "\\`");
311
- }
312
-
313
- function paragraphMarkdown(value: string): string | null {
314
- if (!value) return null;
315
- if (
316
- /^#{1,6}\s/.test(value) ||
317
- /^>\s?/.test(value) ||
318
- /^([-+*]|\d+\.)\s+/.test(value) ||
319
- /^`{3,}/.test(value) ||
320
- /^(-{3,}|\*{3,}|_{3,})$/.test(value)
321
- ) {
322
- return "\\" + value;
323
- }
324
- return value;
325
- }
326
-
327
- function escapeLinkLabel(value: string): string {
328
- return value.replace(/\\/g, "\\\\").replace(/\]/g, "\\]");
329
- }
330
-
331
- function escapeUrl(value: string): string {
332
- return value.trim().replace(/\s/g, "%20").replace(/\)/g, "%29").replace(/</g, "%3C").replace(/>/g, "%3E");
333
- }