@jxsuite/studio 0.19.0 → 0.21.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.css +98 -98
- package/dist/studio.js +13476 -12330
- package/dist/studio.js.map +152 -148
- package/dist/workers/editor.worker.js +79 -79
- package/dist/workers/json.worker.js +109 -109
- package/dist/workers/ts.worker.js +82 -82
- package/package.json +19 -7
- package/src/browse/browse-modal.js +16 -13
- package/src/browse/browse.js +44 -35
- package/src/canvas/canvas-diff.js +24 -16
- package/src/canvas/canvas-helpers.js +24 -19
- package/src/canvas/canvas-live-render.js +113 -39
- package/src/canvas/canvas-render.js +119 -73
- package/src/canvas/canvas-utils.js +51 -21
- package/src/canvas/nested-site-style.js +50 -0
- package/src/editor/component-inline-edit.js +14 -13
- package/src/editor/content-inline-edit.js +22 -10
- package/src/editor/context-menu.js +28 -14
- package/src/editor/convert-targets.js +60 -0
- package/src/editor/convert-to-component.js +20 -19
- package/src/editor/convert-to-repeater.js +226 -0
- package/src/editor/inline-edit.js +69 -28
- package/src/editor/insertion-helper.js +16 -12
- package/src/editor/shortcuts.js +14 -4
- package/src/editor/slash-menu.js +40 -19
- package/src/files/components.js +1 -1
- package/src/files/file-ops.js +37 -16
- package/src/files/files.js +322 -68
- package/src/github/github-auth.js +122 -0
- package/src/github/github-publish.js +126 -0
- package/src/markdown/md-convert.js +172 -107
- package/src/new-project/new-project-modal.js +204 -0
- package/src/panels/activity-bar.js +27 -25
- package/src/panels/ai-panel.js +406 -0
- package/src/panels/block-action-bar.js +77 -28
- package/src/panels/canvas-dnd.js +8 -2
- package/src/panels/data-explorer.js +19 -13
- package/src/panels/dnd.js +144 -92
- package/src/panels/editors.js +55 -35
- package/src/panels/elements-panel.js +33 -19
- package/src/panels/events-panel.js +15 -10
- package/src/panels/git-panel.js +475 -99
- package/src/panels/head-panel.js +121 -81
- package/src/panels/imports-panel.js +94 -65
- package/src/panels/layers-panel.js +64 -43
- package/src/panels/left-panel.js +105 -48
- package/src/panels/overlays.js +1 -0
- package/src/panels/panel-events.js +45 -30
- package/src/panels/preview-render.js +23 -18
- package/src/panels/properties-panel.js +378 -257
- package/src/panels/pseudo-preview.js +2 -2
- package/src/panels/quick-search.js +11 -9
- package/src/panels/right-panel.js +42 -16
- package/src/panels/shared.js +4 -4
- package/src/panels/signals-panel.js +415 -166
- package/src/panels/statusbar.js +10 -6
- package/src/panels/style-inputs.js +67 -35
- package/src/panels/style-panel.js +239 -134
- package/src/panels/style-utils.js +34 -16
- package/src/panels/stylebook-layers-panel.js +12 -10
- package/src/panels/stylebook-panel.js +134 -66
- package/src/panels/tab-strip.js +5 -2
- package/src/panels/toolbar.js +333 -109
- package/src/panels/welcome-screen.js +121 -0
- package/src/platform.js +2 -4
- package/src/platforms/devserver.js +113 -7
- package/src/resize-edges.js +98 -0
- package/src/services/cem-export.js +12 -7
- package/src/services/code-services.js +30 -12
- package/src/settings/content-types-editor.js +8 -8
- package/src/settings/css-vars-editor.js +30 -24
- package/src/settings/defs-editor.js +12 -7
- package/src/settings/general-settings.js +85 -3
- package/src/settings/head-editor.js +30 -24
- package/src/settings/schema-field-ui.js +58 -39
- package/src/settings/settings-modal.js +12 -9
- package/src/site-context.js +41 -31
- package/src/state.js +70 -29
- package/src/store.js +36 -35
- package/src/studio.js +120 -90
- package/src/tabs/tab.js +67 -43
- package/src/tabs/transact.js +23 -13
- package/src/ui/button-group.js +24 -24
- package/src/ui/color-selector.js +28 -23
- package/src/ui/field-row.js +3 -3
- package/src/ui/icons.js +2 -2
- package/src/ui/layers.js +31 -1
- package/src/ui/media-picker.js +7 -7
- package/src/ui/spectrum.js +5 -1
- package/src/ui/unit-selector.js +18 -16
- package/src/ui/value-selector.js +21 -15
- package/src/ui/widgets.js +22 -19
- package/src/utils/canvas-media.js +5 -4
- package/src/utils/edit-display.js +32 -26
- package/src/utils/google-fonts.js +11 -11
- package/src/utils/inherited-style.js +9 -8
- package/src/utils/studio-utils.js +18 -18
- package/src/view.js +58 -1
- package/src/workspace/workspace.js +29 -7
package/src/state.js
CHANGED
|
@@ -9,28 +9,37 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
|
-
* @typedef {Record<string, any>} JxNode
|
|
13
|
-
*
|
|
14
12
|
* @typedef {(string | number)[]} JxPath
|
|
15
13
|
*
|
|
16
|
-
* @typedef {{ document:
|
|
14
|
+
* @typedef {{ document: JxMutableNode; selection: JxPath | null }} HistorySnapshot
|
|
17
15
|
*
|
|
18
16
|
* @typedef {{
|
|
19
|
-
* document:
|
|
17
|
+
* document: JxMutableNode;
|
|
20
18
|
* selection: JxPath | null;
|
|
21
19
|
* hover: JxPath | null;
|
|
22
20
|
* history: HistorySnapshot[];
|
|
23
21
|
* historyIndex: number;
|
|
24
22
|
* dirty: boolean;
|
|
25
|
-
* fileHandle:
|
|
23
|
+
* fileHandle: FileSystemFileHandle | null;
|
|
26
24
|
* documentPath: string | null;
|
|
27
|
-
* documentStack:
|
|
25
|
+
* documentStack: StudioStackFrame[];
|
|
28
26
|
* handlersSource: string | null;
|
|
29
27
|
* mode: string;
|
|
30
|
-
* content: { frontmatter: Record<string,
|
|
31
|
-
* ui: Record<string,
|
|
32
|
-
* canvas: { status: string; scope:
|
|
28
|
+
* content: { frontmatter: Record<string, unknown> };
|
|
29
|
+
* ui: Record<string, unknown>;
|
|
30
|
+
* canvas: { status: string; scope: Record<string, unknown> | null; error: string | null };
|
|
33
31
|
* }} StudioState
|
|
32
|
+
*
|
|
33
|
+
* @typedef {{
|
|
34
|
+
* document: JxMutableNode;
|
|
35
|
+
* selection: JxPath | null;
|
|
36
|
+
* fileHandle: FileSystemFileHandle | null;
|
|
37
|
+
* documentPath: string | null;
|
|
38
|
+
* dirty: boolean;
|
|
39
|
+
* history: HistorySnapshot[];
|
|
40
|
+
* historyIndex: number;
|
|
41
|
+
* mode: string;
|
|
42
|
+
* }} StudioStackFrame
|
|
34
43
|
*/
|
|
35
44
|
|
|
36
45
|
// ─── Path utilities ───────────────────────────────────────────────────────────
|
|
@@ -38,14 +47,14 @@
|
|
|
38
47
|
/**
|
|
39
48
|
* Walk the document tree and return the node at the given path.
|
|
40
49
|
*
|
|
41
|
-
* @param {
|
|
50
|
+
* @param {JxMutableNode} doc
|
|
42
51
|
* @param {JxPath} path
|
|
43
|
-
* @returns {
|
|
52
|
+
* @returns {JxMutableNode}
|
|
44
53
|
*/
|
|
45
54
|
export function getNodeAtPath(doc, path) {
|
|
46
55
|
let node = doc;
|
|
47
56
|
for (const key of path) {
|
|
48
|
-
if (node == null) return undefined;
|
|
57
|
+
if (node == null) return /** @type {JxMutableNode} */ (/** @type {unknown} */ (undefined));
|
|
49
58
|
node = node[key];
|
|
50
59
|
}
|
|
51
60
|
return node;
|
|
@@ -114,10 +123,15 @@ export function isAncestor(path, descendant) {
|
|
|
114
123
|
*
|
|
115
124
|
* NodeType: 'element' (default) | 'map' | 'case' | 'case-ref'
|
|
116
125
|
*
|
|
117
|
-
* @param {
|
|
126
|
+
* @param {JxMutableNode | string | number | boolean} doc
|
|
118
127
|
* @param {JxPath} [path]
|
|
119
128
|
* @param {number} [depth]
|
|
120
|
-
* @returns {{
|
|
129
|
+
* @returns {{
|
|
130
|
+
* node: JxMutableNode | string | number | boolean;
|
|
131
|
+
* path: JxPath;
|
|
132
|
+
* depth: number;
|
|
133
|
+
* nodeType: string;
|
|
134
|
+
* }[]}
|
|
121
135
|
*/
|
|
122
136
|
export function flattenTree(doc, path = [], depth = 0) {
|
|
123
137
|
// Text node children: bare primitives get a "text" row
|
|
@@ -125,7 +139,14 @@ export function flattenTree(doc, path = [], depth = 0) {
|
|
|
125
139
|
return [{ node: doc, path, depth, nodeType: "text" }];
|
|
126
140
|
}
|
|
127
141
|
|
|
128
|
-
/**
|
|
142
|
+
/**
|
|
143
|
+
* @type {{
|
|
144
|
+
* node: JxMutableNode | string | number | boolean;
|
|
145
|
+
* path: JxPath;
|
|
146
|
+
* depth: number;
|
|
147
|
+
* nodeType: string;
|
|
148
|
+
* }[]}
|
|
149
|
+
*/
|
|
129
150
|
const rows = [{ node: doc, path, depth, nodeType: "element" }];
|
|
130
151
|
|
|
131
152
|
// Custom component instances without user-authored children are atomic in the layer tree
|
|
@@ -140,12 +161,22 @@ export function flattenTree(doc, path = [], depth = 0) {
|
|
|
140
161
|
const childPath = [...path, "children", i];
|
|
141
162
|
rows.push(...flattenTree(children[i], childPath, depth + 1));
|
|
142
163
|
}
|
|
143
|
-
} else if (
|
|
164
|
+
} else if (
|
|
165
|
+
children &&
|
|
166
|
+
typeof children === "object" &&
|
|
167
|
+
/** @type {JxMutableNode} */ (children).$prototype === "Array"
|
|
168
|
+
) {
|
|
144
169
|
// $map — emit the map container, then recurse into the template
|
|
145
170
|
rows.push({ node: children, path: [...path, "children"], depth: depth + 1, nodeType: "map" });
|
|
146
|
-
const mapDef = children.map;
|
|
171
|
+
const mapDef = /** @type {JxMutableNode} */ (children).map;
|
|
147
172
|
if (mapDef && typeof mapDef === "object") {
|
|
148
|
-
rows.push(
|
|
173
|
+
rows.push(
|
|
174
|
+
...flattenTree(
|
|
175
|
+
/** @type {JxMutableNode} */ (mapDef),
|
|
176
|
+
[...path, "children", "map"],
|
|
177
|
+
depth + 2,
|
|
178
|
+
),
|
|
179
|
+
);
|
|
149
180
|
}
|
|
150
181
|
}
|
|
151
182
|
|
|
@@ -153,7 +184,7 @@ export function flattenTree(doc, path = [], depth = 0) {
|
|
|
153
184
|
if (doc.$switch && doc.cases && typeof doc.cases === "object") {
|
|
154
185
|
for (const [caseName, caseDef] of Object.entries(doc.cases)) {
|
|
155
186
|
const casePath = [...path, "cases", caseName];
|
|
156
|
-
if (caseDef && typeof caseDef === "object" && /** @type {
|
|
187
|
+
if (caseDef && typeof caseDef === "object" && /** @type {JxMutableNode} */ (caseDef).$ref) {
|
|
157
188
|
rows.push({ node: caseDef, path: casePath, depth: depth + 1, nodeType: "case-ref" });
|
|
158
189
|
} else if (caseDef && typeof caseDef === "object") {
|
|
159
190
|
rows.push({ node: caseDef, path: casePath, depth: depth + 1, nodeType: "case" });
|
|
@@ -170,7 +201,7 @@ export function flattenTree(doc, path = [], depth = 0) {
|
|
|
170
201
|
/**
|
|
171
202
|
* Get a display label for a node (for layers + overlays).
|
|
172
203
|
*
|
|
173
|
-
* @param {
|
|
204
|
+
* @param {JxMutableNode | null} node
|
|
174
205
|
* @returns {string}
|
|
175
206
|
*/
|
|
176
207
|
export function nodeLabel(node) {
|
|
@@ -193,7 +224,7 @@ export function nodeLabel(node) {
|
|
|
193
224
|
// ─── State factory ────────────────────────────────────────────────────────────
|
|
194
225
|
|
|
195
226
|
/**
|
|
196
|
-
* @param {
|
|
227
|
+
* @param {JxMutableNode} doc
|
|
197
228
|
* @returns {StudioState}
|
|
198
229
|
*/
|
|
199
230
|
export function createState(doc) {
|
|
@@ -248,19 +279,19 @@ export function createState(doc) {
|
|
|
248
279
|
/**
|
|
249
280
|
* Compose a flat StudioState from separate doc and session slices.
|
|
250
281
|
*
|
|
251
|
-
* @param {
|
|
252
|
-
* @param {
|
|
282
|
+
* @param {Partial<StudioState>} doc
|
|
283
|
+
* @param {Partial<StudioState>} session
|
|
253
284
|
* @returns {StudioState}
|
|
254
285
|
*/
|
|
255
286
|
export function toFlat(doc, session) {
|
|
256
|
-
return { ...doc, ...session };
|
|
287
|
+
return /** @type {StudioState} */ ({ ...doc, ...session });
|
|
257
288
|
}
|
|
258
289
|
|
|
259
290
|
/**
|
|
260
291
|
* Decompose a flat StudioState into doc and session slices.
|
|
261
292
|
*
|
|
262
293
|
* @param {StudioState} S
|
|
263
|
-
* @returns {{ doc:
|
|
294
|
+
* @returns {{ doc: Partial<StudioState>; session: Partial<StudioState> }}
|
|
264
295
|
*/
|
|
265
296
|
export function fromFlat(S) {
|
|
266
297
|
const {
|
|
@@ -303,14 +334,24 @@ export function fromFlat(S) {
|
|
|
303
334
|
// selectedPath: string|null, searchQuery: string }
|
|
304
335
|
// DirEntry: { name, path, type: "file"|"directory", size, modified }
|
|
305
336
|
|
|
306
|
-
/** @type {
|
|
337
|
+
/** @type {ProjectState | null} */
|
|
307
338
|
export let projectState = null;
|
|
308
339
|
|
|
309
|
-
/** @param {
|
|
340
|
+
/** @param {ProjectState | null} ps */
|
|
310
341
|
export function setProjectState(ps) {
|
|
311
342
|
projectState = ps;
|
|
312
343
|
}
|
|
313
344
|
|
|
345
|
+
/**
|
|
346
|
+
* Return the current project state, asserting it is non-null. Only call when a project is known to
|
|
347
|
+
* be loaded.
|
|
348
|
+
*
|
|
349
|
+
* @returns {ProjectState}
|
|
350
|
+
*/
|
|
351
|
+
export function requireProjectState() {
|
|
352
|
+
return /** @type {ProjectState} */ (projectState);
|
|
353
|
+
}
|
|
354
|
+
|
|
314
355
|
// ─── Frontmatter mutation ───────────────────────────────────────────────────
|
|
315
356
|
|
|
316
357
|
/**
|
|
@@ -319,7 +360,7 @@ export function setProjectState(ps) {
|
|
|
319
360
|
*
|
|
320
361
|
* @param {StudioState} state
|
|
321
362
|
* @param {string} field
|
|
322
|
-
* @param {
|
|
363
|
+
* @param {unknown} value
|
|
323
364
|
* @returns {StudioState}
|
|
324
365
|
*/
|
|
325
366
|
export function updateFrontmatter(state, field, value) {
|
|
@@ -359,7 +400,7 @@ export function hoverNode(state, path) {
|
|
|
359
400
|
* Push current document onto the stack and switch to editing a new document.
|
|
360
401
|
*
|
|
361
402
|
* @param {StudioState} state
|
|
362
|
-
* @param {
|
|
403
|
+
* @param {JxMutableNode} doc
|
|
363
404
|
* @param {string | null} documentPath
|
|
364
405
|
* @returns {StudioState}
|
|
365
406
|
*/
|
package/src/store.js
CHANGED
|
@@ -22,42 +22,34 @@ export {
|
|
|
22
22
|
isAncestor,
|
|
23
23
|
projectState,
|
|
24
24
|
setProjectState,
|
|
25
|
+
requireProjectState,
|
|
25
26
|
updateFrontmatter,
|
|
26
27
|
} from "./state.js";
|
|
27
28
|
|
|
28
|
-
// ───
|
|
29
|
+
// ─── Shell element refs (populated by initShellRefs) ─────────────────────────
|
|
29
30
|
|
|
30
|
-
export
|
|
31
|
-
export
|
|
31
|
+
export let canvasWrap = /** @type {HTMLElement} */ (/** @type {unknown} */ (null));
|
|
32
|
+
export let activityBar = /** @type {HTMLElement} */ (/** @type {unknown} */ (null));
|
|
33
|
+
export let leftPanel = /** @type {HTMLElement} */ (/** @type {unknown} */ (null));
|
|
34
|
+
export let rightPanel = /** @type {HTMLElement} */ (/** @type {unknown} */ (null));
|
|
35
|
+
export let toolbarEl = /** @type {HTMLElement} */ (/** @type {unknown} */ (null));
|
|
36
|
+
export let statusbarEl = /** @type {HTMLElement} */ (/** @type {unknown} */ (null));
|
|
32
37
|
|
|
33
|
-
export
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
38
|
+
export function initShellRefs() {
|
|
39
|
+
canvasWrap = /** @type {HTMLElement} */ (document.querySelector("#canvas-wrap"));
|
|
40
|
+
activityBar = /** @type {HTMLElement} */ (document.querySelector("#activity-bar"));
|
|
41
|
+
leftPanel = /** @type {HTMLElement} */ (document.querySelector("#left-panel"));
|
|
42
|
+
rightPanel = /** @type {HTMLElement} */ (document.querySelector("#right-panel"));
|
|
43
|
+
toolbarEl = /** @type {HTMLElement} */ (document.querySelector("#toolbar"));
|
|
44
|
+
statusbarEl = /** @type {HTMLElement} */ (document.querySelector("#statusbar"));
|
|
45
|
+
}
|
|
39
46
|
|
|
40
47
|
// ─── Shared containers (mutated in place by owner modules) ───────────────────
|
|
41
48
|
|
|
42
|
-
/** WeakMap<
|
|
49
|
+
/** @type {WeakMap<Element, JxPath>} */
|
|
43
50
|
export const elToPath = new WeakMap();
|
|
44
51
|
|
|
45
|
-
/**
|
|
46
|
-
* Canvas panels: Array<{ mediaName, canvas, overlay, overlayClk, viewport, dropLine, element,
|
|
47
|
-
* _width }>
|
|
48
|
-
*
|
|
49
|
-
* @type {{
|
|
50
|
-
* mediaName: string;
|
|
51
|
-
* canvas: HTMLElement;
|
|
52
|
-
* overlay: HTMLElement;
|
|
53
|
-
* overlayClk: HTMLElement;
|
|
54
|
-
* viewport: HTMLElement;
|
|
55
|
-
* scrollContainer?: HTMLElement | null;
|
|
56
|
-
* dropLine: HTMLElement;
|
|
57
|
-
* element?: HTMLElement | null;
|
|
58
|
-
* _width?: number | null;
|
|
59
|
-
* }[]}
|
|
60
|
-
*/
|
|
52
|
+
/** @type {import("./panels/canvas-dnd.js").CanvasPanel[]} */
|
|
61
53
|
export const canvasPanels = [];
|
|
62
54
|
|
|
63
55
|
// ─── Shared constants ────────────────────────────────────────────────────────
|
|
@@ -130,8 +122,8 @@ export function cancelStyleDebounce(/** @type {string} */ prop) {
|
|
|
130
122
|
/**
|
|
131
123
|
* Strip all on* event handler properties from a Jx document tree (deep clone).
|
|
132
124
|
*
|
|
133
|
-
* @param {
|
|
134
|
-
* @returns {
|
|
125
|
+
* @param {JxMutableNode} node
|
|
126
|
+
* @returns {JxMutableNode}
|
|
135
127
|
*/
|
|
136
128
|
export function stripEventHandlers(node) {
|
|
137
129
|
if (!node || typeof node !== "object") return node;
|
|
@@ -218,18 +210,27 @@ export function renderOnly(...names) {
|
|
|
218
210
|
export function updateSession(patch) {
|
|
219
211
|
const tab = activeTab.value;
|
|
220
212
|
if (tab) {
|
|
221
|
-
const p = /**
|
|
222
|
-
|
|
223
|
-
|
|
213
|
+
const p = /**
|
|
214
|
+
* @type {{
|
|
215
|
+
* selection?: unknown;
|
|
216
|
+
* hover?: unknown;
|
|
217
|
+
* clipboard?: unknown;
|
|
218
|
+
* ui?: Record<string, unknown>;
|
|
219
|
+
* canvas?: Record<string, unknown>;
|
|
220
|
+
* }}
|
|
221
|
+
*/ (patch);
|
|
222
|
+
if (p.selection !== undefined)
|
|
223
|
+
tab.session.selection = /** @type {JxPath | null} */ (p.selection);
|
|
224
|
+
if (p.hover !== undefined) tab.session.hover = /** @type {JxPath | null} */ (p.hover);
|
|
224
225
|
if (p.clipboard !== undefined) tab.session.clipboard = p.clipboard;
|
|
225
226
|
if (p.ui) {
|
|
226
227
|
for (const [k, v] of Object.entries(p.ui)) {
|
|
227
|
-
/** @type {
|
|
228
|
+
/** @type {Record<string, unknown>} */ (tab.session.ui)[k] = v;
|
|
228
229
|
}
|
|
229
230
|
}
|
|
230
231
|
if (p.canvas) {
|
|
231
232
|
for (const [k, v] of Object.entries(p.canvas)) {
|
|
232
|
-
/** @type {
|
|
233
|
+
/** @type {Record<string, unknown>} */ (tab.session.canvas)[k] = v;
|
|
233
234
|
}
|
|
234
235
|
}
|
|
235
236
|
}
|
|
@@ -244,7 +245,7 @@ export function updateSession(patch) {
|
|
|
244
245
|
export function updateUi(field, value) {
|
|
245
246
|
const tab = activeTab.value;
|
|
246
247
|
if (tab) {
|
|
247
|
-
/** @type {
|
|
248
|
+
/** @type {Record<string, unknown>} */ (tab.session.ui)[field] = value;
|
|
248
249
|
}
|
|
249
250
|
}
|
|
250
251
|
|
|
@@ -257,7 +258,7 @@ export function updateCanvas(patch) {
|
|
|
257
258
|
const tab = activeTab.value;
|
|
258
259
|
if (tab) {
|
|
259
260
|
for (const [k, v] of Object.entries(patch)) {
|
|
260
|
-
/** @type {
|
|
261
|
+
/** @type {Record<string, unknown>} */ (tab.session.canvas)[k] = v;
|
|
261
262
|
}
|
|
262
263
|
}
|
|
263
264
|
}
|