@jxsuite/studio 0.28.2 → 0.28.4
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/dist/studio.js +86144 -83971
- package/dist/studio.js.map +229 -219
- package/package.json +5 -5
- package/src/browse/browse-modal.ts +9 -3
- package/src/browse/browse.ts +139 -82
- package/src/canvas/canvas-diff.ts +35 -18
- package/src/canvas/canvas-helpers.ts +55 -21
- package/src/canvas/canvas-live-render.ts +140 -89
- package/src/canvas/canvas-render.ts +127 -82
- package/src/canvas/canvas-utils.ts +68 -42
- package/src/canvas/nested-site-style.ts +15 -7
- package/src/editor/component-inline-edit.ts +90 -51
- package/src/editor/content-inline-edit.ts +75 -72
- package/src/editor/context-menu.ts +125 -71
- package/src/editor/convert-targets.ts +17 -13
- package/src/editor/convert-to-component.ts +51 -28
- package/src/editor/convert-to-repeater.ts +44 -19
- package/src/editor/inline-edit.ts +107 -52
- package/src/editor/inline-format.ts +128 -60
- package/src/editor/insertion-helper.ts +26 -14
- package/src/editor/shortcuts.ts +89 -43
- package/src/editor/slash-menu.ts +41 -26
- package/src/files/components.ts +9 -7
- package/src/files/file-ops.ts +53 -33
- package/src/files/files.ts +246 -143
- package/src/format/constraints.ts +25 -15
- package/src/format/format-host.ts +33 -10
- package/src/github/github-auth.ts +24 -14
- package/src/github/github-publish.ts +20 -14
- package/src/new-project/new-project-modal.ts +27 -18
- package/src/panels/activity-bar.ts +27 -26
- package/src/panels/ai-panel.ts +100 -44
- package/src/panels/block-action-bar.ts +84 -52
- package/src/panels/canvas-dnd.ts +47 -28
- package/src/panels/data-explorer.ts +24 -11
- package/src/panels/dnd.ts +148 -100
- package/src/panels/editors.ts +54 -37
- package/src/panels/elements-panel.ts +23 -13
- package/src/panels/events-panel.ts +48 -38
- package/src/panels/git-panel.ts +95 -60
- package/src/panels/head-panel.ts +138 -84
- package/src/panels/imports-panel.ts +66 -29
- package/src/panels/layers-panel.ts +80 -36
- package/src/panels/left-panel.ts +86 -53
- package/src/panels/overlays.ts +36 -19
- package/src/panels/panel-events.ts +48 -27
- package/src/panels/panel-scheduler.ts +41 -23
- package/src/panels/preview-render.ts +26 -26
- package/src/panels/properties-panel.ts +155 -118
- package/src/panels/pseudo-preview.ts +31 -15
- package/src/panels/quick-search.ts +28 -13
- package/src/panels/right-panel.ts +35 -24
- package/src/panels/shared.ts +46 -29
- package/src/panels/signals-panel.ts +303 -186
- package/src/panels/statusbar.ts +27 -12
- package/src/panels/style-inputs.ts +30 -24
- package/src/panels/style-panel.ts +130 -90
- package/src/panels/style-utils.ts +63 -23
- package/src/panels/stylebook-layers-panel.ts +27 -30
- package/src/panels/stylebook-panel.ts +140 -72
- package/src/panels/tab-strip.ts +21 -10
- package/src/panels/toolbar.ts +52 -40
- package/src/panels/welcome-screen.ts +1 -1
- package/src/platform.ts +2 -1
- package/src/platforms/devserver.ts +221 -105
- package/src/recent-projects.ts +15 -7
- package/src/resize-edges.ts +7 -5
- package/src/services/cem-export.ts +73 -57
- package/src/services/code-services.ts +52 -25
- package/src/services/monaco-setup.ts +5 -5
- package/src/settings/content-types-editor.ts +133 -62
- package/src/settings/css-vars-editor.ts +31 -14
- package/src/settings/defs-editor.ts +125 -63
- package/src/settings/general-settings.ts +11 -6
- package/src/settings/head-editor.ts +30 -15
- package/src/settings/schema-field-ui.ts +75 -35
- package/src/settings/settings-modal.ts +35 -17
- package/src/site-context.ts +80 -47
- package/src/state.ts +98 -65
- package/src/store.ts +42 -23
- package/src/studio.ts +165 -144
- package/src/tabs/tab.ts +51 -45
- package/src/tabs/transact.ts +230 -109
- package/src/types.ts +60 -51
- package/src/ui/button-group.ts +3 -2
- package/src/ui/color-selector.ts +33 -15
- package/src/ui/expression-editor.ts +139 -86
- package/src/ui/field-input.ts +35 -18
- package/src/ui/field-row.ts +1 -1
- package/src/ui/icons.ts +5 -6
- package/src/ui/layers.ts +33 -26
- package/src/ui/media-picker.ts +13 -7
- package/src/ui/panel-resize.ts +39 -20
- package/src/ui/spectrum.ts +4 -2
- package/src/ui/unit-selector.ts +20 -11
- package/src/ui/value-selector.ts +13 -7
- package/src/ui/widgets.ts +34 -18
- package/src/utils/canvas-media.ts +66 -33
- package/src/utils/edit-display.ts +43 -44
- package/src/utils/google-fonts.ts +10 -6
- package/src/utils/inherited-style.ts +22 -8
- package/src/utils/studio-utils.ts +88 -45
- package/src/view.ts +9 -5
- package/src/workspace/workspace.ts +49 -29
package/src/studio.ts
CHANGED
|
@@ -7,74 +7,74 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import "./services/monaco-setup.js";
|
|
10
|
+
import { errorMessage } from "@jxsuite/schema/parse";
|
|
10
11
|
|
|
11
12
|
import {
|
|
12
|
-
getNodeAtPath,
|
|
13
13
|
canvasWrap,
|
|
14
|
-
|
|
14
|
+
getNodeAtPath,
|
|
15
|
+
initShellRefs,
|
|
16
|
+
projectState,
|
|
15
17
|
registerRenderer,
|
|
16
18
|
render,
|
|
17
|
-
projectState,
|
|
18
|
-
setProjectState,
|
|
19
19
|
requireProjectState,
|
|
20
|
+
setProjectState,
|
|
21
|
+
toolbarEl,
|
|
20
22
|
updateUi,
|
|
21
|
-
initShellRefs,
|
|
22
23
|
} from "./store";
|
|
23
24
|
|
|
24
|
-
import { activeTab,
|
|
25
|
-
import {
|
|
25
|
+
import { activeTab, closeAllTabs, openTab } from "./workspace/workspace";
|
|
26
|
+
import { mutateUpdateDef, mutateUpdateProperty, transactDoc } from "./tabs/transact";
|
|
26
27
|
import { effect } from "./reactivity";
|
|
27
28
|
|
|
28
29
|
import { view } from "./view";
|
|
29
30
|
|
|
30
|
-
import {
|
|
31
|
+
import { isEditableBlock, isEditing } from "./editor/inline-edit";
|
|
31
32
|
import { initComponentInlineEdit } from "./editor/component-inline-edit";
|
|
32
33
|
import { enterInlineEdit } from "./editor/content-inline-edit";
|
|
33
|
-
import {
|
|
34
|
-
import {
|
|
34
|
+
import { applyTransform, initCanvasUtils, positionZoomIndicator } from "./canvas/canvas-utils";
|
|
35
|
+
import { findCanvasElement, getActivePanel, initCanvasHelpers } from "./canvas/canvas-helpers";
|
|
35
36
|
import { initCanvasRender, renderCanvas } from "./canvas/canvas-render";
|
|
36
37
|
import { initCanvasLiveRender } from "./canvas/canvas-live-render";
|
|
37
38
|
import {
|
|
39
|
+
mountStatusbar,
|
|
38
40
|
renderStatusbar,
|
|
39
|
-
statusMessage,
|
|
40
41
|
setStatusbarRenderer,
|
|
41
|
-
|
|
42
|
+
statusMessage,
|
|
42
43
|
} from "./panels/statusbar";
|
|
43
|
-
import { parseSourceForPath, saveFile,
|
|
44
|
-
import {
|
|
44
|
+
import { exportFile, parseSourceForPath, saveFile, serializeDocument } from "./files/file-ops";
|
|
45
|
+
import { documentExtensions, formatForPath, loadFormats } from "./format/format-host";
|
|
45
46
|
import {
|
|
46
47
|
loadProject as _loadProject,
|
|
47
48
|
openProject as _openProject,
|
|
48
49
|
renderFilesTemplate as _renderFilesTemplate,
|
|
50
|
+
loadDirectory,
|
|
49
51
|
openFileInTab,
|
|
50
52
|
openHomePage,
|
|
51
|
-
setupTreeKeyboard,
|
|
52
53
|
registerFileTreeDnD,
|
|
53
|
-
|
|
54
|
+
setupTreeKeyboard,
|
|
54
55
|
} from "./files/files";
|
|
55
56
|
import { renderImportsTemplate } from "./panels/imports-panel";
|
|
56
57
|
import { renderHeadTemplate } from "./panels/head-panel";
|
|
57
58
|
import { exportCemManifest as _exportCemManifest } from "./services/cem-export";
|
|
58
59
|
|
|
59
|
-
import {
|
|
60
|
+
import { getPlatform, hasPlatform, registerPlatform } from "./platform";
|
|
60
61
|
import { parseMediaEntries } from "./utils/canvas-media";
|
|
61
62
|
import { createDevServerPlatform } from "./platforms/devserver";
|
|
62
63
|
import { mountResizeEdges } from "./resize-edges";
|
|
63
64
|
import { codeService } from "./services/code-services";
|
|
64
|
-
import {
|
|
65
|
+
import { defBadgeLabel, defCategory, renderSignalsTemplate } from "./panels/signals-panel";
|
|
65
66
|
import { loadComponentRegistry } from "./files/components";
|
|
66
67
|
|
|
67
68
|
import { html, render as litRender } from "lit-html";
|
|
68
69
|
|
|
69
70
|
import webdata from "../data/webdata.json";
|
|
70
71
|
import { renderDataExplorerTemplate } from "./panels/data-explorer";
|
|
71
|
-
import { renderGitPanel } from "./panels/git-panel";
|
|
72
|
+
import { cloneRepository, renderGitPanel } from "./panels/git-panel";
|
|
72
73
|
|
|
73
74
|
// ─── Spectrum Web Components ──────────────────────────────────────────────────
|
|
74
75
|
// Explicit class imports + registration — bare side-effect imports are tree-shaken
|
|
75
|
-
//
|
|
76
|
+
// By Bun's bundler despite sideEffects declarations in Spectrum's package.json.
|
|
76
77
|
import { components as _swc } from "./ui/spectrum";
|
|
77
|
-
void _swc;
|
|
78
78
|
import "./ui/panel-resize.js";
|
|
79
79
|
import { initLayers } from "./ui/layers";
|
|
80
80
|
import { initShortcuts } from "./editor/shortcuts";
|
|
@@ -96,14 +96,15 @@ import { initQuickSearch } from "./panels/quick-search";
|
|
|
96
96
|
import { addRecentProject } from "./recent-projects";
|
|
97
97
|
import { initWelcome } from "./panels/welcome-screen";
|
|
98
98
|
import { openNewProjectModal } from "./new-project/new-project-modal";
|
|
99
|
-
import { cloneRepository } from "./panels/git-panel";
|
|
100
99
|
import type { DocumentStackEntry, GitDiffState } from "./types";
|
|
101
100
|
import type { JxPath } from "./state";
|
|
102
101
|
import type { JxMutableNode } from "@jxsuite/schema/types";
|
|
103
102
|
|
|
103
|
+
void _swc;
|
|
104
|
+
|
|
104
105
|
// ─── Globals ──────────────────────────────────────────────────────────────────
|
|
105
106
|
// These mutable variables are local to studio.js for now. As sections are extracted
|
|
106
|
-
//
|
|
107
|
+
// Into their own modules, they will migrate to ctx in store.js.
|
|
107
108
|
|
|
108
109
|
function getCanvasMode() {
|
|
109
110
|
return activeTab.value?.session.ui.canvasMode ?? "design";
|
|
@@ -115,7 +116,9 @@ function setCanvasMode(mode: string) {
|
|
|
115
116
|
gitDiffState = null;
|
|
116
117
|
}
|
|
117
118
|
const tab = activeTab.value;
|
|
118
|
-
if (tab)
|
|
119
|
+
if (tab) {
|
|
120
|
+
tab.session.ui.canvasMode = mode;
|
|
121
|
+
}
|
|
119
122
|
}
|
|
120
123
|
|
|
121
124
|
let gitDiffState: GitDiffState | null = null;
|
|
@@ -127,21 +130,27 @@ async function navigateToComponent(componentPath: string) {
|
|
|
127
130
|
try {
|
|
128
131
|
const platform = getPlatform();
|
|
129
132
|
const content = await platform.readFile(componentPath);
|
|
130
|
-
if (!content)
|
|
133
|
+
if (!content) {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
131
136
|
const parsed = JSON.parse(content);
|
|
132
137
|
const tab = activeTab.value;
|
|
133
|
-
if (!tab)
|
|
138
|
+
if (!tab) {
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
134
141
|
|
|
135
142
|
// Push current state onto the document stack
|
|
136
143
|
const frame = {
|
|
144
|
+
dirty: tab.doc.dirty,
|
|
137
145
|
document: tab.doc.document,
|
|
138
|
-
selection: tab.session.selection,
|
|
139
146
|
documentPath: tab.documentPath,
|
|
140
|
-
dirty: tab.doc.dirty,
|
|
141
147
|
mode: tab.doc.mode,
|
|
148
|
+
selection: tab.session.selection,
|
|
142
149
|
sourceFormat: tab.doc.sourceFormat,
|
|
143
150
|
};
|
|
144
|
-
if (!tab.session.documentStack)
|
|
151
|
+
if (!tab.session.documentStack) {
|
|
152
|
+
tab.session.documentStack = [];
|
|
153
|
+
}
|
|
145
154
|
tab.session.documentStack.push(frame);
|
|
146
155
|
|
|
147
156
|
// Load the component
|
|
@@ -157,28 +166,32 @@ async function navigateToComponent(componentPath: string) {
|
|
|
157
166
|
|
|
158
167
|
render();
|
|
159
168
|
statusMessage(`Editing component: ${parsed.tagName || componentPath}`);
|
|
160
|
-
} catch (
|
|
161
|
-
const err =
|
|
169
|
+
} catch (error) {
|
|
170
|
+
const err = error as Error;
|
|
162
171
|
statusMessage(`Error: ${err.message}`);
|
|
163
172
|
}
|
|
164
173
|
}
|
|
165
174
|
|
|
166
175
|
async function navigateBack() {
|
|
167
176
|
const tab = activeTab.value;
|
|
168
|
-
if (!tab?.session.documentStack || tab.session.documentStack.length === 0)
|
|
177
|
+
if (!tab?.session.documentStack || tab.session.documentStack.length === 0) {
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
169
180
|
if (tab.doc.dirty && tab.documentPath) {
|
|
170
181
|
try {
|
|
171
182
|
const platform = getPlatform();
|
|
172
183
|
await platform.writeFile(tab.documentPath, await serializeDocument(tab));
|
|
173
|
-
} catch (
|
|
174
|
-
const err =
|
|
184
|
+
} catch (error) {
|
|
185
|
+
const err = error as Error;
|
|
175
186
|
statusMessage(`Save error: ${err.message}`);
|
|
176
187
|
}
|
|
177
188
|
}
|
|
178
189
|
|
|
179
190
|
// Pop the stack
|
|
180
191
|
const frame = tab.session.documentStack.pop() as Record<string, unknown> | undefined;
|
|
181
|
-
if (!frame)
|
|
192
|
+
if (!frame) {
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
182
195
|
tab.doc.document = frame.document as JxMutableNode;
|
|
183
196
|
tab.doc.dirty = frame.dirty as boolean;
|
|
184
197
|
tab.doc.mode = frame.mode as string;
|
|
@@ -195,13 +208,15 @@ async function navigateBack() {
|
|
|
195
208
|
async function navigateToLevel(targetIndex: number) {
|
|
196
209
|
const tab = activeTab.value;
|
|
197
210
|
const stack = tab?.session.documentStack;
|
|
198
|
-
if (!stack || targetIndex < 0 || targetIndex >= stack.length)
|
|
211
|
+
if (!stack || targetIndex < 0 || targetIndex >= stack.length) {
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
199
214
|
if (tab.doc.dirty && tab.documentPath) {
|
|
200
215
|
try {
|
|
201
216
|
const platform = getPlatform();
|
|
202
217
|
await platform.writeFile(tab.documentPath, await serializeDocument(tab));
|
|
203
|
-
} catch (
|
|
204
|
-
const err =
|
|
218
|
+
} catch (error) {
|
|
219
|
+
const err = error as Error;
|
|
205
220
|
statusMessage(`Save error: ${err.message}`);
|
|
206
221
|
}
|
|
207
222
|
}
|
|
@@ -225,7 +240,9 @@ async function closeFunctionEditor() {
|
|
|
225
240
|
const editing =
|
|
226
241
|
/** @type {{ type: string; defName?: string; path?: JxPath; eventKey?: string } | null} */ tab
|
|
227
242
|
?.session.ui.editingFunction;
|
|
228
|
-
if (!editing || !tab)
|
|
243
|
+
if (!editing || !tab) {
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
229
246
|
if (view.functionEditor) {
|
|
230
247
|
const currentCode = view.functionEditor.getValue();
|
|
231
248
|
const minResult = await codeService("minify", { code: currentCode });
|
|
@@ -253,7 +270,7 @@ async function closeFunctionEditor() {
|
|
|
253
270
|
|
|
254
271
|
const datalistHost = document.createElement("div");
|
|
255
272
|
datalistHost.style.display = "contents";
|
|
256
|
-
document.body.
|
|
273
|
+
document.body.append(datalistHost);
|
|
257
274
|
litRender(
|
|
258
275
|
html`
|
|
259
276
|
<datalist id="tag-names">
|
|
@@ -265,15 +282,17 @@ litRender(
|
|
|
265
282
|
);
|
|
266
283
|
|
|
267
284
|
requestIdleCallback(() => {
|
|
268
|
-
const dl = document.
|
|
269
|
-
if (!dl)
|
|
285
|
+
const dl = document.querySelector("#css-props");
|
|
286
|
+
if (!dl) {
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
270
289
|
const frag = document.createDocumentFragment();
|
|
271
290
|
for (const [name] of webdata.cssProps) {
|
|
272
291
|
const opt = document.createElement("option");
|
|
273
292
|
opt.value = name;
|
|
274
|
-
frag.
|
|
293
|
+
frag.append(opt);
|
|
275
294
|
}
|
|
276
|
-
dl.
|
|
295
|
+
dl.append(frag);
|
|
277
296
|
});
|
|
278
297
|
|
|
279
298
|
initCssData(webdata);
|
|
@@ -295,17 +314,17 @@ initShellRefs();
|
|
|
295
314
|
|
|
296
315
|
// Mount extracted panel modules
|
|
297
316
|
toolbarPanel.mount(toolbarEl, {
|
|
317
|
+
closeFunctionEditor: () => closeFunctionEditor(),
|
|
318
|
+
getCanvasMode,
|
|
298
319
|
navigateBack: () => navigateBack(),
|
|
299
320
|
navigateToLevel: (i: number) => navigateToLevel(i),
|
|
300
|
-
closeFunctionEditor: () => closeFunctionEditor(),
|
|
301
321
|
openProject: () => openProject(),
|
|
302
322
|
openRecentProject: (root: string) => openRecentProject(root),
|
|
303
|
-
saveFile: () => saveFile(),
|
|
304
323
|
parseMediaEntries,
|
|
305
|
-
getCanvasMode,
|
|
306
|
-
setCanvasMode,
|
|
307
324
|
renderCanvas: () => renderCanvas(),
|
|
308
325
|
safeRenderRightPanel: () => safeRenderRightPanel(),
|
|
326
|
+
saveFile: () => saveFile(),
|
|
327
|
+
setCanvasMode,
|
|
309
328
|
});
|
|
310
329
|
|
|
311
330
|
initLayers();
|
|
@@ -332,41 +351,45 @@ initCanvasHelpers({
|
|
|
332
351
|
initCanvasUtils({
|
|
333
352
|
getCanvasMode,
|
|
334
353
|
getZoom: () => activeTab.value?.session.ui.zoom ?? 1,
|
|
354
|
+
renderStylebookOverlays,
|
|
335
355
|
setZoomDirect: (zoom) => {
|
|
336
|
-
if (activeTab.value)
|
|
356
|
+
if (activeTab.value) {
|
|
357
|
+
activeTab.value.session.ui.zoom = zoom;
|
|
358
|
+
}
|
|
337
359
|
},
|
|
338
|
-
renderStylebookOverlays,
|
|
339
360
|
});
|
|
340
361
|
initPanelEvents({
|
|
341
|
-
getCanvasMode,
|
|
342
362
|
enterInlineEdit,
|
|
363
|
+
getCanvasMode,
|
|
343
364
|
navigateToComponent,
|
|
344
365
|
});
|
|
345
366
|
initCanvasLiveRender({
|
|
346
367
|
getCanvasMode,
|
|
347
368
|
});
|
|
348
369
|
initCanvasRender({
|
|
349
|
-
getCanvasMode,
|
|
350
|
-
setCanvasMode,
|
|
351
|
-
openFileFromTree,
|
|
352
|
-
exportFile,
|
|
353
370
|
closeFunctionEditor: () => closeFunctionEditor(),
|
|
371
|
+
exportFile,
|
|
372
|
+
getCanvasMode,
|
|
354
373
|
get gitDiffState() {
|
|
355
374
|
return gitDiffState;
|
|
356
375
|
},
|
|
376
|
+
openFileFromTree,
|
|
377
|
+
setCanvasMode,
|
|
357
378
|
setGitDiffState: (state: GitDiffState | null) => {
|
|
358
379
|
gitDiffState = state;
|
|
359
380
|
},
|
|
360
381
|
});
|
|
361
382
|
|
|
362
383
|
initWelcome({
|
|
363
|
-
|
|
364
|
-
openRecentProject: (root: string) => openRecentProject(root),
|
|
384
|
+
cloneRepository: () => cloneRepository({ openRecentProject }),
|
|
365
385
|
openNewProject: async () => {
|
|
366
386
|
const result = await openNewProjectModal();
|
|
367
|
-
if (result)
|
|
387
|
+
if (result) {
|
|
388
|
+
openRecentProject(result.root);
|
|
389
|
+
}
|
|
368
390
|
},
|
|
369
|
-
|
|
391
|
+
openProject: () => openProject(),
|
|
392
|
+
openRecentProject: (root: string) => openRecentProject(root),
|
|
370
393
|
});
|
|
371
394
|
|
|
372
395
|
// Effect-driven canvas rendering: auto-triggers renderCanvas when reactive deps change.
|
|
@@ -391,8 +414,8 @@ effect(() => {
|
|
|
391
414
|
_canvasRafId = 0;
|
|
392
415
|
try {
|
|
393
416
|
renderCanvas();
|
|
394
|
-
} catch (
|
|
395
|
-
console.error("renderCanvas error:",
|
|
417
|
+
} catch (error) {
|
|
418
|
+
console.error("renderCanvas error:", error);
|
|
396
419
|
}
|
|
397
420
|
});
|
|
398
421
|
});
|
|
@@ -400,36 +423,36 @@ effect(() => {
|
|
|
400
423
|
});
|
|
401
424
|
|
|
402
425
|
rightPanelMod.mount({
|
|
403
|
-
navigateToComponent,
|
|
404
426
|
getCanvasMode,
|
|
427
|
+
navigateToComponent,
|
|
405
428
|
renderCanvas: () => renderCanvas(),
|
|
406
429
|
updateForcedPseudoPreview,
|
|
407
430
|
});
|
|
408
431
|
|
|
409
432
|
leftPanelMod.mount({
|
|
410
|
-
|
|
411
|
-
setCanvasMode,
|
|
412
|
-
renderImportsTemplate,
|
|
413
|
-
renderFilesTemplate,
|
|
414
|
-
renderSignalsTemplate,
|
|
415
|
-
renderDataExplorerTemplate,
|
|
416
|
-
renderHeadTemplate,
|
|
417
|
-
renderGitPanel,
|
|
418
|
-
renderCanvas: () => renderCanvas(),
|
|
419
|
-
defCategory,
|
|
433
|
+
cloneRepository: () => cloneRepository({ openRecentProject }),
|
|
420
434
|
defBadgeLabel,
|
|
421
|
-
|
|
422
|
-
webdata,
|
|
435
|
+
defCategory,
|
|
423
436
|
defaultDef,
|
|
424
|
-
|
|
425
|
-
|
|
437
|
+
getCanvasMode,
|
|
438
|
+
navigateToComponent,
|
|
426
439
|
registerComponentsDnD,
|
|
427
|
-
|
|
440
|
+
registerElementsDnD,
|
|
428
441
|
registerFileTreeDnD,
|
|
429
|
-
|
|
442
|
+
registerLayersDnD,
|
|
443
|
+
renderCanvas: () => renderCanvas(),
|
|
444
|
+
renderDataExplorerTemplate,
|
|
445
|
+
renderFilesTemplate,
|
|
446
|
+
renderGitPanel,
|
|
447
|
+
renderHeadTemplate,
|
|
448
|
+
renderImportsTemplate,
|
|
449
|
+
renderSignalsTemplate,
|
|
450
|
+
setCanvasMode,
|
|
430
451
|
setGitDiffState: (state: GitDiffState | null) => {
|
|
431
452
|
gitDiffState = state;
|
|
432
453
|
},
|
|
454
|
+
setupTreeKeyboard,
|
|
455
|
+
webdata,
|
|
433
456
|
});
|
|
434
457
|
|
|
435
458
|
// Register all renderers with the store so render()/renderOnly() work
|
|
@@ -444,8 +467,12 @@ mountActivityBar();
|
|
|
444
467
|
|
|
445
468
|
// Clicking on the canvas-wrap background (outside any canvas panel) deselects the current element
|
|
446
469
|
canvasWrap.addEventListener("click", (e: MouseEvent) => {
|
|
447
|
-
if (e.target !== canvasWrap && e.target !== view.panzoomWrap)
|
|
448
|
-
|
|
470
|
+
if (e.target !== canvasWrap && e.target !== view.panzoomWrap) {
|
|
471
|
+
return;
|
|
472
|
+
}
|
|
473
|
+
if (!activeTab.value?.session.selection) {
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
449
476
|
activeTab.value.session.selection = null;
|
|
450
477
|
});
|
|
451
478
|
|
|
@@ -471,6 +498,7 @@ if (_projectParam) {
|
|
|
471
498
|
} else {
|
|
472
499
|
render();
|
|
473
500
|
const platform = getPlatform();
|
|
501
|
+
// oxlint-disable-next-line unicorn/prefer-top-level-await -- deliberate fire-and-forget: project probing must not block the initial render
|
|
474
502
|
(async () => {
|
|
475
503
|
try {
|
|
476
504
|
const siteCtx = platform.resolveSiteContext
|
|
@@ -482,26 +510,28 @@ if (_projectParam) {
|
|
|
482
510
|
if (siteCtx.sitePath) {
|
|
483
511
|
platform.projectRoot = siteCtx.sitePath;
|
|
484
512
|
// Await activation so the server resolves project-relative static files
|
|
485
|
-
if (platform.activate)
|
|
513
|
+
if (platform.activate) {
|
|
514
|
+
await platform.activate();
|
|
515
|
+
}
|
|
486
516
|
}
|
|
487
517
|
|
|
488
518
|
setProjectState({
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
projectRoot: siteCtx.sitePath,
|
|
519
|
+
dirs: new Map(),
|
|
520
|
+
expanded: new Set(),
|
|
492
521
|
isSiteProject: true,
|
|
522
|
+
name: siteCtx.projectConfig?.name || "Project",
|
|
493
523
|
projectConfig: siteCtx.projectConfig || null,
|
|
494
524
|
projectDirs: [],
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
selectedPath: siteCtx.fileRelPath || null,
|
|
525
|
+
projectRoot: siteCtx.sitePath,
|
|
526
|
+
root: siteCtx.sitePath,
|
|
498
527
|
searchQuery: "",
|
|
528
|
+
selectedPath: siteCtx.fileRelPath || null,
|
|
499
529
|
});
|
|
500
530
|
|
|
501
531
|
await loadComponentRegistry();
|
|
502
532
|
|
|
503
533
|
// Load directory tree and populate projectDirs from conventional dirs found
|
|
504
|
-
const conventionalDirs = [
|
|
534
|
+
const conventionalDirs = new Set([
|
|
505
535
|
"pages",
|
|
506
536
|
"layouts",
|
|
507
537
|
"components",
|
|
@@ -509,12 +539,12 @@ if (_projectParam) {
|
|
|
509
539
|
"data",
|
|
510
540
|
"public",
|
|
511
541
|
"styles",
|
|
512
|
-
];
|
|
542
|
+
]);
|
|
513
543
|
const dirEntries = await platform.listDirectory(".");
|
|
514
544
|
requireProjectState().dirs.set(".", dirEntries);
|
|
515
545
|
const foundDirs = [];
|
|
516
546
|
for (const e of dirEntries) {
|
|
517
|
-
if (e.type === "directory" && conventionalDirs.
|
|
547
|
+
if (e.type === "directory" && conventionalDirs.has(e.name)) {
|
|
518
548
|
foundDirs.push(e.name);
|
|
519
549
|
requireProjectState().expanded.add(e.path || e.name);
|
|
520
550
|
const sub = await platform.listDirectory(e.path || e.name);
|
|
@@ -544,18 +574,22 @@ if (_projectParam) {
|
|
|
544
574
|
break;
|
|
545
575
|
} catch {}
|
|
546
576
|
}
|
|
547
|
-
if (!opened)
|
|
577
|
+
if (!opened) {
|
|
578
|
+
fileRelPath = "project.json";
|
|
579
|
+
}
|
|
548
580
|
}
|
|
549
581
|
|
|
550
582
|
const content = await platform.readFile(fileRelPath);
|
|
551
583
|
if (content) {
|
|
552
|
-
let
|
|
584
|
+
let frontmatter, parsedDoc, parsedMode;
|
|
553
585
|
await loadFormats();
|
|
554
586
|
const fileFormat = formatForPath(fileRelPath);
|
|
555
|
-
if (fileFormat) {
|
|
587
|
+
if (fileFormat || !fileRelPath.endsWith(".json")) {
|
|
588
|
+
// ParseSourceForPath throws a descriptive error when no format class claims the
|
|
589
|
+
// Extension (better than letting JSON.parse choke on non-JSON source).
|
|
556
590
|
const result = await parseSourceForPath(fileRelPath, content);
|
|
557
591
|
parsedDoc = result.document;
|
|
558
|
-
frontmatter = result
|
|
592
|
+
({ frontmatter } = result);
|
|
559
593
|
parsedMode = result.mode;
|
|
560
594
|
} else {
|
|
561
595
|
parsedDoc = JSON.parse(content);
|
|
@@ -570,7 +604,9 @@ if (_projectParam) {
|
|
|
570
604
|
sourceFormat: fileFormat?.name ?? null,
|
|
571
605
|
});
|
|
572
606
|
|
|
573
|
-
if (parsedMode === "content" && activeTab.value)
|
|
607
|
+
if (parsedMode === "content" && activeTab.value) {
|
|
608
|
+
activeTab.value.doc.mode = "content";
|
|
609
|
+
}
|
|
574
610
|
if (fileRelPath === "project.json" && activeTab.value) {
|
|
575
611
|
activeTab.value.session.ui.canvasMode = "stylebook";
|
|
576
612
|
}
|
|
@@ -578,8 +614,8 @@ if (_projectParam) {
|
|
|
578
614
|
render();
|
|
579
615
|
statusMessage(`Opened ${fileRelPath}`);
|
|
580
616
|
}
|
|
581
|
-
} catch (
|
|
582
|
-
statusMessage(`Error: ${(
|
|
617
|
+
} catch (error) {
|
|
618
|
+
statusMessage(`Error: ${errorMessage(error)}`);
|
|
583
619
|
}
|
|
584
620
|
})();
|
|
585
621
|
}
|
|
@@ -595,27 +631,6 @@ function renderLeftPanel() {
|
|
|
595
631
|
leftPanelMod.render();
|
|
596
632
|
}
|
|
597
633
|
|
|
598
|
-
// ─── DnD registration: delegated to panels/dnd.js ───────────────────────────
|
|
599
|
-
|
|
600
|
-
// ─── Stylebook ───────────────────────────────────────────────────────────────
|
|
601
|
-
// Extracted to panels/stylebook-panel.js
|
|
602
|
-
|
|
603
|
-
// ─── Inspector ────────────────────────────────────────────────────────────────
|
|
604
|
-
// Extracted to panels/properties-panel.js
|
|
605
|
-
|
|
606
|
-
// ─── Style Sidebar (metadata-driven) ───────────────────────────────────────────
|
|
607
|
-
|
|
608
|
-
// UNIT_RE — imported from ui/unit-selector.js
|
|
609
|
-
|
|
610
|
-
// inferInputType — imported from studio-utils.js
|
|
611
|
-
|
|
612
|
-
// ─── Style panel ────────────────────────────────────────────────────────────
|
|
613
|
-
// Extracted to panels/style-utils.js, panels/style-inputs.js, panels/style-panel.js
|
|
614
|
-
|
|
615
|
-
// ─── Source/Function editors: delegated to panels/editors.js ─────────────────
|
|
616
|
-
|
|
617
|
-
// ─── File tree (delegated to files.js) ───────────────────────────────────────
|
|
618
|
-
|
|
619
634
|
function loadProject() {
|
|
620
635
|
return _loadProject();
|
|
621
636
|
}
|
|
@@ -636,20 +651,20 @@ async function openRecentProject(root: string) {
|
|
|
636
651
|
|
|
637
652
|
setProjectState({
|
|
638
653
|
...projectState,
|
|
639
|
-
projectRoot: root,
|
|
640
|
-
isSiteProject: true,
|
|
641
|
-
projectConfig: config,
|
|
642
|
-
name: config.name || root.split("/").pop(),
|
|
643
654
|
dirs: new Map(),
|
|
644
655
|
expanded: new Set(),
|
|
645
|
-
|
|
656
|
+
isSiteProject: true,
|
|
657
|
+
name: config.name || root.split("/").pop(),
|
|
658
|
+
projectConfig: config,
|
|
659
|
+
projectRoot: root,
|
|
646
660
|
searchQuery: "",
|
|
661
|
+
selectedPath: null,
|
|
647
662
|
});
|
|
648
663
|
|
|
649
664
|
await loadDirectory(".");
|
|
650
665
|
await loadComponentRegistry();
|
|
651
666
|
|
|
652
|
-
const conventionalDirs = [
|
|
667
|
+
const conventionalDirs = new Set([
|
|
653
668
|
"pages",
|
|
654
669
|
"layouts",
|
|
655
670
|
"components",
|
|
@@ -657,10 +672,10 @@ async function openRecentProject(root: string) {
|
|
|
657
672
|
"data",
|
|
658
673
|
"public",
|
|
659
674
|
"styles",
|
|
660
|
-
];
|
|
675
|
+
]);
|
|
661
676
|
const entries = requireProjectState().dirs.get(".") || [];
|
|
662
677
|
for (const e of entries) {
|
|
663
|
-
if (e.type === "directory" && conventionalDirs.
|
|
678
|
+
if (e.type === "directory" && conventionalDirs.has(e.name)) {
|
|
664
679
|
requireProjectState().expanded.add(e.path || e.name);
|
|
665
680
|
await loadDirectory(e.path || e.name);
|
|
666
681
|
}
|
|
@@ -673,14 +688,14 @@ async function openRecentProject(root: string) {
|
|
|
673
688
|
statusMessage(`Opened project: ${requireProjectState().name}`);
|
|
674
689
|
|
|
675
690
|
await openHomePage();
|
|
676
|
-
} catch (
|
|
677
|
-
statusMessage(`Error: ${(
|
|
691
|
+
} catch (error) {
|
|
692
|
+
statusMessage(`Error: ${errorMessage(error)}`);
|
|
678
693
|
}
|
|
679
694
|
}
|
|
680
695
|
function renderFilesTemplate() {
|
|
681
696
|
return _renderFilesTemplate({
|
|
682
|
-
openProject,
|
|
683
697
|
openFileFromTree,
|
|
698
|
+
openProject,
|
|
684
699
|
renderLeftPanel,
|
|
685
700
|
});
|
|
686
701
|
}
|
|
@@ -690,19 +705,9 @@ function openFileFromTree(path: string) {
|
|
|
690
705
|
|
|
691
706
|
// ─── Keyboard shortcuts ───────────────────────────────────────────────────────
|
|
692
707
|
initShortcuts(() => ({
|
|
693
|
-
canvasMode: getCanvasMode(),
|
|
694
|
-
panX: view.panX,
|
|
695
|
-
panY: view.panY,
|
|
696
|
-
setPan: (x, y) => {
|
|
697
|
-
view.panX = x;
|
|
698
|
-
view.panY = y;
|
|
699
|
-
view.needsCenter = false;
|
|
700
|
-
},
|
|
701
708
|
applyTransform,
|
|
702
|
-
|
|
709
|
+
canvasMode: getCanvasMode(),
|
|
703
710
|
componentInlineEdit: view.componentInlineEdit,
|
|
704
|
-
saveFile,
|
|
705
|
-
openProject,
|
|
706
711
|
enterEditOnPath(path) {
|
|
707
712
|
requestAnimationFrame(() => {
|
|
708
713
|
const activePanel = getActivePanel();
|
|
@@ -714,16 +719,30 @@ initShortcuts(() => ({
|
|
|
714
719
|
}
|
|
715
720
|
});
|
|
716
721
|
},
|
|
722
|
+
openProject,
|
|
723
|
+
panX: view.panX,
|
|
724
|
+
panY: view.panY,
|
|
725
|
+
positionZoomIndicator,
|
|
726
|
+
saveFile,
|
|
727
|
+
setPan: (x, y) => {
|
|
728
|
+
view.panX = x;
|
|
729
|
+
view.panY = y;
|
|
730
|
+
view.needsCenter = false;
|
|
731
|
+
},
|
|
717
732
|
}));
|
|
718
733
|
|
|
719
734
|
// ─── Autosave (registered as update middleware) ──────────────────────────────
|
|
720
735
|
|
|
721
|
-
const AUTO_SAVE_DELAY
|
|
736
|
+
const AUTO_SAVE_DELAY = 2000;
|
|
722
737
|
|
|
723
738
|
function scheduleAutosave() {
|
|
724
739
|
const tab = activeTab.value;
|
|
725
|
-
if (!tab?.fileHandle || !tab.doc.dirty)
|
|
726
|
-
|
|
740
|
+
if (!tab?.fileHandle || !tab.doc.dirty) {
|
|
741
|
+
return;
|
|
742
|
+
}
|
|
743
|
+
if (view.autosaveTimer) {
|
|
744
|
+
clearTimeout(view.autosaveTimer);
|
|
745
|
+
}
|
|
727
746
|
view.autosaveTimer = setTimeout(async () => {
|
|
728
747
|
const t = activeTab.value;
|
|
729
748
|
if (t?.fileHandle && t.doc.dirty && "createWritable" in t.fileHandle) {
|
|
@@ -739,5 +758,7 @@ function scheduleAutosave() {
|
|
|
739
758
|
}
|
|
740
759
|
|
|
741
760
|
effect(() => {
|
|
742
|
-
if (activeTab.value?.doc.dirty)
|
|
761
|
+
if (activeTab.value?.doc.dirty) {
|
|
762
|
+
scheduleAutosave();
|
|
763
|
+
}
|
|
743
764
|
});
|