@jxsuite/studio 0.11.0 → 0.14.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 +6517 -5870
- package/dist/studio.js.map +57 -51
- package/package.json +5 -3
- package/src/canvas/canvas-diff.js +184 -0
- package/src/canvas/canvas-helpers.js +10 -14
- package/src/canvas/canvas-live-render.js +28 -2
- package/src/canvas/canvas-render.js +170 -20
- package/src/canvas/canvas-utils.js +22 -25
- package/src/editor/component-inline-edit.js +55 -44
- package/src/editor/content-inline-edit.js +47 -50
- package/src/editor/context-menu.js +78 -53
- package/src/editor/convert-to-component.js +11 -14
- package/src/editor/insertion-helper.js +8 -11
- package/src/editor/shortcuts.js +88 -64
- package/src/files/components.js +15 -4
- package/src/files/file-ops.js +57 -108
- package/src/files/files.js +81 -28
- package/src/panels/activity-bar.js +31 -7
- package/src/panels/block-action-bar.js +104 -80
- package/src/panels/canvas-dnd.js +4 -10
- package/src/panels/dnd.js +77 -35
- package/src/panels/editors.js +17 -26
- package/src/panels/elements-panel.js +17 -11
- package/src/panels/events-panel.js +44 -39
- package/src/panels/git-panel.js +47 -4
- package/src/panels/layers-panel.js +25 -21
- package/src/panels/left-panel.js +109 -44
- package/src/panels/overlays.js +91 -41
- package/src/panels/panel-events.js +28 -37
- package/src/panels/preview-render.js +7 -3
- package/src/panels/properties-panel.js +179 -104
- package/src/panels/pseudo-preview.js +9 -8
- package/src/panels/right-panel.js +85 -37
- package/src/panels/shared.js +0 -22
- package/src/panels/signals-panel.js +125 -54
- package/src/panels/statusbar.js +26 -19
- package/src/panels/style-inputs.js +5 -4
- package/src/panels/style-panel.js +128 -105
- package/src/panels/style-utils.js +8 -6
- package/src/panels/stylebook-layers-panel.js +5 -5
- package/src/panels/stylebook-panel.js +27 -31
- package/src/panels/tab-strip.js +124 -0
- package/src/panels/toolbar.js +40 -10
- package/src/reactivity.js +28 -0
- package/src/settings/content-types-editor.js +56 -7
- package/src/settings/defs-editor.js +56 -7
- package/src/settings/schema-field-ui.js +60 -25
- package/src/state.js +2 -459
- package/src/store.js +61 -219
- package/src/studio.js +163 -300
- package/src/tabs/tab.js +168 -0
- package/src/tabs/transact.js +406 -0
- package/src/ui/color-selector.js +4 -5
- package/src/view.js +3 -0
- package/src/workspace/workspace.js +90 -0
package/src/store.js
CHANGED
|
@@ -8,35 +8,10 @@
|
|
|
8
8
|
|
|
9
9
|
// ─── Re-exports from state.js ────────────────────────────────────────────────
|
|
10
10
|
|
|
11
|
+
import { activeTab } from "./workspace/workspace.js";
|
|
12
|
+
|
|
11
13
|
export {
|
|
12
14
|
createState,
|
|
13
|
-
selectNode,
|
|
14
|
-
hoverNode,
|
|
15
|
-
undo,
|
|
16
|
-
redo,
|
|
17
|
-
insertNode,
|
|
18
|
-
removeNode,
|
|
19
|
-
duplicateNode,
|
|
20
|
-
wrapNode,
|
|
21
|
-
moveNode,
|
|
22
|
-
updateProperty,
|
|
23
|
-
updateStyle,
|
|
24
|
-
updateAttribute,
|
|
25
|
-
addDef,
|
|
26
|
-
removeDef,
|
|
27
|
-
updateDef,
|
|
28
|
-
renameDef,
|
|
29
|
-
updateMediaStyle,
|
|
30
|
-
updateMedia,
|
|
31
|
-
updateNestedStyle,
|
|
32
|
-
updateMediaNestedStyle,
|
|
33
|
-
pushDocument,
|
|
34
|
-
popDocument,
|
|
35
|
-
updateProp,
|
|
36
|
-
addSwitchCase,
|
|
37
|
-
removeSwitchCase,
|
|
38
|
-
renameSwitchCase,
|
|
39
|
-
applyMutation,
|
|
40
15
|
getNodeAtPath,
|
|
41
16
|
flattenTree,
|
|
42
17
|
nodeLabel,
|
|
@@ -48,8 +23,6 @@ export {
|
|
|
48
23
|
projectState,
|
|
49
24
|
setProjectState,
|
|
50
25
|
updateFrontmatter,
|
|
51
|
-
toFlat,
|
|
52
|
-
fromFlat,
|
|
53
26
|
} from "./state.js";
|
|
54
27
|
|
|
55
28
|
// ─── DOM shortcuts & element refs ────────────────────────────────────────────
|
|
@@ -57,12 +30,12 @@ export {
|
|
|
57
30
|
export const $ = (/** @type {string} */ sel) => document.querySelector(sel);
|
|
58
31
|
export const _$$ = (/** @type {string} */ sel) => document.querySelectorAll(sel);
|
|
59
32
|
|
|
60
|
-
export const canvasWrap = /** @type {
|
|
61
|
-
export const activityBar = /** @type {
|
|
62
|
-
export const leftPanel = /** @type {
|
|
63
|
-
export const rightPanel = /** @type {
|
|
64
|
-
export const toolbarEl = /** @type {
|
|
65
|
-
export const statusbarEl = /** @type {
|
|
33
|
+
export const canvasWrap = /** @type {HTMLElement} */ (document.querySelector("#canvas-wrap"));
|
|
34
|
+
export const activityBar = /** @type {HTMLElement} */ (document.querySelector("#activity-bar"));
|
|
35
|
+
export const leftPanel = /** @type {HTMLElement} */ (document.querySelector("#left-panel"));
|
|
36
|
+
export const rightPanel = /** @type {HTMLElement} */ (document.querySelector("#right-panel"));
|
|
37
|
+
export const toolbarEl = /** @type {HTMLElement} */ (document.querySelector("#toolbar"));
|
|
38
|
+
export const statusbarEl = /** @type {HTMLElement} */ (document.querySelector("#statusbar"));
|
|
66
39
|
|
|
67
40
|
// ─── Shared containers (mutated in place by owner modules) ───────────────────
|
|
68
41
|
|
|
@@ -70,9 +43,19 @@ export const statusbarEl = /** @type {any} */ (document.querySelector("#statusba
|
|
|
70
43
|
export const elToPath = new WeakMap();
|
|
71
44
|
|
|
72
45
|
/**
|
|
73
|
-
* Canvas panels: Array<{ mediaName, canvas, overlay, overlayClk, viewport, dropLine
|
|
46
|
+
* Canvas panels: Array<{ mediaName, canvas, overlay, overlayClk, viewport, dropLine, element,
|
|
47
|
+
* _width }>
|
|
74
48
|
*
|
|
75
|
-
* @type {
|
|
49
|
+
* @type {{
|
|
50
|
+
* mediaName: string;
|
|
51
|
+
* canvas: HTMLElement;
|
|
52
|
+
* overlay: HTMLElement;
|
|
53
|
+
* overlayClk: HTMLElement;
|
|
54
|
+
* viewport: HTMLElement;
|
|
55
|
+
* dropLine: HTMLElement;
|
|
56
|
+
* element?: HTMLElement | null;
|
|
57
|
+
* _width?: number | null;
|
|
58
|
+
* }[]}
|
|
76
59
|
*/
|
|
77
60
|
export const canvasPanels = [];
|
|
78
61
|
|
|
@@ -110,7 +93,7 @@ export const COMMON_SELECTORS = [
|
|
|
110
93
|
"::placeholder",
|
|
111
94
|
];
|
|
112
95
|
|
|
113
|
-
/** @param {
|
|
96
|
+
/** @param {string} k */
|
|
114
97
|
export function isNestedSelector(k) {
|
|
115
98
|
return k.startsWith(":") || k.startsWith(".") || k.startsWith("&") || k.startsWith("[");
|
|
116
99
|
}
|
|
@@ -120,12 +103,12 @@ export function isNestedSelector(k) {
|
|
|
120
103
|
const _styleDebounceTimers = new Map();
|
|
121
104
|
|
|
122
105
|
/**
|
|
123
|
-
* @param {
|
|
124
|
-
* @param {
|
|
125
|
-
* @param {
|
|
106
|
+
* @param {string} prop
|
|
107
|
+
* @param {number} ms
|
|
108
|
+
* @param {Function} fn
|
|
126
109
|
*/
|
|
127
110
|
export function debouncedStyleCommit(prop, ms, fn) {
|
|
128
|
-
return (/** @type {
|
|
111
|
+
return (/** @type {unknown[]} */ ...args) => {
|
|
129
112
|
clearTimeout(_styleDebounceTimers.get(prop));
|
|
130
113
|
_styleDebounceTimers.set(
|
|
131
114
|
prop,
|
|
@@ -146,13 +129,13 @@ export function cancelStyleDebounce(/** @type {string} */ prop) {
|
|
|
146
129
|
/**
|
|
147
130
|
* Strip all on* event handler properties from a Jx document tree (deep clone).
|
|
148
131
|
*
|
|
149
|
-
* @param {
|
|
150
|
-
* @returns {
|
|
132
|
+
* @param {import("./state.js").JxNode | unknown} node
|
|
133
|
+
* @returns {import("./state.js").JxNode | unknown}
|
|
151
134
|
*/
|
|
152
135
|
export function stripEventHandlers(node) {
|
|
153
136
|
if (!node || typeof node !== "object") return node;
|
|
154
137
|
if (Array.isArray(node)) return node.map(stripEventHandlers);
|
|
155
|
-
/** @type {Record<string,
|
|
138
|
+
/** @type {Record<string, unknown>} */
|
|
156
139
|
const out = {};
|
|
157
140
|
for (const [k, v] of Object.entries(node)) {
|
|
158
141
|
if (k.startsWith("on") && typeof v === "object" && (v?.$ref || v?.$prototype === "Function"))
|
|
@@ -160,12 +143,12 @@ export function stripEventHandlers(node) {
|
|
|
160
143
|
if (k === "children") {
|
|
161
144
|
out.children = Array.isArray(v) ? v.map(stripEventHandlers) : stripEventHandlers(v);
|
|
162
145
|
} else if (k === "cases" && typeof v === "object") {
|
|
163
|
-
/** @type {Record<string,
|
|
146
|
+
/** @type {Record<string, unknown>} */
|
|
164
147
|
const cases = {};
|
|
165
148
|
for (const [ck, cv] of Object.entries(v)) cases[ck] = stripEventHandlers(cv);
|
|
166
149
|
out.cases = cases;
|
|
167
150
|
} else if (k === "state" && typeof v === "object" && v !== null) {
|
|
168
|
-
/** @type {Record<string,
|
|
151
|
+
/** @type {Record<string, unknown>} */
|
|
169
152
|
const state = {};
|
|
170
153
|
for (const [sk, sv] of Object.entries(v)) {
|
|
171
154
|
if (sv && typeof sv === "object" && sv.timing === "server") continue;
|
|
@@ -224,197 +207,56 @@ export function renderOnly(...names) {
|
|
|
224
207
|
}
|
|
225
208
|
}
|
|
226
209
|
|
|
227
|
-
// ───
|
|
228
|
-
// studio.js registers the real update implementation via setUpdateFn() during bootstrap.
|
|
229
|
-
// This allows extracted modules to import `update` from store.js without circular deps.
|
|
230
|
-
|
|
231
|
-
/** @type {Function} */
|
|
232
|
-
let _updateFn = () => {
|
|
233
|
-
throw new Error("update() called before setUpdateFn() — bootstrap not complete");
|
|
234
|
-
};
|
|
235
|
-
|
|
236
|
-
/** @type {Function} */
|
|
237
|
-
let _getStateFn = () => null;
|
|
210
|
+
// ─── Session dispatch ──────────────────────────────────────────────────────
|
|
238
211
|
|
|
239
212
|
/**
|
|
240
|
-
*
|
|
213
|
+
* Dispatch a session-only state update (selection, hover, ui). Writes directly to reactive tab.
|
|
241
214
|
*
|
|
242
|
-
* @param {
|
|
243
|
-
*/
|
|
244
|
-
export function setUpdateFn(fn) {
|
|
245
|
-
_updateFn = fn;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
/**
|
|
249
|
-
* Register the state getter. Called by studio.js at module load time.
|
|
250
|
-
*
|
|
251
|
-
* @param {Function} fn — returns current S
|
|
252
|
-
*/
|
|
253
|
-
export function setGetStateFn(fn) {
|
|
254
|
-
_getStateFn = fn;
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
/**
|
|
258
|
-
* Get the current state (live, not stale).
|
|
259
|
-
*
|
|
260
|
-
* @returns {any}
|
|
261
|
-
*/
|
|
262
|
-
export function getState() {
|
|
263
|
-
return _getStateFn();
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
/**
|
|
267
|
-
* Dispatch a state update + selective re-render.
|
|
268
|
-
*
|
|
269
|
-
* @param {any} newState
|
|
270
|
-
*/
|
|
271
|
-
export function update(newState) {
|
|
272
|
-
_updateFn(newState);
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
// ─── Session dispatch (late-bound) ──────────────────────────────────────────
|
|
276
|
-
// Lightweight dispatcher for session-only changes (selection, hover, ui).
|
|
277
|
-
// Does NOT trigger autosave middleware or push history.
|
|
278
|
-
|
|
279
|
-
/** @type {Function} */
|
|
280
|
-
let _updateSessionFn = () => {
|
|
281
|
-
throw new Error("updateSession() called before setUpdateSessionFn() — bootstrap not complete");
|
|
282
|
-
};
|
|
283
|
-
|
|
284
|
-
/** @type {Function} */
|
|
285
|
-
let _getDocFn = () => null;
|
|
286
|
-
|
|
287
|
-
/** @type {Function} */
|
|
288
|
-
let _getSessionFn = () => null;
|
|
289
|
-
|
|
290
|
-
/** @param {Function} fn */
|
|
291
|
-
export function setUpdateSessionFn(fn) {
|
|
292
|
-
_updateSessionFn = fn;
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
/** @param {Function} fn */
|
|
296
|
-
export function setGetDocFn(fn) {
|
|
297
|
-
_getDocFn = fn;
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
/** @param {Function} fn */
|
|
301
|
-
export function setGetSessionFn(fn) {
|
|
302
|
-
_getSessionFn = fn;
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
/** @returns {any} */
|
|
306
|
-
export function getDoc() {
|
|
307
|
-
return _getDocFn();
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
/** @returns {any} */
|
|
311
|
-
export function getSession() {
|
|
312
|
-
return _getSessionFn();
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
/**
|
|
316
|
-
* Dispatch a session-only state update (selection, hover, ui). Does not trigger autosave.
|
|
317
|
-
*
|
|
318
|
-
* @param {any} patch — partial session object, e.g. { ui: { zoom: 2 } }
|
|
215
|
+
* @param {object} patch — partial session object, e.g. { ui: { zoom: 2 } }
|
|
319
216
|
*/
|
|
320
217
|
export function updateSession(patch) {
|
|
321
|
-
|
|
218
|
+
const tab = activeTab.value;
|
|
219
|
+
if (tab) {
|
|
220
|
+
const p = /** @type {any} */ (patch);
|
|
221
|
+
if (p.selection !== undefined) tab.session.selection = p.selection;
|
|
222
|
+
if (p.hover !== undefined) tab.session.hover = p.hover;
|
|
223
|
+
if (p.clipboard !== undefined) tab.session.clipboard = p.clipboard;
|
|
224
|
+
if (p.ui) {
|
|
225
|
+
for (const [k, v] of Object.entries(p.ui)) {
|
|
226
|
+
/** @type {any} */ (tab.session.ui)[k] = v;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
if (p.canvas) {
|
|
230
|
+
for (const [k, v] of Object.entries(p.canvas)) {
|
|
231
|
+
/** @type {any} */ (tab.session.canvas)[k] = v;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
322
235
|
}
|
|
323
236
|
|
|
324
237
|
/**
|
|
325
|
-
* Update a single UI field
|
|
326
|
-
* history).
|
|
238
|
+
* Update a single UI field.
|
|
327
239
|
*
|
|
328
240
|
* @param {string} field
|
|
329
|
-
* @param {
|
|
241
|
+
* @param {unknown} value
|
|
330
242
|
*/
|
|
331
243
|
export function updateUi(field, value) {
|
|
332
|
-
|
|
244
|
+
const tab = activeTab.value;
|
|
245
|
+
if (tab) {
|
|
246
|
+
/** @type {any} */ (tab.session.ui)[field] = value;
|
|
247
|
+
}
|
|
333
248
|
}
|
|
334
249
|
|
|
335
250
|
/**
|
|
336
251
|
* Update the canvas async state (status, scope, error).
|
|
337
252
|
*
|
|
338
|
-
* @param {
|
|
253
|
+
* @param {object} patch
|
|
339
254
|
*/
|
|
340
255
|
export function updateCanvas(patch) {
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
// Panels subscribe to state changes and decide when to re-render, rather than
|
|
346
|
-
// being called unconditionally from _update/_updateSession.
|
|
347
|
-
|
|
348
|
-
/** @typedef {{ doc: boolean; selection: boolean; hover: boolean; ui: boolean; mode: boolean }} Change */
|
|
349
|
-
|
|
350
|
-
/** @type {Set<(change: Change) => void>} */
|
|
351
|
-
const _subscribers = new Set();
|
|
352
|
-
|
|
353
|
-
/**
|
|
354
|
-
* Subscribe to state changes. Returns an unsubscribe function.
|
|
355
|
-
*
|
|
356
|
-
* @param {(change: Change) => void} fn
|
|
357
|
-
* @returns {() => void}
|
|
358
|
-
*/
|
|
359
|
-
export function subscribe(fn) {
|
|
360
|
-
_subscribers.add(fn);
|
|
361
|
-
return () => _subscribers.delete(fn);
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
/**
|
|
365
|
-
* Notify all subscribers of a state change.
|
|
366
|
-
*
|
|
367
|
-
* @param {Change} change
|
|
368
|
-
*/
|
|
369
|
-
export function notify(change) {
|
|
370
|
-
for (const fn of _subscribers) {
|
|
371
|
-
try {
|
|
372
|
-
fn(change);
|
|
373
|
-
} catch (e) {
|
|
374
|
-
console.error("Subscriber failed:", e);
|
|
256
|
+
const tab = activeTab.value;
|
|
257
|
+
if (tab) {
|
|
258
|
+
for (const [k, v] of Object.entries(patch)) {
|
|
259
|
+
/** @type {any} */ (tab.session.canvas)[k] = v;
|
|
375
260
|
}
|
|
376
261
|
}
|
|
377
262
|
}
|
|
378
|
-
|
|
379
|
-
/** @type {Function[]} */
|
|
380
|
-
const _updateMiddleware = [];
|
|
381
|
-
|
|
382
|
-
/**
|
|
383
|
-
* Register middleware that runs after every update().
|
|
384
|
-
*
|
|
385
|
-
* @param {Function} fn — receives (state) after core update
|
|
386
|
-
*/
|
|
387
|
-
export function addUpdateMiddleware(fn) {
|
|
388
|
-
_updateMiddleware.push(fn);
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
/**
|
|
392
|
-
* Run all registered update middleware.
|
|
393
|
-
*
|
|
394
|
-
* @param {any} state
|
|
395
|
-
*/
|
|
396
|
-
export function runUpdateMiddleware(state) {
|
|
397
|
-
for (const mw of _updateMiddleware) mw(state);
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
/** @type {Function[]} */
|
|
401
|
-
const _postRenderHooks = [];
|
|
402
|
-
|
|
403
|
-
/**
|
|
404
|
-
* Register a hook that runs after renders in update().
|
|
405
|
-
*
|
|
406
|
-
* @param {Function} fn — receives (prevDoc, prevSel)
|
|
407
|
-
*/
|
|
408
|
-
export function addPostRenderHook(fn) {
|
|
409
|
-
_postRenderHooks.push(fn);
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
/**
|
|
413
|
-
* Run all registered post-render hooks.
|
|
414
|
-
*
|
|
415
|
-
* @param {any} prevDoc
|
|
416
|
-
* @param {any} prevSel
|
|
417
|
-
*/
|
|
418
|
-
export function runPostRenderHooks(prevDoc, prevSel) {
|
|
419
|
-
for (const hook of _postRenderHooks) hook(prevDoc, prevSel);
|
|
420
|
-
}
|