@jxsuite/studio 0.23.1 → 0.24.0
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 +2231 -715
- package/dist/studio.js.map +121 -104
- package/package.json +8 -7
- package/src/browse/{browse-modal.js → browse-modal.ts} +9 -10
- package/src/browse/{browse.js → browse.ts} +110 -85
- package/src/canvas/{canvas-diff.js → canvas-diff.ts} +42 -35
- package/src/canvas/{canvas-helpers.js → canvas-helpers.ts} +25 -19
- package/src/canvas/{canvas-live-render.js → canvas-live-render.ts} +53 -63
- package/src/canvas/{canvas-render.js → canvas-render.ts} +110 -121
- package/src/canvas/{canvas-utils.js → canvas-utils.ts} +48 -44
- package/src/canvas/{nested-site-style.js → nested-site-style.ts} +3 -3
- package/src/editor/{component-inline-edit.js → component-inline-edit.ts} +45 -33
- package/src/editor/{content-inline-edit.js → content-inline-edit.ts} +27 -45
- package/src/editor/{context-menu.js → context-menu.ts} +38 -36
- package/src/editor/{convert-targets.js → convert-targets.ts} +15 -12
- package/src/editor/{convert-to-component.js → convert-to-component.ts} +25 -26
- package/src/editor/{convert-to-repeater.js → convert-to-repeater.ts} +60 -40
- package/src/editor/{inline-edit.js → inline-edit.ts} +91 -55
- package/src/editor/{inline-format.js → inline-format.ts} +46 -51
- package/src/editor/{insertion-helper.js → insertion-helper.ts} +47 -63
- package/src/editor/{shortcuts.js → shortcuts.ts} +45 -31
- package/src/editor/{slash-menu.js → slash-menu.ts} +34 -35
- package/src/files/{components.js → components.ts} +12 -15
- package/src/files/{file-ops.js → file-ops.ts} +36 -29
- package/src/files/{files.js → files.ts} +102 -84
- package/src/github/{github-auth.js → github-auth.ts} +3 -3
- package/src/github/{github-publish.js → github-publish.ts} +20 -18
- package/src/markdown/{md-allowlist.js → md-allowlist.ts} +5 -2
- package/src/markdown/{md-convert.js → md-convert.ts} +146 -169
- package/src/new-project/{new-project-modal.js → new-project-modal.ts} +16 -18
- package/src/panels/{activity-bar.js → activity-bar.ts} +26 -34
- package/src/panels/{ai-panel.js → ai-panel.ts} +30 -30
- package/src/panels/{block-action-bar.js → block-action-bar.ts} +64 -73
- package/src/panels/{canvas-dnd.js → canvas-dnd.ts} +44 -49
- package/src/panels/{data-explorer.js → data-explorer.ts} +21 -15
- package/src/panels/dnd.ts +417 -0
- package/src/panels/{editors.js → editors.ts} +43 -50
- package/src/panels/{elements-panel.js → elements-panel.ts} +30 -27
- package/src/panels/{events-panel.js → events-panel.ts} +33 -25
- package/src/panels/{git-panel.js → git-panel.ts} +97 -83
- package/src/panels/{head-panel.js → head-panel.ts} +117 -139
- package/src/panels/{imports-panel.js → imports-panel.ts} +69 -89
- package/src/panels/{layers-panel.js → layers-panel.ts} +61 -57
- package/src/panels/{left-panel.js → left-panel.ts} +56 -63
- package/src/panels/{overlays.js → overlays.ts} +28 -36
- package/src/panels/{panel-events.js → panel-events.ts} +53 -51
- package/src/panels/{preview-render.js → preview-render.ts} +20 -15
- package/src/panels/{properties-panel.js → properties-panel.ts} +212 -246
- package/src/panels/{pseudo-preview.js → pseudo-preview.ts} +9 -8
- package/src/panels/{quick-search.js → quick-search.ts} +16 -17
- package/src/panels/{right-panel.js → right-panel.ts} +41 -59
- package/src/panels/{shared.js → shared.ts} +7 -5
- package/src/panels/{signals-panel.js → signals-panel.ts} +322 -362
- package/src/panels/{statusbar.js → statusbar.ts} +14 -14
- package/src/panels/{style-inputs.js → style-inputs.ts} +66 -48
- package/src/panels/{style-panel.js → style-panel.ts} +235 -290
- package/src/panels/{style-utils.js → style-utils.ts} +32 -31
- package/src/panels/{stylebook-layers-panel.js → stylebook-layers-panel.ts} +22 -14
- package/src/panels/{stylebook-panel.js → stylebook-panel.ts} +127 -140
- package/src/panels/{tab-strip.js → tab-strip.ts} +12 -14
- package/src/panels/{toolbar.js → toolbar.ts} +68 -70
- package/src/panels/{welcome-screen.js → welcome-screen.ts} +14 -16
- package/src/{platform.js → platform.ts} +5 -3
- package/src/platforms/{devserver.js → devserver.ts} +53 -44
- package/src/{recent-projects.js → recent-projects.ts} +17 -12
- package/src/{resize-edges.js → resize-edges.ts} +18 -13
- package/src/services/{cem-export.js → cem-export.ts} +14 -7
- package/src/services/{code-services.js → code-services.ts} +29 -20
- package/src/services/{monaco-setup.js → monaco-setup.ts} +2 -3
- package/src/settings/{content-types-editor.js → content-types-editor.ts} +88 -67
- package/src/settings/{css-vars-editor.js → css-vars-editor.ts} +78 -51
- package/src/settings/{defs-editor.js → defs-editor.ts} +70 -56
- package/src/settings/{general-settings.js → general-settings.ts} +29 -28
- package/src/settings/{head-editor.js → head-editor.ts} +37 -35
- package/src/settings/{schema-field-ui.js → schema-field-ui.ts} +86 -76
- package/src/settings/{settings-modal.js → settings-modal.ts} +14 -16
- package/src/{site-context.js → site-context.ts} +35 -32
- package/src/{state.js → state.ts} +95 -78
- package/src/{store.js → store.ts} +60 -63
- package/src/{studio.js → studio.ts} +117 -119
- package/src/tabs/tab.ts +196 -0
- package/src/tabs/{transact.js → transact.ts} +107 -60
- package/src/types.ts +185 -0
- package/src/ui/{button-group.js → button-group.ts} +21 -21
- package/src/ui/{color-selector.js → color-selector.ts} +44 -37
- package/src/ui/{expression-editor.js → expression-editor.ts} +65 -40
- package/src/ui/{field-row.js → field-row.ts} +19 -2
- package/src/ui/{icons.js → icons.ts} +2 -2
- package/src/ui/{layers.js → layers.ts} +33 -24
- package/src/ui/{media-picker.js → media-picker.ts} +13 -9
- package/src/ui/{panel-resize.js → panel-resize.ts} +9 -4
- package/src/ui/{spectrum.js → spectrum.ts} +4 -4
- package/src/ui/{unit-selector.js → unit-selector.ts} +16 -19
- package/src/ui/{value-selector.js → value-selector.ts} +28 -28
- package/src/ui/{widgets.js → widgets.ts} +62 -15
- package/src/utils/{canvas-media.js → canvas-media.ts} +29 -14
- package/src/utils/{edit-display.js → edit-display.ts} +23 -20
- package/src/utils/{google-fonts.js → google-fonts.ts} +14 -13
- package/src/utils/{inherited-style.js → inherited-style.ts} +15 -10
- package/src/utils/{studio-utils.js → studio-utils.ts} +38 -26
- package/src/{view.js → view.ts} +53 -55
- package/src/workspace/{workspace.js → workspace.ts} +44 -33
- package/src/panels/dnd.js +0 -426
- package/src/tabs/tab.js +0 -192
- /package/src/{reactivity.js → reactivity.ts} +0 -0
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/// <reference lib="dom" />
|
|
1
2
|
/**
|
|
2
3
|
* Studio.js — Jx Studio main application
|
|
3
4
|
*
|
|
@@ -18,28 +19,28 @@ import {
|
|
|
18
19
|
requireProjectState,
|
|
19
20
|
updateUi,
|
|
20
21
|
initShellRefs,
|
|
21
|
-
} from "./store
|
|
22
|
+
} from "./store";
|
|
22
23
|
|
|
23
|
-
import { activeTab, openTab, closeAllTabs } from "./workspace/workspace
|
|
24
|
-
import { transactDoc, mutateUpdateDef, mutateUpdateProperty } from "./tabs/transact
|
|
25
|
-
import { effect } from "./reactivity
|
|
24
|
+
import { activeTab, openTab, closeAllTabs } from "./workspace/workspace";
|
|
25
|
+
import { transactDoc, mutateUpdateDef, mutateUpdateProperty } from "./tabs/transact";
|
|
26
|
+
import { effect } from "./reactivity";
|
|
26
27
|
|
|
27
|
-
import { view } from "./view
|
|
28
|
+
import { view } from "./view";
|
|
28
29
|
|
|
29
|
-
import { isEditing, isEditableBlock } from "./editor/inline-edit
|
|
30
|
-
import { initComponentInlineEdit } from "./editor/component-inline-edit
|
|
31
|
-
import { enterInlineEdit } from "./editor/content-inline-edit
|
|
32
|
-
import { initCanvasUtils, applyTransform, positionZoomIndicator } from "./canvas/canvas-utils
|
|
33
|
-
import { initCanvasHelpers, getActivePanel, findCanvasElement } from "./canvas/canvas-helpers
|
|
34
|
-
import { initCanvasRender, renderCanvas } from "./canvas/canvas-render
|
|
35
|
-
import { initCanvasLiveRender } from "./canvas/canvas-live-render
|
|
30
|
+
import { isEditing, isEditableBlock } from "./editor/inline-edit";
|
|
31
|
+
import { initComponentInlineEdit } from "./editor/component-inline-edit";
|
|
32
|
+
import { enterInlineEdit } from "./editor/content-inline-edit";
|
|
33
|
+
import { initCanvasUtils, applyTransform, positionZoomIndicator } from "./canvas/canvas-utils";
|
|
34
|
+
import { initCanvasHelpers, getActivePanel, findCanvasElement } from "./canvas/canvas-helpers";
|
|
35
|
+
import { initCanvasRender, renderCanvas } from "./canvas/canvas-render";
|
|
36
|
+
import { initCanvasLiveRender } from "./canvas/canvas-live-render";
|
|
36
37
|
import {
|
|
37
38
|
renderStatusbar,
|
|
38
39
|
statusMessage,
|
|
39
40
|
setStatusbarRenderer,
|
|
40
41
|
mountStatusbar,
|
|
41
|
-
} from "./panels/statusbar
|
|
42
|
-
import { loadMarkdown, saveFile, exportFile, serializeDocument } from "./files/file-ops
|
|
42
|
+
} from "./panels/statusbar";
|
|
43
|
+
import { loadMarkdown, saveFile, exportFile, serializeDocument } from "./files/file-ops";
|
|
43
44
|
import {
|
|
44
45
|
loadProject as _loadProject,
|
|
45
46
|
openProject as _openProject,
|
|
@@ -49,51 +50,55 @@ import {
|
|
|
49
50
|
setupTreeKeyboard,
|
|
50
51
|
registerFileTreeDnD,
|
|
51
52
|
loadDirectory,
|
|
52
|
-
} from "./files/files
|
|
53
|
-
import { renderImportsTemplate } from "./panels/imports-panel
|
|
54
|
-
import { renderHeadTemplate } from "./panels/head-panel
|
|
55
|
-
import { exportCemManifest as _exportCemManifest } from "./services/cem-export
|
|
56
|
-
|
|
57
|
-
import { registerPlatform, getPlatform, hasPlatform } from "./platform
|
|
58
|
-
import { parseMediaEntries } from "./utils/canvas-media
|
|
59
|
-
import { createDevServerPlatform } from "./platforms/devserver
|
|
60
|
-
import { mountResizeEdges } from "./resize-edges
|
|
61
|
-
import { codeService } from "./services/code-services
|
|
62
|
-
import { defCategory, defBadgeLabel, renderSignalsTemplate } from "./panels/signals-panel
|
|
63
|
-
import { loadComponentRegistry } from "./files/components
|
|
53
|
+
} from "./files/files";
|
|
54
|
+
import { renderImportsTemplate } from "./panels/imports-panel";
|
|
55
|
+
import { renderHeadTemplate } from "./panels/head-panel";
|
|
56
|
+
import { exportCemManifest as _exportCemManifest } from "./services/cem-export";
|
|
57
|
+
|
|
58
|
+
import { registerPlatform, getPlatform, hasPlatform } from "./platform";
|
|
59
|
+
import { parseMediaEntries } from "./utils/canvas-media";
|
|
60
|
+
import { createDevServerPlatform } from "./platforms/devserver";
|
|
61
|
+
import { mountResizeEdges } from "./resize-edges";
|
|
62
|
+
import { codeService } from "./services/code-services";
|
|
63
|
+
import { defCategory, defBadgeLabel, renderSignalsTemplate } from "./panels/signals-panel";
|
|
64
|
+
import { loadComponentRegistry } from "./files/components";
|
|
64
65
|
|
|
65
66
|
import { html, render as litRender } from "lit-html";
|
|
66
67
|
|
|
67
68
|
import webdata from "../data/webdata.json";
|
|
68
|
-
import { renderDataExplorerTemplate } from "./panels/data-explorer
|
|
69
|
-
import { renderGitPanel } from "./panels/git-panel
|
|
69
|
+
import { renderDataExplorerTemplate } from "./panels/data-explorer";
|
|
70
|
+
import { renderGitPanel } from "./panels/git-panel";
|
|
70
71
|
|
|
71
72
|
// ─── Spectrum Web Components ──────────────────────────────────────────────────
|
|
72
73
|
// Explicit class imports + registration — bare side-effect imports are tree-shaken
|
|
73
74
|
// by Bun's bundler despite sideEffects declarations in Spectrum's package.json.
|
|
74
|
-
import { components as _swc } from "./ui/spectrum
|
|
75
|
+
import { components as _swc } from "./ui/spectrum";
|
|
76
|
+
void _swc;
|
|
75
77
|
import "./ui/panel-resize.js";
|
|
76
|
-
import { initLayers } from "./ui/layers
|
|
77
|
-
import { initShortcuts } from "./editor/shortcuts
|
|
78
|
-
import { renderActivityBar, mount as mountActivityBar } from "./panels/activity-bar
|
|
79
|
-
import * as toolbarPanel from "./panels/toolbar
|
|
80
|
-
import * as overlaysPanel from "./panels/overlays
|
|
81
|
-
import * as rightPanelMod from "./panels/right-panel
|
|
82
|
-
import * as leftPanelMod from "./panels/left-panel
|
|
83
|
-
import * as tabStrip from "./panels/tab-strip
|
|
84
|
-
import { renderStylebookOverlays } from "./panels/stylebook-panel
|
|
85
|
-
import { registerLayersDnD, registerComponentsDnD, registerElementsDnD } from "./panels/dnd
|
|
86
|
-
import { defaultDef } from "./panels/shared
|
|
87
|
-
import { registerFunctionCompletions } from "./panels/editors
|
|
88
|
-
import { renderBlockActionBar, initBlockActionBar } from "./panels/block-action-bar
|
|
89
|
-
import { initCssData } from "./panels/style-utils
|
|
90
|
-
import { updateForcedPseudoPreview } from "./panels/pseudo-preview
|
|
91
|
-
import { initPanelEvents } from "./panels/panel-events
|
|
92
|
-
import { initQuickSearch } from "./panels/quick-search
|
|
93
|
-
import { addRecentProject } from "./recent-projects
|
|
94
|
-
import { initWelcome } from "./panels/welcome-screen
|
|
95
|
-
import { openNewProjectModal } from "./new-project/new-project-modal
|
|
96
|
-
import { cloneRepository } from "./panels/git-panel
|
|
78
|
+
import { initLayers } from "./ui/layers";
|
|
79
|
+
import { initShortcuts } from "./editor/shortcuts";
|
|
80
|
+
import { renderActivityBar, mount as mountActivityBar } from "./panels/activity-bar";
|
|
81
|
+
import * as toolbarPanel from "./panels/toolbar";
|
|
82
|
+
import * as overlaysPanel from "./panels/overlays";
|
|
83
|
+
import * as rightPanelMod from "./panels/right-panel";
|
|
84
|
+
import * as leftPanelMod from "./panels/left-panel";
|
|
85
|
+
import * as tabStrip from "./panels/tab-strip";
|
|
86
|
+
import { renderStylebookOverlays } from "./panels/stylebook-panel";
|
|
87
|
+
import { registerLayersDnD, registerComponentsDnD, registerElementsDnD } from "./panels/dnd";
|
|
88
|
+
import { defaultDef } from "./panels/shared";
|
|
89
|
+
import { registerFunctionCompletions } from "./panels/editors";
|
|
90
|
+
import { renderBlockActionBar, initBlockActionBar } from "./panels/block-action-bar";
|
|
91
|
+
import { initCssData } from "./panels/style-utils";
|
|
92
|
+
import { updateForcedPseudoPreview } from "./panels/pseudo-preview";
|
|
93
|
+
import { initPanelEvents } from "./panels/panel-events";
|
|
94
|
+
import { initQuickSearch } from "./panels/quick-search";
|
|
95
|
+
import { addRecentProject } from "./recent-projects";
|
|
96
|
+
import { initWelcome } from "./panels/welcome-screen";
|
|
97
|
+
import { openNewProjectModal } from "./new-project/new-project-modal";
|
|
98
|
+
import { cloneRepository } from "./panels/git-panel";
|
|
99
|
+
import type { DocumentStackEntry, GitDiffState } from "./types";
|
|
100
|
+
import type { JxPath } from "./state";
|
|
101
|
+
import type { JxMutableNode } from "@jxsuite/schema/types";
|
|
97
102
|
|
|
98
103
|
// ─── Globals ──────────────────────────────────────────────────────────────────
|
|
99
104
|
// These mutable variables are local to studio.js for now. As sections are extracted
|
|
@@ -104,7 +109,7 @@ function getCanvasMode() {
|
|
|
104
109
|
}
|
|
105
110
|
|
|
106
111
|
/** @param {string} mode */
|
|
107
|
-
function setCanvasMode(mode) {
|
|
112
|
+
function setCanvasMode(mode: string) {
|
|
108
113
|
if (getCanvasMode() === "git-diff" && mode !== "git-diff") {
|
|
109
114
|
gitDiffState = null;
|
|
110
115
|
}
|
|
@@ -112,13 +117,12 @@ function setCanvasMode(mode) {
|
|
|
112
117
|
if (tab) tab.session.ui.canvasMode = mode;
|
|
113
118
|
}
|
|
114
119
|
|
|
115
|
-
|
|
116
|
-
let gitDiffState = null;
|
|
120
|
+
let gitDiffState: GitDiffState | null = null;
|
|
117
121
|
|
|
118
122
|
// ─── Component registry ───────────────────────────────────────────────────────
|
|
119
123
|
|
|
120
124
|
/** @param {string} componentPath */
|
|
121
|
-
async function navigateToComponent(componentPath) {
|
|
125
|
+
async function navigateToComponent(componentPath: string) {
|
|
122
126
|
try {
|
|
123
127
|
const platform = getPlatform();
|
|
124
128
|
const content = await platform.readFile(componentPath);
|
|
@@ -142,7 +146,7 @@ async function navigateToComponent(componentPath) {
|
|
|
142
146
|
// Load the component
|
|
143
147
|
tab.doc.document = parsed;
|
|
144
148
|
tab.doc.dirty = false;
|
|
145
|
-
tab.doc.mode =
|
|
149
|
+
tab.doc.mode = null as unknown as string;
|
|
146
150
|
tab.doc.sourceFormat = null;
|
|
147
151
|
tab.documentPath = componentPath;
|
|
148
152
|
tab.session.selection = null;
|
|
@@ -152,8 +156,8 @@ async function navigateToComponent(componentPath) {
|
|
|
152
156
|
|
|
153
157
|
render();
|
|
154
158
|
statusMessage(`Editing component: ${parsed.tagName || componentPath}`);
|
|
155
|
-
} catch (
|
|
156
|
-
const err =
|
|
159
|
+
} catch (e) {
|
|
160
|
+
const err = e as Error;
|
|
157
161
|
statusMessage(`Error: ${err.message}`);
|
|
158
162
|
}
|
|
159
163
|
}
|
|
@@ -165,23 +169,21 @@ async function navigateBack() {
|
|
|
165
169
|
try {
|
|
166
170
|
const platform = getPlatform();
|
|
167
171
|
await platform.writeFile(tab.documentPath, serializeDocument(tab));
|
|
168
|
-
} catch (
|
|
169
|
-
const err =
|
|
172
|
+
} catch (e) {
|
|
173
|
+
const err = e as Error;
|
|
170
174
|
statusMessage(`Save error: ${err.message}`);
|
|
171
175
|
}
|
|
172
176
|
}
|
|
173
177
|
|
|
174
178
|
// Pop the stack
|
|
175
|
-
const frame =
|
|
176
|
-
tab.session.documentStack.pop()
|
|
177
|
-
);
|
|
179
|
+
const frame = tab.session.documentStack.pop() as Record<string, unknown> | undefined;
|
|
178
180
|
if (!frame) return;
|
|
179
|
-
tab.doc.document =
|
|
180
|
-
tab.doc.dirty =
|
|
181
|
-
tab.doc.mode =
|
|
182
|
-
tab.doc.sourceFormat =
|
|
183
|
-
tab.documentPath =
|
|
184
|
-
tab.session.selection =
|
|
181
|
+
tab.doc.document = frame.document as JxMutableNode;
|
|
182
|
+
tab.doc.dirty = frame.dirty as boolean;
|
|
183
|
+
tab.doc.mode = frame.mode as string;
|
|
184
|
+
tab.doc.sourceFormat = frame.sourceFormat as string | null;
|
|
185
|
+
tab.documentPath = frame.documentPath as string | null;
|
|
186
|
+
tab.session.selection = frame.selection as JxPath | null;
|
|
185
187
|
view.leftTab = "layers";
|
|
186
188
|
|
|
187
189
|
render();
|
|
@@ -189,7 +191,7 @@ async function navigateBack() {
|
|
|
189
191
|
}
|
|
190
192
|
|
|
191
193
|
/** @param {number} targetIndex */
|
|
192
|
-
async function navigateToLevel(targetIndex) {
|
|
194
|
+
async function navigateToLevel(targetIndex: number) {
|
|
193
195
|
const tab = activeTab.value;
|
|
194
196
|
const stack = tab?.session.documentStack;
|
|
195
197
|
if (!stack || targetIndex < 0 || targetIndex >= stack.length) return;
|
|
@@ -197,20 +199,20 @@ async function navigateToLevel(targetIndex) {
|
|
|
197
199
|
try {
|
|
198
200
|
const platform = getPlatform();
|
|
199
201
|
await platform.writeFile(tab.documentPath, serializeDocument(tab));
|
|
200
|
-
} catch (
|
|
201
|
-
const err =
|
|
202
|
+
} catch (e) {
|
|
203
|
+
const err = e as Error;
|
|
202
204
|
statusMessage(`Save error: ${err.message}`);
|
|
203
205
|
}
|
|
204
206
|
}
|
|
205
207
|
|
|
206
|
-
const frame =
|
|
208
|
+
const frame = stack[targetIndex] as DocumentStackEntry;
|
|
207
209
|
tab.session.documentStack = stack.slice(0, targetIndex);
|
|
208
|
-
tab.doc.document =
|
|
209
|
-
tab.doc.dirty =
|
|
210
|
-
tab.doc.mode =
|
|
211
|
-
tab.doc.sourceFormat =
|
|
212
|
-
tab.documentPath =
|
|
213
|
-
tab.session.selection =
|
|
210
|
+
tab.doc.document = frame.document as JxMutableNode;
|
|
211
|
+
tab.doc.dirty = frame.dirty as boolean;
|
|
212
|
+
tab.doc.mode = frame.mode as string;
|
|
213
|
+
tab.doc.sourceFormat = frame.sourceFormat as string | null;
|
|
214
|
+
tab.documentPath = frame.documentPath as string | null;
|
|
215
|
+
tab.session.selection = frame.selection as JxPath | null;
|
|
214
216
|
view.leftTab = "layers";
|
|
215
217
|
|
|
216
218
|
render();
|
|
@@ -229,23 +231,16 @@ async function closeFunctionEditor() {
|
|
|
229
231
|
const minResult = await codeService("minify", { code: currentCode });
|
|
230
232
|
const bodyToStore = minResult?.code ?? currentCode;
|
|
231
233
|
if (editing.type === "def") {
|
|
232
|
-
transactDoc(tab, (t) =>
|
|
233
|
-
mutateUpdateDef(t, /** @type {string} */ (editing.defName), { body: bodyToStore }),
|
|
234
|
-
);
|
|
234
|
+
transactDoc(tab, (t) => mutateUpdateDef(t, editing.defName as string, { body: bodyToStore }));
|
|
235
235
|
} else if (editing.type === "event") {
|
|
236
|
-
const node = getNodeAtPath(tab.doc.document,
|
|
237
|
-
const current = node?.[
|
|
236
|
+
const node = getNodeAtPath(tab.doc.document, editing.path as JxPath);
|
|
237
|
+
const current = node?.[editing.eventKey as string] || {};
|
|
238
238
|
transactDoc(tab, (t) =>
|
|
239
|
-
mutateUpdateProperty(
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
.../** @type {object} */ (current),
|
|
245
|
-
$prototype: "Function",
|
|
246
|
-
body: bodyToStore,
|
|
247
|
-
},
|
|
248
|
-
),
|
|
239
|
+
mutateUpdateProperty(t, editing.path as JxPath, editing.eventKey as string, {
|
|
240
|
+
.../** @type {object} */ (current),
|
|
241
|
+
$prototype: "Function",
|
|
242
|
+
body: bodyToStore,
|
|
243
|
+
}),
|
|
249
244
|
);
|
|
250
245
|
}
|
|
251
246
|
view.functionEditor.dispose();
|
|
@@ -262,17 +257,25 @@ document.body.appendChild(datalistHost);
|
|
|
262
257
|
litRender(
|
|
263
258
|
html`
|
|
264
259
|
<datalist id="tag-names">
|
|
265
|
-
${webdata.allTags.map((
|
|
266
|
-
</datalist>
|
|
267
|
-
<datalist id="css-props">
|
|
268
|
-
${webdata.cssProps.map(
|
|
269
|
-
(/** @type {string[]} */ [name]) => html`<option value=${name}></option>`,
|
|
270
|
-
)}
|
|
260
|
+
${webdata.allTags.map((tag: string) => html`<option value=${tag}></option>`)}
|
|
271
261
|
</datalist>
|
|
262
|
+
<datalist id="css-props"></datalist>
|
|
272
263
|
`,
|
|
273
264
|
datalistHost,
|
|
274
265
|
);
|
|
275
266
|
|
|
267
|
+
requestIdleCallback(() => {
|
|
268
|
+
const dl = document.getElementById("css-props");
|
|
269
|
+
if (!dl) return;
|
|
270
|
+
const frag = document.createDocumentFragment();
|
|
271
|
+
for (const [name] of webdata.cssProps) {
|
|
272
|
+
const opt = document.createElement("option");
|
|
273
|
+
opt.value = name;
|
|
274
|
+
frag.appendChild(opt);
|
|
275
|
+
}
|
|
276
|
+
dl.appendChild(frag);
|
|
277
|
+
});
|
|
278
|
+
|
|
276
279
|
initCssData(webdata);
|
|
277
280
|
|
|
278
281
|
// ─── Module-level UI state (must be before render() call) ─────────────────────
|
|
@@ -293,10 +296,10 @@ initShellRefs();
|
|
|
293
296
|
// Mount extracted panel modules
|
|
294
297
|
toolbarPanel.mount(toolbarEl, {
|
|
295
298
|
navigateBack: () => navigateBack(),
|
|
296
|
-
navigateToLevel: (
|
|
299
|
+
navigateToLevel: (i: number) => navigateToLevel(i),
|
|
297
300
|
closeFunctionEditor: () => closeFunctionEditor(),
|
|
298
301
|
openProject: () => openProject(),
|
|
299
|
-
openRecentProject: (
|
|
302
|
+
openRecentProject: (root: string) => openRecentProject(root),
|
|
300
303
|
saveFile: () => saveFile(),
|
|
301
304
|
parseMediaEntries,
|
|
302
305
|
getCanvasMode,
|
|
@@ -308,7 +311,7 @@ toolbarPanel.mount(toolbarEl, {
|
|
|
308
311
|
initLayers();
|
|
309
312
|
initQuickSearch();
|
|
310
313
|
|
|
311
|
-
tabStrip.mount(
|
|
314
|
+
tabStrip.mount(document.querySelector("#tab-strip") as HTMLElement);
|
|
312
315
|
|
|
313
316
|
overlaysPanel.mount({
|
|
314
317
|
getCanvasMode,
|
|
@@ -351,16 +354,14 @@ initCanvasRender({
|
|
|
351
354
|
get gitDiffState() {
|
|
352
355
|
return gitDiffState;
|
|
353
356
|
},
|
|
354
|
-
setGitDiffState: (
|
|
355
|
-
/** @type {import("./canvas/canvas-render.js").GitDiffState | null} */ state,
|
|
356
|
-
) => {
|
|
357
|
+
setGitDiffState: (state: GitDiffState | null) => {
|
|
357
358
|
gitDiffState = state;
|
|
358
359
|
},
|
|
359
360
|
});
|
|
360
361
|
|
|
361
362
|
initWelcome({
|
|
362
363
|
openProject: () => openProject(),
|
|
363
|
-
openRecentProject: (
|
|
364
|
+
openRecentProject: (root: string) => openRecentProject(root),
|
|
364
365
|
openNewProject: async () => {
|
|
365
366
|
const result = await openNewProjectModal();
|
|
366
367
|
if (result) openRecentProject(result.root);
|
|
@@ -426,9 +427,7 @@ leftPanelMod.mount({
|
|
|
426
427
|
setupTreeKeyboard,
|
|
427
428
|
registerFileTreeDnD,
|
|
428
429
|
cloneRepository: () => cloneRepository({ openRecentProject }),
|
|
429
|
-
setGitDiffState: (
|
|
430
|
-
/** @type {import("./canvas/canvas-render.js").GitDiffState | null} */ state,
|
|
431
|
-
) => {
|
|
430
|
+
setGitDiffState: (state: GitDiffState | null) => {
|
|
432
431
|
gitDiffState = state;
|
|
433
432
|
},
|
|
434
433
|
});
|
|
@@ -444,7 +443,7 @@ mountStatusbar();
|
|
|
444
443
|
mountActivityBar();
|
|
445
444
|
|
|
446
445
|
// Clicking on the canvas-wrap background (outside any canvas panel) deselects the current element
|
|
447
|
-
canvasWrap.addEventListener("click", (
|
|
446
|
+
canvasWrap.addEventListener("click", (e: MouseEvent) => {
|
|
448
447
|
if (e.target !== canvasWrap && e.target !== view.panzoomWrap) return;
|
|
449
448
|
if (!activeTab.value?.session.selection) return;
|
|
450
449
|
activeTab.value.session.selection = null;
|
|
@@ -560,7 +559,7 @@ if (_projectParam) {
|
|
|
560
559
|
id: fileRelPath,
|
|
561
560
|
documentPath: fileRelPath,
|
|
562
561
|
document: parsedDoc,
|
|
563
|
-
frontmatter,
|
|
562
|
+
...(frontmatter != null && { frontmatter }),
|
|
564
563
|
sourceFormat: isMd ? "md" : null,
|
|
565
564
|
});
|
|
566
565
|
|
|
@@ -572,8 +571,8 @@ if (_projectParam) {
|
|
|
572
571
|
render();
|
|
573
572
|
statusMessage(`Opened ${fileRelPath}`);
|
|
574
573
|
}
|
|
575
|
-
} catch (
|
|
576
|
-
statusMessage(`Error: ${
|
|
574
|
+
} catch (e) {
|
|
575
|
+
statusMessage(`Error: ${(e as Error).message}`);
|
|
577
576
|
}
|
|
578
577
|
})();
|
|
579
578
|
}
|
|
@@ -619,7 +618,7 @@ function openProject() {
|
|
|
619
618
|
renderLeftPanel,
|
|
620
619
|
});
|
|
621
620
|
}
|
|
622
|
-
async function openRecentProject(
|
|
621
|
+
async function openRecentProject(root: string) {
|
|
623
622
|
try {
|
|
624
623
|
const platform = getPlatform();
|
|
625
624
|
platform.projectRoot = root;
|
|
@@ -667,14 +666,14 @@ async function openRecentProject(/** @type {string} */ root) {
|
|
|
667
666
|
statusMessage(`Opened project: ${requireProjectState().name}`);
|
|
668
667
|
|
|
669
668
|
await openHomePage();
|
|
670
|
-
} catch (
|
|
671
|
-
statusMessage(`Error: ${
|
|
669
|
+
} catch (e) {
|
|
670
|
+
statusMessage(`Error: ${(e as Error).message}`);
|
|
672
671
|
}
|
|
673
672
|
}
|
|
674
673
|
function renderFilesTemplate() {
|
|
675
674
|
return _renderFilesTemplate({ openProject, openFileFromTree, renderLeftPanel });
|
|
676
675
|
}
|
|
677
|
-
function openFileFromTree(
|
|
676
|
+
function openFileFromTree(path: string) {
|
|
678
677
|
return openFileInTab(path);
|
|
679
678
|
}
|
|
680
679
|
|
|
@@ -708,8 +707,7 @@ initShortcuts(() => ({
|
|
|
708
707
|
|
|
709
708
|
// ─── Autosave (registered as update middleware) ──────────────────────────────
|
|
710
709
|
|
|
711
|
-
|
|
712
|
-
const AUTO_SAVE_DELAY = 2000;
|
|
710
|
+
const AUTO_SAVE_DELAY: number = 2000;
|
|
713
711
|
|
|
714
712
|
function scheduleAutosave() {
|
|
715
713
|
const tab = activeTab.value;
|
package/src/tabs/tab.ts
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
/// <reference lib="dom" />
|
|
2
|
+
import { reactive, effectScope } from "../reactivity";
|
|
3
|
+
import type {
|
|
4
|
+
GitDiffState,
|
|
5
|
+
InlineEditDef,
|
|
6
|
+
FunctionEditDef,
|
|
7
|
+
DocumentStackEntry,
|
|
8
|
+
GitStatusResult,
|
|
9
|
+
GitBranchesResult,
|
|
10
|
+
} from "../types";
|
|
11
|
+
import type { JxMutableNode } from "@jxsuite/schema/types";
|
|
12
|
+
|
|
13
|
+
export interface TabUi {
|
|
14
|
+
rightTab: string;
|
|
15
|
+
canvasMode: string;
|
|
16
|
+
zoom: number;
|
|
17
|
+
activeMedia: string | null;
|
|
18
|
+
activeSelector: string | null;
|
|
19
|
+
editingFunction: FunctionEditDef | null;
|
|
20
|
+
featureToggles: Record<string, boolean>;
|
|
21
|
+
styleSections: Record<string, boolean>;
|
|
22
|
+
inspectorSections: Record<string, boolean>;
|
|
23
|
+
styleShorthands: Record<string, boolean>;
|
|
24
|
+
styleFilter: string;
|
|
25
|
+
styleFilterActive: boolean;
|
|
26
|
+
stylebookSelection: string | null;
|
|
27
|
+
stylebookTab: string;
|
|
28
|
+
stylebookFilter: string;
|
|
29
|
+
stylebookCustomizedOnly: boolean;
|
|
30
|
+
settingsTab: string;
|
|
31
|
+
gitStatus: GitStatusResult | null;
|
|
32
|
+
gitBranches: GitBranchesResult | null;
|
|
33
|
+
gitCommitMessage: string;
|
|
34
|
+
gitLoading: boolean;
|
|
35
|
+
gitError: string | null;
|
|
36
|
+
gitDiffState: GitDiffState | null;
|
|
37
|
+
pendingInlineEdit: InlineEditDef | null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
interface HistorySnapshot {
|
|
41
|
+
document: Record<string, unknown>;
|
|
42
|
+
selection: (string | number)[] | null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface Tab {
|
|
46
|
+
id: string;
|
|
47
|
+
documentPath: string | null;
|
|
48
|
+
fileHandle: FileSystemFileHandle | null;
|
|
49
|
+
capabilities: { modes: string[] };
|
|
50
|
+
scope: { stop(): void; run<T>(fn: () => T): T | undefined; [k: string]: unknown };
|
|
51
|
+
doc: {
|
|
52
|
+
document: JxMutableNode;
|
|
53
|
+
content: { frontmatter: Record<string, unknown> };
|
|
54
|
+
mode: string;
|
|
55
|
+
sourceFormat: string | null;
|
|
56
|
+
handlersSource: string | null;
|
|
57
|
+
dirty: boolean;
|
|
58
|
+
};
|
|
59
|
+
session: {
|
|
60
|
+
selection: (string | number)[] | null;
|
|
61
|
+
hover: (string | number)[] | null;
|
|
62
|
+
clipboard: JxMutableNode | null;
|
|
63
|
+
documentStack: DocumentStackEntry[];
|
|
64
|
+
ui: TabUi;
|
|
65
|
+
canvas: {
|
|
66
|
+
status: string;
|
|
67
|
+
scope: { stop(): void; [k: string]: unknown } | null;
|
|
68
|
+
error: string | null;
|
|
69
|
+
pendingInlineEdit: InlineEditDef | null;
|
|
70
|
+
};
|
|
71
|
+
};
|
|
72
|
+
history: {
|
|
73
|
+
snapshots: HistorySnapshot[];
|
|
74
|
+
index: number;
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** @returns {TabUi} */
|
|
79
|
+
function createDefaultUi() {
|
|
80
|
+
return {
|
|
81
|
+
rightTab: "properties",
|
|
82
|
+
canvasMode: "edit",
|
|
83
|
+
zoom: 1,
|
|
84
|
+
activeMedia: null,
|
|
85
|
+
activeSelector: null,
|
|
86
|
+
editingFunction: null,
|
|
87
|
+
featureToggles: {},
|
|
88
|
+
styleSections: {},
|
|
89
|
+
inspectorSections: {},
|
|
90
|
+
styleShorthands: {},
|
|
91
|
+
styleFilter: "",
|
|
92
|
+
styleFilterActive: false,
|
|
93
|
+
stylebookSelection: null,
|
|
94
|
+
stylebookTab: "elements",
|
|
95
|
+
stylebookFilter: "",
|
|
96
|
+
stylebookCustomizedOnly: false,
|
|
97
|
+
settingsTab: "stylebook",
|
|
98
|
+
gitStatus: null,
|
|
99
|
+
gitBranches: null,
|
|
100
|
+
gitCommitMessage: "",
|
|
101
|
+
gitLoading: false,
|
|
102
|
+
gitError: null,
|
|
103
|
+
gitDiffState: null,
|
|
104
|
+
pendingInlineEdit: null,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const ALL_MODES = ["edit", "design", "preview", "source", "stylebook"];
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Create a new tab with reactive doc/session/history trees, owned by an effectScope.
|
|
112
|
+
*
|
|
113
|
+
* @param {{
|
|
114
|
+
* id: string;
|
|
115
|
+
* documentPath?: string | null;
|
|
116
|
+
* fileHandle?: FileSystemFileHandle | null;
|
|
117
|
+
* document: Record<string, unknown>;
|
|
118
|
+
* frontmatter?: Record<string, unknown>;
|
|
119
|
+
* sourceFormat?: string | null;
|
|
120
|
+
* capabilities?: { modes?: string[] };
|
|
121
|
+
* }} opts
|
|
122
|
+
* @returns {Tab}
|
|
123
|
+
*/
|
|
124
|
+
export function createTab({
|
|
125
|
+
id,
|
|
126
|
+
documentPath = null,
|
|
127
|
+
fileHandle = null,
|
|
128
|
+
document,
|
|
129
|
+
frontmatter,
|
|
130
|
+
sourceFormat = null,
|
|
131
|
+
capabilities,
|
|
132
|
+
}: {
|
|
133
|
+
id: string;
|
|
134
|
+
documentPath?: string | null;
|
|
135
|
+
fileHandle?: FileSystemFileHandle | null;
|
|
136
|
+
document: Record<string, unknown>;
|
|
137
|
+
frontmatter?: Record<string, unknown>;
|
|
138
|
+
sourceFormat?: string | null;
|
|
139
|
+
capabilities?: { modes?: string[] };
|
|
140
|
+
}) {
|
|
141
|
+
const scope = effectScope();
|
|
142
|
+
|
|
143
|
+
const resolvedModes = capabilities?.modes ?? inferModes(documentPath, sourceFormat);
|
|
144
|
+
|
|
145
|
+
const tab = scope.run(() => ({
|
|
146
|
+
id,
|
|
147
|
+
documentPath,
|
|
148
|
+
fileHandle,
|
|
149
|
+
capabilities: { modes: resolvedModes },
|
|
150
|
+
scope,
|
|
151
|
+
doc: reactive({
|
|
152
|
+
document,
|
|
153
|
+
sourceFormat,
|
|
154
|
+
content: { frontmatter: frontmatter || {} },
|
|
155
|
+
mode:
|
|
156
|
+
sourceFormat === "md" ? "content" : documentPath?.endsWith(".md") ? "content" : "component",
|
|
157
|
+
handlersSource: null,
|
|
158
|
+
dirty: false,
|
|
159
|
+
}),
|
|
160
|
+
session: reactive({
|
|
161
|
+
selection: null,
|
|
162
|
+
hover: null,
|
|
163
|
+
clipboard: null,
|
|
164
|
+
documentStack: [],
|
|
165
|
+
ui: createDefaultUi(),
|
|
166
|
+
canvas: { status: "idle", scope: null, error: null, pendingInlineEdit: null },
|
|
167
|
+
}),
|
|
168
|
+
history: reactive({
|
|
169
|
+
snapshots: [{ document: structuredClone(document), selection: null }],
|
|
170
|
+
index: 0,
|
|
171
|
+
}),
|
|
172
|
+
})) as unknown as Tab;
|
|
173
|
+
|
|
174
|
+
return tab;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* @param {string | null | undefined} documentPath
|
|
179
|
+
* @param {string | null} sourceFormat
|
|
180
|
+
* @returns {string[]}
|
|
181
|
+
*/
|
|
182
|
+
function inferModes(documentPath: string | null | undefined, sourceFormat: string | null) {
|
|
183
|
+
if (documentPath === "project.json") return ["stylebook", "source"];
|
|
184
|
+
if (sourceFormat === "md" || documentPath?.endsWith(".md"))
|
|
185
|
+
return ["edit", "design", "preview", "source"];
|
|
186
|
+
return ALL_MODES;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Dispose a tab — stops its effectScope, killing all effects created within it.
|
|
191
|
+
*
|
|
192
|
+
* @param {Tab} tab
|
|
193
|
+
*/
|
|
194
|
+
export function disposeTab(tab: Tab) {
|
|
195
|
+
tab.scope.stop();
|
|
196
|
+
}
|