viteboard 0.1.1 → 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.
- package/content/docs/index.md +1 -1
- package/content/docs/product-roadmap.board.json +4 -4
- package/content/machine/board-v1.schema.json +138 -0
- package/content/machine/example.board.json +217 -0
- package/content/machine/guide.md +270 -0
- package/content/machine/llms.txt +31 -0
- package/content/machine/viteboard.json +62 -0
- package/dist/assets/index-D2W76uu9.js +96 -0
- package/dist/assets/index-rMkUjGXP.css +1 -0
- package/dist/index.html +4 -2
- package/package.json +10 -12
- package/vite.config.ts +619 -38
- package/dist/assets/index-C30zRPsW.js +0 -60
- package/dist/assets/index-e5aqtN8W.css +0 -1
- package/index.html +0 -13
- package/src/app/AppController.ts +0 -962
- package/src/app/BoardState.ts +0 -130
- package/src/app/ClipboardService.ts +0 -159
- package/src/app/CommandManager.ts +0 -66
- package/src/app/ElementActions.ts +0 -152
- package/src/app/EmbedRegionSelector.ts +0 -103
- package/src/app/ExportPng.ts +0 -107
- package/src/app/ImageInsertService.ts +0 -141
- package/src/app/KeyboardShortcuts.ts +0 -124
- package/src/app/main.ts +0 -6
- package/src/board/BoardPreview.ts +0 -189
- package/src/board/BoardService.ts +0 -222
- package/src/canvas/CanvasRenderer.ts +0 -253
- package/src/canvas/CanvasSurface.ts +0 -70
- package/src/canvas/HitTester.ts +0 -110
- package/src/canvas/ImageCache.ts +0 -123
- package/src/canvas/PerformanceMonitor.ts +0 -31
- package/src/canvas/RenderScheduler.ts +0 -26
- package/src/canvas/Viewport.ts +0 -77
- package/src/content/ContentApi.ts +0 -258
- package/src/content/Markdown.ts +0 -431
- package/src/content/MarkdownView.ts +0 -70
- package/src/editor/DocEditor.ts +0 -799
- package/src/editor/htmlToMarkdown.ts +0 -333
- package/src/elements/renderElement.ts +0 -598
- package/src/elements/types.ts +0 -119
- package/src/shell/Shell.ts +0 -2948
- package/src/shell/Sidebar.ts +0 -1651
- package/src/storage/AssetStore.ts +0 -86
- package/src/storage/BoardSerializer.ts +0 -114
- package/src/storage/ImportExportService.ts +0 -153
- package/src/storage/IndexedDbStore.ts +0 -92
- package/src/storage/LocalBoardStore.ts +0 -104
- package/src/styles.css +0 -3332
- package/src/templates/handoffTemplate.ts +0 -34
- package/src/templates/helpers.ts +0 -132
- package/src/templates/index.ts +0 -79
- package/src/templates/journeyMapTemplate.ts +0 -52
- package/src/templates/opportunityTreeTemplate.ts +0 -45
- package/src/templates/personaTemplate.ts +0 -41
- package/src/templates/prioritizationMatrixTemplate.ts +0 -44
- package/src/templates/requirementsFlowTemplate.ts +0 -45
- package/src/templates/screenshotReviewTemplate.ts +0 -52
- package/src/templates/systemDiagramTemplate.ts +0 -41
- package/src/templates/workflowTemplate.ts +0 -35
- package/src/testing/StressTestGenerator.ts +0 -134
- package/src/testing/StressTestPanel.ts +0 -64
- package/src/tools/ArrowTool.ts +0 -97
- package/src/tools/ImageTool.ts +0 -39
- package/src/tools/PanTool.ts +0 -34
- package/src/tools/SelectTool.ts +0 -377
- package/src/tools/ShapeTool.ts +0 -106
- package/src/tools/StickyTool.ts +0 -69
- package/src/tools/TaskTool.ts +0 -52
- package/src/tools/TextTool.ts +0 -45
- package/src/tools/ToolContext.ts +0 -51
- package/src/tools/ToolController.ts +0 -178
- package/src/ui/Modal.ts +0 -58
- package/src/ui/PerformanceOverlay.ts +0 -75
- package/src/ui/StorageManager.ts +0 -94
- package/src/ui/TemplatePicker.ts +0 -67
- package/src/ui/TextEditorOverlay.ts +0 -262
- package/src/ui/Toolbar.ts +0 -189
- package/src/ui/TopControls.ts +0 -137
- package/src/ui/icons.ts +0 -56
- package/tsconfig.json +0 -19
|
@@ -1,262 +0,0 @@
|
|
|
1
|
-
import type { BoardState } from "../app/BoardState";
|
|
2
|
-
import type { CommandManager } from "../app/CommandManager";
|
|
3
|
-
import type { BoardElement } from "../elements/types";
|
|
4
|
-
import { worldToScreen } from "../canvas/Viewport";
|
|
5
|
-
import { getRenderedTextFontSize, invalidateTextCache } from "../elements/renderElement";
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Temporary HTML textarea positioned over a canvas element for editing.
|
|
9
|
-
* Canvas renders the final text; the textarea exists only during editing.
|
|
10
|
-
*/
|
|
11
|
-
export class TextEditorOverlay {
|
|
12
|
-
private state: BoardState;
|
|
13
|
-
private commands: CommandManager;
|
|
14
|
-
private textareas: HTMLTextAreaElement[] = [];
|
|
15
|
-
private editingId: string | null = null;
|
|
16
|
-
|
|
17
|
-
constructor(state: BoardState, commands: CommandManager) {
|
|
18
|
-
this.state = state;
|
|
19
|
-
this.commands = commands;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
get isEditing(): boolean {
|
|
23
|
-
return this.editingId !== null;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
open(el: BoardElement): void {
|
|
27
|
-
this.close();
|
|
28
|
-
this.editingId = el.id;
|
|
29
|
-
if (el.data.labelPosition === "sides") {
|
|
30
|
-
this.openSideEditors(el);
|
|
31
|
-
return;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const ta = document.createElement("textarea");
|
|
35
|
-
ta.className = "text-editor";
|
|
36
|
-
ta.value = (el.data.text as string) ?? "";
|
|
37
|
-
if (el.data.labelPosition === "sides") ta.placeholder = "left label | right label";
|
|
38
|
-
this.position(ta, el);
|
|
39
|
-
|
|
40
|
-
document.body.appendChild(ta);
|
|
41
|
-
this.textareas = [ta];
|
|
42
|
-
ta.focus();
|
|
43
|
-
ta.select();
|
|
44
|
-
|
|
45
|
-
// hide canvas text while editing by temporarily blanking it
|
|
46
|
-
const originalText = (el.data.text as string) ?? "";
|
|
47
|
-
el.data.text = "";
|
|
48
|
-
el.updatedAt = Date.now();
|
|
49
|
-
invalidateTextCache(el.id);
|
|
50
|
-
this.state.requestRender();
|
|
51
|
-
|
|
52
|
-
const commit = () => {
|
|
53
|
-
const newText = ta.value;
|
|
54
|
-
el.data.text = newText;
|
|
55
|
-
el.updatedAt = Date.now();
|
|
56
|
-
invalidateTextCache(el.id);
|
|
57
|
-
|
|
58
|
-
if (newText !== originalText) {
|
|
59
|
-
const id = el.id;
|
|
60
|
-
const state = this.state;
|
|
61
|
-
this.commands.push(
|
|
62
|
-
"edit-text",
|
|
63
|
-
() => {
|
|
64
|
-
const target = state.elementsById.get(id);
|
|
65
|
-
if (target) {
|
|
66
|
-
target.data.text = newText;
|
|
67
|
-
target.updatedAt = Date.now();
|
|
68
|
-
invalidateTextCache(id);
|
|
69
|
-
state.emitChange();
|
|
70
|
-
}
|
|
71
|
-
},
|
|
72
|
-
() => {
|
|
73
|
-
const target = state.elementsById.get(id);
|
|
74
|
-
if (target) {
|
|
75
|
-
target.data.text = originalText;
|
|
76
|
-
target.updatedAt = Date.now();
|
|
77
|
-
invalidateTextCache(id);
|
|
78
|
-
state.emitChange();
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
);
|
|
82
|
-
}
|
|
83
|
-
this.state.emitChange();
|
|
84
|
-
this.close();
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
ta.addEventListener("blur", commit);
|
|
88
|
-
ta.addEventListener("keydown", (e) => {
|
|
89
|
-
e.stopPropagation();
|
|
90
|
-
if (e.key === "Escape") {
|
|
91
|
-
el.data.text = originalText;
|
|
92
|
-
invalidateTextCache(el.id);
|
|
93
|
-
this.close();
|
|
94
|
-
this.state.requestRender();
|
|
95
|
-
} else if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
|
|
96
|
-
ta.blur();
|
|
97
|
-
}
|
|
98
|
-
});
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
private openSideEditors(el: BoardElement): void {
|
|
102
|
-
const originalText = String(el.data.text ?? "");
|
|
103
|
-
const [leftValue = "", rightValue = ""] = originalText.split("|", 2).map((part) => part.trim());
|
|
104
|
-
const left = this.makeSideEditor("Left label", leftValue);
|
|
105
|
-
const right = this.makeSideEditor("Right label", rightValue);
|
|
106
|
-
this.positionSideEditors(left, right, el);
|
|
107
|
-
document.body.append(left, right);
|
|
108
|
-
this.textareas = [left, right];
|
|
109
|
-
left.focus();
|
|
110
|
-
left.select();
|
|
111
|
-
|
|
112
|
-
el.data.text = "";
|
|
113
|
-
el.updatedAt = Date.now();
|
|
114
|
-
invalidateTextCache(el.id);
|
|
115
|
-
this.state.requestRender();
|
|
116
|
-
|
|
117
|
-
let settled = false;
|
|
118
|
-
const commit = () => {
|
|
119
|
-
if (settled) return;
|
|
120
|
-
settled = true;
|
|
121
|
-
const next = `${left.value.trim()} | ${right.value.trim()}`;
|
|
122
|
-
el.data.text = next;
|
|
123
|
-
el.updatedAt = Date.now();
|
|
124
|
-
invalidateTextCache(el.id);
|
|
125
|
-
if (next !== originalText) this.pushTextCommand(el.id, originalText, next);
|
|
126
|
-
this.state.emitChange();
|
|
127
|
-
this.close();
|
|
128
|
-
};
|
|
129
|
-
const cancel = () => {
|
|
130
|
-
if (settled) return;
|
|
131
|
-
settled = true;
|
|
132
|
-
el.data.text = originalText;
|
|
133
|
-
invalidateTextCache(el.id);
|
|
134
|
-
this.close();
|
|
135
|
-
this.state.requestRender();
|
|
136
|
-
};
|
|
137
|
-
for (const editor of [left, right]) {
|
|
138
|
-
editor.addEventListener("keydown", (e) => {
|
|
139
|
-
e.stopPropagation();
|
|
140
|
-
if (e.key === "Escape") cancel();
|
|
141
|
-
else if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) commit();
|
|
142
|
-
});
|
|
143
|
-
editor.addEventListener("blur", () => {
|
|
144
|
-
window.setTimeout(() => {
|
|
145
|
-
if (!this.textareas.includes(document.activeElement as HTMLTextAreaElement)) commit();
|
|
146
|
-
}, 0);
|
|
147
|
-
});
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
private makeSideEditor(placeholder: string, value: string): HTMLTextAreaElement {
|
|
152
|
-
const ta = document.createElement("textarea");
|
|
153
|
-
ta.className = "text-editor text-editor-side";
|
|
154
|
-
ta.placeholder = placeholder;
|
|
155
|
-
ta.value = value;
|
|
156
|
-
ta.style.fontFamily = '-apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif';
|
|
157
|
-
ta.style.fontWeight = "600";
|
|
158
|
-
ta.style.textAlign = "center";
|
|
159
|
-
ta.style.lineHeight = "1.35";
|
|
160
|
-
ta.style.color = this.state.theme === "dark" ? "#f2f2f2" : "#1f2933";
|
|
161
|
-
return ta;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
private positionSideEditors(left: HTMLTextAreaElement, right: HTMLTextAreaElement, el: BoardElement): void {
|
|
165
|
-
const vp = this.state.viewport;
|
|
166
|
-
const start = worldToScreen(el.x, el.y, vp);
|
|
167
|
-
const end = worldToScreen(el.x + el.width, el.y + el.height, vp);
|
|
168
|
-
const fontSize = 13 * vp.scale;
|
|
169
|
-
const width = Math.max(82, Math.min(150, Math.hypot(end.x - start.x, end.y - start.y) * 0.34));
|
|
170
|
-
const height = Math.max(24, fontSize * 1.35 + 4);
|
|
171
|
-
for (const [editor, position] of [[left, 0.24], [right, 0.76]] as const) {
|
|
172
|
-
editor.style.left = (start.x + (end.x - start.x) * position - width / 2) + "px";
|
|
173
|
-
editor.style.top = (start.y + (end.y - start.y) * position - height / 2) + "px";
|
|
174
|
-
editor.style.width = width + "px";
|
|
175
|
-
editor.style.height = height + "px";
|
|
176
|
-
editor.style.fontSize = fontSize + "px";
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
private pushTextCommand(id: string, originalText: string, newText: string): void {
|
|
181
|
-
const state = this.state;
|
|
182
|
-
this.commands.push(
|
|
183
|
-
"edit-text",
|
|
184
|
-
() => {
|
|
185
|
-
const target = state.elementsById.get(id);
|
|
186
|
-
if (target) { target.data.text = newText; target.updatedAt = Date.now(); invalidateTextCache(id); state.emitChange(); }
|
|
187
|
-
},
|
|
188
|
-
() => {
|
|
189
|
-
const target = state.elementsById.get(id);
|
|
190
|
-
if (target) { target.data.text = originalText; target.updatedAt = Date.now(); invalidateTextCache(id); state.emitChange(); }
|
|
191
|
-
}
|
|
192
|
-
);
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
/** Reposition while viewport changes (e.g. window resize). */
|
|
196
|
-
private position(ta: HTMLTextAreaElement, el: BoardElement): void {
|
|
197
|
-
const vp = this.state.viewport;
|
|
198
|
-
if (el.type === "arrow" || el.type === "line" || el.type === "connector") {
|
|
199
|
-
const start = worldToScreen(el.x, el.y, vp);
|
|
200
|
-
const end = worldToScreen(el.x + el.width, el.y + el.height, vp);
|
|
201
|
-
const fontSize = 13 * vp.scale;
|
|
202
|
-
const width = Math.max(100, Math.min(240, Math.hypot(end.x - start.x, end.y - start.y) - 16));
|
|
203
|
-
const height = Math.max(24, fontSize * 1.35 + 4);
|
|
204
|
-
ta.style.left = (start.x + end.x) / 2 - width / 2 + "px";
|
|
205
|
-
ta.style.top = (start.y + end.y) / 2 - height / 2 + "px";
|
|
206
|
-
ta.style.width = width + "px";
|
|
207
|
-
ta.style.height = height + "px";
|
|
208
|
-
ta.style.fontSize = fontSize + "px";
|
|
209
|
-
ta.style.lineHeight = "1.35";
|
|
210
|
-
ta.style.fontFamily = '-apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif';
|
|
211
|
-
ta.style.fontWeight = "600";
|
|
212
|
-
ta.style.color = this.state.theme === "dark" ? "#f2f2f2" : "#1f2933";
|
|
213
|
-
ta.style.textAlign = "center";
|
|
214
|
-
return;
|
|
215
|
-
}
|
|
216
|
-
const tl = worldToScreen(el.x, el.y, vp);
|
|
217
|
-
const isChevron = el.data.shapeKind === "chevron";
|
|
218
|
-
const fontSize = getRenderedTextFontSize(el) * vp.scale;
|
|
219
|
-
const chevronPad =
|
|
220
|
-
isChevron
|
|
221
|
-
? Math.min(Math.abs(el.width) * 0.18, Math.abs(el.height) * 0.55) + 8
|
|
222
|
-
: 10;
|
|
223
|
-
const xPad = (el.type === "sticky" ? 14 : el.type === "task" ? 50 : el.type === "text" ? 0 : chevronPad) * vp.scale;
|
|
224
|
-
const isCentered = el.type !== "text" && el.type !== "task";
|
|
225
|
-
const availableHeight = isChevron
|
|
226
|
-
? el.height * vp.scale
|
|
227
|
-
: el.height * vp.scale - (el.type === "sticky" ? 28 : isCentered ? chevronPad * 2 : 0) * vp.scale;
|
|
228
|
-
// A textarea fills from its top edge. Give centered diagram nodes a
|
|
229
|
-
// content-height editor and position that editor on the canvas baseline.
|
|
230
|
-
const lineCount = Math.max(1, String(el.data.text ?? "").split(/\r?\n/).length);
|
|
231
|
-
const contentHeight = Math.max(24, fontSize * (isChevron ? 1.18 : 1.35) * lineCount + 4);
|
|
232
|
-
const editorHeight = isCentered
|
|
233
|
-
? Math.min(Math.max(24, availableHeight), contentHeight)
|
|
234
|
-
: Math.max(24, availableHeight);
|
|
235
|
-
const yPad = isCentered
|
|
236
|
-
? Math.max(0, (el.height * vp.scale - editorHeight) / 2)
|
|
237
|
-
: el.type === "sticky" ? 14 * vp.scale : 0;
|
|
238
|
-
const rightPad = (el.type === "task" ? 16 : el.type === "sticky" ? 14 : el.type === "text" ? 0 : chevronPad) * vp.scale;
|
|
239
|
-
|
|
240
|
-
ta.style.left = tl.x + xPad + "px";
|
|
241
|
-
ta.style.top = tl.y + yPad + "px";
|
|
242
|
-
ta.style.width = Math.max(40, el.width * vp.scale - xPad - rightPad) + "px";
|
|
243
|
-
ta.style.height = editorHeight + "px";
|
|
244
|
-
ta.style.fontSize = fontSize + "px";
|
|
245
|
-
ta.style.lineHeight = isChevron ? "1.18" : "1.35";
|
|
246
|
-
ta.style.fontFamily =
|
|
247
|
-
el.style.fontFamily ?? '-apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif';
|
|
248
|
-
ta.style.fontWeight = isChevron ? "750" : el.style.fontWeight ?? "400";
|
|
249
|
-
ta.style.color =
|
|
250
|
-
el.style.textColor ?? (this.state.theme === "dark" ? "#f2f2f2" : "#1f2933");
|
|
251
|
-
ta.style.textAlign = el.type === "text" || el.type === "task" ? "left" : "center";
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
close(): void {
|
|
255
|
-
if (this.textareas.length) {
|
|
256
|
-
const editors = this.textareas;
|
|
257
|
-
this.textareas = [];
|
|
258
|
-
this.editingId = null;
|
|
259
|
-
for (const editor of editors) editor.remove();
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
}
|
package/src/ui/Toolbar.ts
DELETED
|
@@ -1,189 +0,0 @@
|
|
|
1
|
-
import { ICONS } from "./icons";
|
|
2
|
-
import type { ToolName } from "../tools/ToolContext";
|
|
3
|
-
|
|
4
|
-
export type ToolbarActions = {
|
|
5
|
-
setTool: (tool: ToolName) => void;
|
|
6
|
-
openTemplates: () => void;
|
|
7
|
-
toggleTheme: () => void;
|
|
8
|
-
openExportMenu: () => void;
|
|
9
|
-
toggleStressPanel: () => void;
|
|
10
|
-
/** Optional: start the drag-to-select embed flow (file-backed boards only). */
|
|
11
|
-
copyEmbed?: () => void;
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
const TOOL_BUTTONS: { tool: ToolName; icon: string; label: string; key: string }[] = [
|
|
15
|
-
{ tool: "select", icon: "select", label: "Select", key: "V" },
|
|
16
|
-
{ tool: "hand", icon: "hand", label: "Hand", key: "H" },
|
|
17
|
-
{ tool: "sticky", icon: "sticky", label: "Sticky note", key: "S" },
|
|
18
|
-
{ tool: "task", icon: "task", label: "Task", key: "" },
|
|
19
|
-
{ tool: "text", icon: "text", label: "Text", key: "T" },
|
|
20
|
-
{ tool: "image", icon: "image", label: "Image", key: "I" },
|
|
21
|
-
];
|
|
22
|
-
|
|
23
|
-
const SHAPE_BUTTONS: { tool: ToolName; icon: string; label: string; key: string }[] = [
|
|
24
|
-
{ tool: "rectangle", icon: "rectangle", label: "Rectangle", key: "R" },
|
|
25
|
-
{ tool: "ellipse", icon: "ellipse", label: "Ellipse", key: "O" },
|
|
26
|
-
{ tool: "diamond", icon: "diamond", label: "Diamond", key: "" },
|
|
27
|
-
{ tool: "triangle", icon: "triangle", label: "Triangle", key: "" },
|
|
28
|
-
{ tool: "pill", icon: "pill", label: "Pill", key: "" },
|
|
29
|
-
{ tool: "chevron", icon: "chevron", label: "Chevron", key: "" },
|
|
30
|
-
{ tool: "parallelogram", icon: "parallelogram", label: "Parallelogram", key: "" },
|
|
31
|
-
{ tool: "hexagon", icon: "hexagon", label: "Hexagon", key: "" },
|
|
32
|
-
{ tool: "cylinder", icon: "cylinder", label: "Cylinder", key: "" },
|
|
33
|
-
];
|
|
34
|
-
|
|
35
|
-
const ARROW_BUTTONS: { tool: ToolName; icon: string; label: string; key: string }[] = [
|
|
36
|
-
{ tool: "arrow", icon: "arrow", label: "Arrow (hold Shift to snap)", key: "A" },
|
|
37
|
-
{ tool: "dashedArrow", icon: "arrowDashed", label: "Dashed arrow", key: "" },
|
|
38
|
-
{ tool: "twoWayArrow", icon: "arrowBoth", label: "Two-way arrow", key: "" },
|
|
39
|
-
{ tool: "labeledArrow", icon: "arrowLabel", label: "Centered label arrow", key: "" },
|
|
40
|
-
{ tool: "sidedLabelArrow", icon: "arrowLabel", label: "Left and right labels", key: "" },
|
|
41
|
-
];
|
|
42
|
-
|
|
43
|
-
const SHAPE_TOOLS = new Set<ToolName>(SHAPE_BUTTONS.map((item) => item.tool));
|
|
44
|
-
|
|
45
|
-
export class Toolbar {
|
|
46
|
-
element: HTMLDivElement;
|
|
47
|
-
private toolButtons = new Map<ToolName, HTMLButtonElement>();
|
|
48
|
-
private shapeButtons = new Map<ToolName, HTMLButtonElement>();
|
|
49
|
-
private shapeBtn!: HTMLButtonElement;
|
|
50
|
-
private shapeGroup!: HTMLDivElement;
|
|
51
|
-
private arrowGroup!: HTMLDivElement;
|
|
52
|
-
private themeBtn!: HTMLButtonElement;
|
|
53
|
-
|
|
54
|
-
constructor(parent: HTMLElement, actions: ToolbarActions) {
|
|
55
|
-
this.element = document.createElement("div");
|
|
56
|
-
this.element.className = "toolbar";
|
|
57
|
-
|
|
58
|
-
for (const def of TOOL_BUTTONS) {
|
|
59
|
-
const btn = this.makeButton(def.icon, def.label, def.key);
|
|
60
|
-
btn.addEventListener("click", () => actions.setTool(def.tool));
|
|
61
|
-
this.toolButtons.set(def.tool, btn);
|
|
62
|
-
this.element.appendChild(btn);
|
|
63
|
-
if (def.tool === "sticky") this.element.appendChild(this.makeShapePicker(actions));
|
|
64
|
-
if (def.tool === "text") this.element.appendChild(this.makeArrowPicker(actions));
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
this.element.appendChild(this.divider());
|
|
68
|
-
|
|
69
|
-
const templatesBtn = this.makeButton("templates", "Templates", "");
|
|
70
|
-
templatesBtn.addEventListener("click", actions.openTemplates);
|
|
71
|
-
this.element.appendChild(templatesBtn);
|
|
72
|
-
|
|
73
|
-
this.element.appendChild(this.divider());
|
|
74
|
-
|
|
75
|
-
this.themeBtn = this.makeButton("moon", "Toggle theme", "");
|
|
76
|
-
this.themeBtn.addEventListener("click", actions.toggleTheme);
|
|
77
|
-
this.element.appendChild(this.themeBtn);
|
|
78
|
-
|
|
79
|
-
const exportBtn = this.makeButton("export", "Export / Import", "");
|
|
80
|
-
exportBtn.addEventListener("click", actions.openExportMenu);
|
|
81
|
-
this.element.appendChild(exportBtn);
|
|
82
|
-
|
|
83
|
-
const stressBtn = this.makeButton("flask", "Stress test", "");
|
|
84
|
-
stressBtn.addEventListener("click", actions.toggleStressPanel);
|
|
85
|
-
this.element.appendChild(stressBtn);
|
|
86
|
-
|
|
87
|
-
if (actions.copyEmbed) {
|
|
88
|
-
this.element.appendChild(this.divider());
|
|
89
|
-
const embedBtn = this.makeButton("embed", "Copy embed of a region", "");
|
|
90
|
-
embedBtn.addEventListener("click", actions.copyEmbed);
|
|
91
|
-
this.element.appendChild(embedBtn);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
parent.appendChild(this.element);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
private makeButton(icon: string, label: string, key: string): HTMLButtonElement {
|
|
98
|
-
const btn = document.createElement("button");
|
|
99
|
-
btn.className = "tool-btn";
|
|
100
|
-
btn.setAttribute("aria-label", label);
|
|
101
|
-
btn.innerHTML = ICONS[icon] ?? "";
|
|
102
|
-
const tip = document.createElement("span");
|
|
103
|
-
tip.className = "tooltip";
|
|
104
|
-
tip.textContent = label;
|
|
105
|
-
if (key) {
|
|
106
|
-
const kbd = document.createElement("kbd");
|
|
107
|
-
kbd.textContent = key;
|
|
108
|
-
tip.appendChild(kbd);
|
|
109
|
-
}
|
|
110
|
-
btn.appendChild(tip);
|
|
111
|
-
return btn;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
private makeShapePicker(actions: ToolbarActions): HTMLDivElement {
|
|
115
|
-
this.shapeGroup = document.createElement("div");
|
|
116
|
-
this.shapeGroup.className = "shape-tool-group";
|
|
117
|
-
this.shapeBtn = this.makeButton("shapes", "Shapes", "");
|
|
118
|
-
this.shapeBtn.classList.add("shape-tool-trigger");
|
|
119
|
-
this.shapeBtn.addEventListener("click", (e) => {
|
|
120
|
-
e.stopPropagation();
|
|
121
|
-
this.shapeGroup.classList.toggle("open");
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
const menu = document.createElement("div");
|
|
125
|
-
menu.className = "shape-tool-popover";
|
|
126
|
-
for (const def of SHAPE_BUTTONS) {
|
|
127
|
-
const btn = this.makeButton(def.icon, def.label, def.key);
|
|
128
|
-
btn.classList.add("shape-option-btn");
|
|
129
|
-
btn.addEventListener("click", (e) => {
|
|
130
|
-
e.stopPropagation();
|
|
131
|
-
actions.setTool(def.tool);
|
|
132
|
-
this.shapeGroup.classList.remove("open");
|
|
133
|
-
});
|
|
134
|
-
this.shapeButtons.set(def.tool, btn);
|
|
135
|
-
menu.appendChild(btn);
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
this.shapeGroup.append(this.shapeBtn, menu);
|
|
139
|
-
return this.shapeGroup;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
private makeArrowPicker(actions: ToolbarActions): HTMLDivElement {
|
|
143
|
-
this.arrowGroup = document.createElement("div");
|
|
144
|
-
this.arrowGroup.className = "shape-tool-group";
|
|
145
|
-
const trigger = this.makeButton("arrow", "Arrow types", "");
|
|
146
|
-
trigger.classList.add("shape-tool-trigger");
|
|
147
|
-
trigger.addEventListener("click", (e) => {
|
|
148
|
-
e.stopPropagation();
|
|
149
|
-
this.arrowGroup.classList.toggle("open");
|
|
150
|
-
});
|
|
151
|
-
const menu = document.createElement("div");
|
|
152
|
-
menu.className = "shape-tool-popover";
|
|
153
|
-
for (const def of ARROW_BUTTONS) {
|
|
154
|
-
const btn = this.makeButton(def.icon, def.label, def.key);
|
|
155
|
-
btn.classList.add("shape-option-btn");
|
|
156
|
-
btn.addEventListener("click", (e) => {
|
|
157
|
-
e.stopPropagation();
|
|
158
|
-
actions.setTool(def.tool);
|
|
159
|
-
this.arrowGroup.classList.remove("open");
|
|
160
|
-
});
|
|
161
|
-
this.toolButtons.set(def.tool, btn);
|
|
162
|
-
menu.appendChild(btn);
|
|
163
|
-
}
|
|
164
|
-
this.arrowGroup.append(trigger, menu);
|
|
165
|
-
return this.arrowGroup;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
private divider(): HTMLDivElement {
|
|
169
|
-
const d = document.createElement("div");
|
|
170
|
-
d.className = "toolbar-divider";
|
|
171
|
-
return d;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
setActiveTool(tool: ToolName): void {
|
|
175
|
-
for (const [name, btn] of this.toolButtons) {
|
|
176
|
-
btn.classList.toggle("active", name === tool);
|
|
177
|
-
}
|
|
178
|
-
for (const [name, btn] of this.shapeButtons) {
|
|
179
|
-
btn.classList.toggle("active", name === tool);
|
|
180
|
-
}
|
|
181
|
-
this.shapeBtn?.classList.toggle("active", SHAPE_TOOLS.has(tool));
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
setTheme(theme: "light" | "dark"): void {
|
|
185
|
-
const tip = this.themeBtn.querySelector(".tooltip");
|
|
186
|
-
this.themeBtn.innerHTML = ICONS[theme === "dark" ? "sun" : "moon"];
|
|
187
|
-
if (tip) this.themeBtn.appendChild(tip);
|
|
188
|
-
}
|
|
189
|
-
}
|
package/src/ui/TopControls.ts
DELETED
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
import { ICONS } from "./icons";
|
|
2
|
-
|
|
3
|
-
export type TopControlActions = {
|
|
4
|
-
resetView: () => void;
|
|
5
|
-
zoomIn: () => void;
|
|
6
|
-
zoomOut: () => void;
|
|
7
|
-
openStorageManager: () => void;
|
|
8
|
-
togglePerfOverlay: () => void;
|
|
9
|
-
onTitleChange: (title: string) => void;
|
|
10
|
-
deleteBoard?: () => void;
|
|
11
|
-
/** Optional: shown as a "← ViteBoard" button to the left of the title. */
|
|
12
|
-
onExit?: () => void;
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
export class TopControls {
|
|
16
|
-
private zoomLabel!: HTMLButtonElement;
|
|
17
|
-
private titleInput!: HTMLInputElement;
|
|
18
|
-
private titleMeasure!: HTMLSpanElement;
|
|
19
|
-
|
|
20
|
-
constructor(parent: HTMLElement, actions: TopControlActions, initialTitle: string) {
|
|
21
|
-
// top-left: optional back button + board title
|
|
22
|
-
const topLeft = document.createElement("div");
|
|
23
|
-
topLeft.className = "top-left";
|
|
24
|
-
|
|
25
|
-
if (actions.onExit) {
|
|
26
|
-
const back = document.createElement("button");
|
|
27
|
-
back.className = "control-btn back-btn";
|
|
28
|
-
back.innerHTML = `${ICONS.arrowLeft ?? ""}<span>ViteBoard</span>`;
|
|
29
|
-
back.title = "Save and back to ViteBoard";
|
|
30
|
-
back.setAttribute("aria-label", "Back to ViteBoard");
|
|
31
|
-
back.addEventListener("click", actions.onExit);
|
|
32
|
-
topLeft.appendChild(back);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
this.titleInput = document.createElement("input");
|
|
36
|
-
this.titleInput.className = "board-title";
|
|
37
|
-
this.titleInput.value = initialTitle;
|
|
38
|
-
this.titleInput.maxLength = 120;
|
|
39
|
-
this.titleInput.setAttribute("aria-label", "Board title");
|
|
40
|
-
this.titleInput.addEventListener("change", () =>
|
|
41
|
-
actions.onTitleChange(this.titleInput.value.trim() || "Untitled Board")
|
|
42
|
-
);
|
|
43
|
-
this.titleInput.addEventListener("input", () => this.resizeTitleInput());
|
|
44
|
-
this.titleInput.addEventListener("keydown", (e) => {
|
|
45
|
-
e.stopPropagation();
|
|
46
|
-
if (e.key === "Enter") this.titleInput.blur();
|
|
47
|
-
});
|
|
48
|
-
this.titleMeasure = document.createElement("span");
|
|
49
|
-
this.titleMeasure.className = "board-title-measure";
|
|
50
|
-
topLeft.appendChild(this.titleInput);
|
|
51
|
-
topLeft.appendChild(this.titleMeasure);
|
|
52
|
-
parent.appendChild(topLeft);
|
|
53
|
-
this.resizeTitleInput();
|
|
54
|
-
|
|
55
|
-
// top-right controls
|
|
56
|
-
const topRight = document.createElement("div");
|
|
57
|
-
topRight.className = "top-right";
|
|
58
|
-
|
|
59
|
-
const zoomGroup = document.createElement("div");
|
|
60
|
-
zoomGroup.className = "control-group";
|
|
61
|
-
|
|
62
|
-
const zoomOut = this.btn("−", "Zoom out (Ctrl/Cmd + wheel or Ctrl/Cmd + -)");
|
|
63
|
-
zoomOut.addEventListener("click", actions.zoomOut);
|
|
64
|
-
zoomGroup.appendChild(zoomOut);
|
|
65
|
-
|
|
66
|
-
this.zoomLabel = this.btn("100%", "Reset zoom to 100% (Ctrl/Cmd + 0)");
|
|
67
|
-
this.zoomLabel.classList.add("zoom-label");
|
|
68
|
-
this.zoomLabel.addEventListener("click", actions.resetView);
|
|
69
|
-
zoomGroup.appendChild(this.zoomLabel);
|
|
70
|
-
|
|
71
|
-
const zoomIn = this.btn("+", "Zoom in (Ctrl/Cmd + wheel or Ctrl/Cmd + +)");
|
|
72
|
-
zoomIn.addEventListener("click", actions.zoomIn);
|
|
73
|
-
zoomGroup.appendChild(zoomIn);
|
|
74
|
-
|
|
75
|
-
topRight.appendChild(zoomGroup);
|
|
76
|
-
|
|
77
|
-
const utilGroup = document.createElement("div");
|
|
78
|
-
utilGroup.className = "control-group";
|
|
79
|
-
|
|
80
|
-
const resetBtn = this.iconBtn("reset", "Reset view");
|
|
81
|
-
resetBtn.addEventListener("click", actions.resetView);
|
|
82
|
-
utilGroup.appendChild(resetBtn);
|
|
83
|
-
|
|
84
|
-
const storageBtn = this.iconBtn("storage", "Storage manager");
|
|
85
|
-
storageBtn.addEventListener("click", actions.openStorageManager);
|
|
86
|
-
utilGroup.appendChild(storageBtn);
|
|
87
|
-
|
|
88
|
-
const perfBtn = this.iconBtn("gauge", "Performance overlay");
|
|
89
|
-
perfBtn.addEventListener("click", actions.togglePerfOverlay);
|
|
90
|
-
utilGroup.appendChild(perfBtn);
|
|
91
|
-
|
|
92
|
-
if (actions.deleteBoard) {
|
|
93
|
-
const deleteBtn = this.iconBtn("trash", "Delete board");
|
|
94
|
-
deleteBtn.classList.add("danger-control");
|
|
95
|
-
deleteBtn.addEventListener("click", actions.deleteBoard);
|
|
96
|
-
utilGroup.appendChild(deleteBtn);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
topRight.appendChild(utilGroup);
|
|
100
|
-
parent.appendChild(topRight);
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
private btn(text: string, label: string): HTMLButtonElement {
|
|
104
|
-
const b = document.createElement("button");
|
|
105
|
-
b.className = "control-btn";
|
|
106
|
-
b.textContent = text;
|
|
107
|
-
b.title = label;
|
|
108
|
-
b.setAttribute("aria-label", label);
|
|
109
|
-
return b;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
private iconBtn(icon: string, label: string): HTMLButtonElement {
|
|
113
|
-
const b = document.createElement("button");
|
|
114
|
-
b.className = "control-btn";
|
|
115
|
-
b.innerHTML = ICONS[icon] ?? "";
|
|
116
|
-
b.title = label;
|
|
117
|
-
b.setAttribute("aria-label", label);
|
|
118
|
-
return b;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
setZoom(scale: number): void {
|
|
122
|
-
this.zoomLabel.textContent = Math.round(scale * 100) + "%";
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
setTitle(title: string): void {
|
|
126
|
-
this.titleInput.value = title;
|
|
127
|
-
this.resizeTitleInput();
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
private resizeTitleInput(): void {
|
|
131
|
-
if (!this.titleMeasure || !this.titleInput) return;
|
|
132
|
-
this.titleMeasure.textContent = this.titleInput.value || "Untitled Board";
|
|
133
|
-
const desired = Math.ceil(this.titleMeasure.getBoundingClientRect().width + 22);
|
|
134
|
-
const max = Math.max(180, window.innerWidth - 460);
|
|
135
|
-
this.titleInput.style.width = Math.min(Math.max(90, desired), max) + "px";
|
|
136
|
-
}
|
|
137
|
-
}
|
package/src/ui/icons.ts
DELETED
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Inline SVG icons (stroke-based, Lucide-style geometry, hand-written).
|
|
3
|
-
* Returned as innerHTML-safe static strings (no user content).
|
|
4
|
-
*/
|
|
5
|
-
export const ICONS: Record<string, string> = {
|
|
6
|
-
select: `<svg viewBox="0 0 24 24"><path d="M4 3l7 18 2.5-7.5L21 11z"/></svg>`,
|
|
7
|
-
hand: `<svg viewBox="0 0 24 24"><path d="M18 11V6.5a1.5 1.5 0 0 0-3 0V11m0-1V4.5a1.5 1.5 0 0 0-3 0V10m0 .5v-5a1.5 1.5 0 0 0-3 0V12m9-1v1.5a1.5 1.5 0 0 1 3 0V14c0 4-2.5 7-6.5 7S8 18.5 6 15l-1.8-3.2a1.4 1.4 0 0 1 2.3-1.5L8 12V7"/></svg>`,
|
|
8
|
-
sticky: `<svg viewBox="0 0 24 24"><path d="M5 4h14a1 1 0 0 1 1 1v9l-6 6H5a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1z"/><path d="M14 20v-5a1 1 0 0 1 1-1h5"/></svg>`,
|
|
9
|
-
task: `<svg viewBox="0 0 24 24"><rect x="4" y="5" width="16" height="14" rx="3"/><path d="M8 12l2.3 2.3L16 8.8"/></svg>`,
|
|
10
|
-
rectangle: `<svg viewBox="0 0 24 24"><rect x="4" y="5" width="16" height="14" rx="2"/></svg>`,
|
|
11
|
-
ellipse: `<svg viewBox="0 0 24 24"><ellipse cx="12" cy="12" rx="8.5" ry="7"/></svg>`,
|
|
12
|
-
diamond: `<svg viewBox="0 0 24 24"><path d="M12 3l8 9-8 9-8-9z"/></svg>`,
|
|
13
|
-
triangle: `<svg viewBox="0 0 24 24"><path d="M12 4l9 16H3z"/></svg>`,
|
|
14
|
-
pill: `<svg viewBox="0 0 24 24"><rect x="3.5" y="7" width="17" height="10" rx="5"/></svg>`,
|
|
15
|
-
chevron: `<svg viewBox="0 0 24 24"><path d="M4 6h12l5 6-5 6H4l5-6z"/></svg>`,
|
|
16
|
-
parallelogram: `<svg viewBox="0 0 24 24"><path d="M7 5h13l-3 14H4z"/></svg>`,
|
|
17
|
-
hexagon: `<svg viewBox="0 0 24 24"><path d="M8 4h8l5 8-5 8H8l-5-8z"/></svg>`,
|
|
18
|
-
cylinder: `<svg viewBox="0 0 24 24"><ellipse cx="12" cy="6" rx="7" ry="3"/><path d="M5 6v11c0 1.7 3.1 3 7 3s7-1.3 7-3V6"/></svg>`,
|
|
19
|
-
arrowDashed: `<svg viewBox="0 0 24 24"><path stroke-dasharray="4 3" d="M4 12h14m0 0l-5-5m5 5l-5 5"/></svg>`,
|
|
20
|
-
arrowBoth: `<svg viewBox="0 0 24 24"><path d="M4 12h16m-16 0l5-5m-5 5l5 5m11-5l-5-5m5 5l-5 5"/></svg>`,
|
|
21
|
-
arrowLabel: `<svg viewBox="0 0 24 24"><path d="M3 17L17 7m0 0h-6m6 0v6"/><rect x="7" y="9" width="10" height="6" rx="1"/></svg>`,
|
|
22
|
-
text: `<svg viewBox="0 0 24 24"><path d="M5 6V4h14v2M12 4v16m-3 0h6"/></svg>`,
|
|
23
|
-
arrow: `<svg viewBox="0 0 24 24"><path d="M5 19L19 5m0 0h-7m7 0v7"/></svg>`,
|
|
24
|
-
image: `<svg viewBox="0 0 24 24"><rect x="3" y="4" width="18" height="16" rx="2"/><circle cx="9" cy="10" r="1.6"/><path d="M21 16l-5-5-9 9"/></svg>`,
|
|
25
|
-
templates: `<svg viewBox="0 0 24 24"><rect x="3" y="3" width="8" height="8" rx="1.5"/><rect x="13" y="3" width="8" height="8" rx="1.5"/><rect x="3" y="13" width="8" height="8" rx="1.5"/><rect x="13" y="13" width="8" height="8" rx="1.5"/></svg>`,
|
|
26
|
-
shapes: `<svg viewBox="0 0 24 24"><rect x="4" y="5" width="7" height="7" rx="1.5"/><circle cx="16.5" cy="8.5" r="3.5"/><path d="M7.5 14l3.8 6H3.7z"/><path d="M15 14h4l2 3-2 3h-4l2-3z"/></svg>`,
|
|
27
|
-
sun: `<svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="4"/><path d="M12 2v2m0 16v2M4.9 4.9l1.4 1.4m11.4 11.4l1.4 1.4M2 12h2m16 0h2M4.9 19.1l1.4-1.4m11.4-11.4l1.4-1.4"/></svg>`,
|
|
28
|
-
moon: `<svg viewBox="0 0 24 24"><path d="M21 12.8A9 9 0 1 1 11.2 3 7 7 0 0 0 21 12.8z"/></svg>`,
|
|
29
|
-
export: `<svg viewBox="0 0 24 24"><path d="M12 15V3m0 0L7 8m5-5l5 5M4 17v2a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-2"/></svg>`,
|
|
30
|
-
import: `<svg viewBox="0 0 24 24"><path d="M12 3v12m0 0l-5-5m5 5l5-5M4 17v2a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-2"/></svg>`,
|
|
31
|
-
gauge: `<svg viewBox="0 0 24 24"><path d="M12 14l4-4M3.3 14a9 9 0 1 1 17.4 0"/></svg>`,
|
|
32
|
-
storage: `<svg viewBox="0 0 24 24"><ellipse cx="12" cy="5.5" rx="8" ry="2.5"/><path d="M4 5.5V12c0 1.4 3.6 2.5 8 2.5s8-1.1 8-2.5V5.5M4 12v6.5C4 19.9 7.6 21 12 21s8-1.1 8-2.5V12"/></svg>`,
|
|
33
|
-
flask: `<svg viewBox="0 0 24 24"><path d="M9 3h6M10 3v6L4.5 18a2 2 0 0 0 1.8 3h11.4a2 2 0 0 0 1.8-3L14 9V3"/><path d="M7 15h10"/></svg>`,
|
|
34
|
-
reset: `<svg viewBox="0 0 24 24"><path d="M3 12a9 9 0 1 0 2.6-6.3M3 4v5h5"/></svg>`,
|
|
35
|
-
camera: `<svg viewBox="0 0 24 24"><path d="M3 8a2 2 0 0 1 2-2h2l2-2h6l2 2h2a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><circle cx="12" cy="13" r="3.5"/></svg>`,
|
|
36
|
-
trash: `<svg viewBox="0 0 24 24"><path d="M4 7h16M10 11v6m4-6v6M6 7l1 13a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1l1-13M9 7V4a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v3"/></svg>`,
|
|
37
|
-
embed: `<svg viewBox="0 0 24 24"><rect x="3" y="5" width="18" height="14" rx="2" stroke-dasharray="3 3"/><path d="M9 10l-2 2 2 2m6-4l2 2-2 2"/></svg>`,
|
|
38
|
-
arrowLeft: `<svg viewBox="0 0 24 24"><path d="M19 12H5m0 0l6-6m-6 6l6 6"/></svg>`,
|
|
39
|
-
search: `<svg viewBox="0 0 24 24"><circle cx="11" cy="11" r="7"/><path d="M20 20l-4-4"/></svg>`,
|
|
40
|
-
panelLeft: `<svg viewBox="0 0 24 24"><rect x="4" y="4" width="16" height="16" rx="2"/><path d="M10 4v16"/></svg>`,
|
|
41
|
-
edit: `<svg viewBox="0 0 24 24"><path d="M12 20h9"/><path d="M16.5 3.5a2.1 2.1 0 0 1 3 3L7 19l-4 1 1-4z"/></svg>`,
|
|
42
|
-
copy: `<svg viewBox="0 0 24 24"><rect x="8" y="8" width="11" height="11" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v1"/></svg>`,
|
|
43
|
-
folder: `<svg viewBox="0 0 24 24"><path d="M4 7a2 2 0 0 1 2-2h4l2 2h6a2 2 0 0 1 2 2v7a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2z"/></svg>`,
|
|
44
|
-
folderOpen: `<svg viewBox="0 0 24 24"><path d="M4 7a2 2 0 0 1 2-2h4l2 2h6a2 2 0 0 1 2 2v1M4 8l1.5 9c.1.6.6 1 1.2 1h10.6c.6 0 1.1-.4 1.2-1L20 8z"/></svg>`,
|
|
45
|
-
file: `<svg viewBox="0 0 24 24"><path d="M13 3H6a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V10z"/><path d="M13 3v7h7"/></svg>`,
|
|
46
|
-
fileText: `<svg viewBox="0 0 24 24"><path d="M13 3H6a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V10z"/><path d="M13 3v7h7M10 13h4m-4 4h4"/></svg>`,
|
|
47
|
-
board: `<svg viewBox="0 0 24 24"><rect x="3" y="3" width="7" height="7" rx="1"/><rect x="12" y="3" width="9" height="7" rx="1"/><rect x="3" y="12" width="9" height="9" rx="1"/><rect x="14" y="12" width="7" height="9" rx="1"/></svg>`,
|
|
48
|
-
doc: `<svg viewBox="0 0 24 24"><path d="M14 3H6a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z"/><path d="M14 3v6h6M10 13h4m-4 4h4m-4-8h1"/></svg>`,
|
|
49
|
-
skill: `<svg viewBox="0 0 24 24"><path d="M12 2l3.1 6.3 6.9 1-5 4.9 1.2 6.8-6.2-3.3-6.2 3.3 1.2-6.8-5-4.9 6.9-1z"/></svg>`,
|
|
50
|
-
plus: `<svg viewBox="0 0 24 24"><path d="M12 5v14m-7-7h14"/></svg>`,
|
|
51
|
-
moreVertical: `<svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="1"/><circle cx="12" cy="5" r="1"/><circle cx="12" cy="19" r="1"/></svg>`,
|
|
52
|
-
chevronRight: `<svg viewBox="0 0 24 24"><path d="M9 18l6-6-6-6"/></svg>`,
|
|
53
|
-
chevronDown: `<svg viewBox="0 0 24 24"><path d="M6 9l6 6 6-6"/></svg>`,
|
|
54
|
-
wiki: `<svg viewBox="0 0 24 24"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><path d="M14 2v6h6M8 13h2m-2 4h6m-6-8h1"/></svg>`,
|
|
55
|
-
list: `<svg viewBox="0 0 24 24"><line x1="8" y1="6" x2="21" y2="6"/><line x1="8" y1="12" x2="21" y2="12"/><line x1="8" y1="18" x2="21" y2="18"/><circle cx="4" cy="6" r="1"/><circle cx="4" cy="12" r="1"/><circle cx="4" cy="18" r="1"/></svg>`,
|
|
56
|
-
};
|
package/tsconfig.json
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"target": "ES2022",
|
|
4
|
-
"useDefineForClassFields": true,
|
|
5
|
-
"module": "ESNext",
|
|
6
|
-
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
|
7
|
-
"skipLibCheck": true,
|
|
8
|
-
"moduleResolution": "bundler",
|
|
9
|
-
"allowImportingTsExtensions": true,
|
|
10
|
-
"resolveJsonModule": true,
|
|
11
|
-
"isolatedModules": true,
|
|
12
|
-
"noEmit": true,
|
|
13
|
-
"strict": true,
|
|
14
|
-
"noUnusedLocals": false,
|
|
15
|
-
"noUnusedParameters": false,
|
|
16
|
-
"noFallthroughCasesInSwitch": true
|
|
17
|
-
},
|
|
18
|
-
"include": ["src"]
|
|
19
|
-
}
|