@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/tabs/transact.ts
CHANGED
|
@@ -1,15 +1,39 @@
|
|
|
1
1
|
/// <reference lib="dom" />
|
|
2
2
|
import { toRaw } from "../reactivity";
|
|
3
|
-
import {
|
|
3
|
+
import { jsonClone } from "../utils/studio-utils";
|
|
4
|
+
import { childIndex, getNodeAtPath, isAncestor, parentElementPath, pathsEqual } from "../state";
|
|
4
5
|
|
|
5
6
|
import type { Tab } from "../tabs/tab";
|
|
6
7
|
import type { JxPath } from "../state";
|
|
7
8
|
|
|
8
9
|
import type { JsonValue } from "../types";
|
|
9
|
-
import type { JxMutableNode } from "@jxsuite/schema/types";
|
|
10
|
+
import type { JxEventBinding, JxMutableNode, JxStateObject, JxStyle } from "@jxsuite/schema/types";
|
|
11
|
+
import { ensureNestedStyle, getNestedStyle } from "@jxsuite/schema/guards";
|
|
10
12
|
|
|
11
13
|
const HISTORY_LIMIT = 100;
|
|
12
14
|
|
|
15
|
+
/** Any value that can sit at a document node key (undefined/null/"" deletes it). */
|
|
16
|
+
export type JxNodeValue =
|
|
17
|
+
| JsonValue
|
|
18
|
+
| JxMutableNode
|
|
19
|
+
| (JxMutableNode | string)[]
|
|
20
|
+
| JxEventBinding
|
|
21
|
+
| undefined;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* The editable children array of a node, created when absent. Mapped-array children (`$prototype:
|
|
25
|
+
* "Array"`) cannot be index-mutated — fail loudly instead of corrupting.
|
|
26
|
+
*/
|
|
27
|
+
function childArray(node: JxMutableNode): (JxMutableNode | string)[] {
|
|
28
|
+
if (!node.children) {
|
|
29
|
+
node.children = [];
|
|
30
|
+
}
|
|
31
|
+
if (!Array.isArray(node.children)) {
|
|
32
|
+
throw new TypeError("Cannot insert into mapped-array children; edit the map template instead");
|
|
33
|
+
}
|
|
34
|
+
return node.children;
|
|
35
|
+
}
|
|
36
|
+
|
|
13
37
|
// ─── Transactional layer ─────────────────────────────────────────────────────
|
|
14
38
|
|
|
15
39
|
/**
|
|
@@ -25,7 +49,9 @@ export function transactDoc(
|
|
|
25
49
|
mutationFn: (tab: Tab) => void,
|
|
26
50
|
{ skipHistory = false }: { skipHistory?: boolean } = {},
|
|
27
51
|
) {
|
|
28
|
-
if (!tab)
|
|
52
|
+
if (!tab) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
29
55
|
mutationFn(tab);
|
|
30
56
|
|
|
31
57
|
// Replace the document root reference so effects tracking tab.doc.document re-trigger.
|
|
@@ -35,12 +61,14 @@ export function transactDoc(
|
|
|
35
61
|
|
|
36
62
|
if (!skipHistory) {
|
|
37
63
|
const snapshot = {
|
|
38
|
-
document:
|
|
64
|
+
document: jsonClone(raw),
|
|
39
65
|
selection: tab.session.selection ? [...tab.session.selection] : null,
|
|
40
66
|
};
|
|
41
67
|
const truncated = tab.history.snapshots.slice(0, tab.history.index + 1);
|
|
42
68
|
truncated.push(snapshot);
|
|
43
|
-
if (truncated.length > HISTORY_LIMIT)
|
|
69
|
+
if (truncated.length > HISTORY_LIMIT) {
|
|
70
|
+
truncated.shift();
|
|
71
|
+
}
|
|
44
72
|
tab.history.snapshots = truncated;
|
|
45
73
|
tab.history.index = truncated.length - 1;
|
|
46
74
|
}
|
|
@@ -67,8 +95,10 @@ export function transact(
|
|
|
67
95
|
|
|
68
96
|
/** @param {Tab} tab */
|
|
69
97
|
export function undo(tab: Tab) {
|
|
70
|
-
if (tab.history.index <= 0)
|
|
71
|
-
|
|
98
|
+
if (tab.history.index <= 0) {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
tab.history.index -= 1;
|
|
72
102
|
const snap = tab.history.snapshots[tab.history.index];
|
|
73
103
|
tab.doc.document = structuredClone(toRaw(snap.document));
|
|
74
104
|
tab.session.selection = snap.selection ? [...toRaw(snap.selection)] : null;
|
|
@@ -77,8 +107,10 @@ export function undo(tab: Tab) {
|
|
|
77
107
|
|
|
78
108
|
/** @param {Tab} tab */
|
|
79
109
|
export function redo(tab: Tab) {
|
|
80
|
-
if (tab.history.index >= tab.history.snapshots.length - 1)
|
|
81
|
-
|
|
110
|
+
if (tab.history.index >= tab.history.snapshots.length - 1) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
tab.history.index += 1;
|
|
82
114
|
const snap = tab.history.snapshots[tab.history.index];
|
|
83
115
|
tab.doc.document = structuredClone(toRaw(snap.document));
|
|
84
116
|
tab.session.selection = snap.selection ? [...toRaw(snap.selection)] : null;
|
|
@@ -100,9 +132,10 @@ export function mutateInsertNode(
|
|
|
100
132
|
nodeDef: JxMutableNode,
|
|
101
133
|
) {
|
|
102
134
|
const parent = getNodeAtPath(tab.doc.document, parentPath);
|
|
103
|
-
if (!parent)
|
|
104
|
-
|
|
105
|
-
|
|
135
|
+
if (!parent) {
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
childArray(parent).splice(index, 0, structuredClone(nodeDef));
|
|
106
139
|
}
|
|
107
140
|
|
|
108
141
|
/**
|
|
@@ -110,7 +143,9 @@ export function mutateInsertNode(
|
|
|
110
143
|
* @param {JxPath} path
|
|
111
144
|
*/
|
|
112
145
|
export function mutateRemoveNode(tab: Tab, path: JxPath) {
|
|
113
|
-
if (!path || path.length < 2)
|
|
146
|
+
if (!path || path.length < 2) {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
114
149
|
const elemPath = parentElementPath(path) as JxPath;
|
|
115
150
|
const idx = childIndex(path) as number;
|
|
116
151
|
(getNodeAtPath(tab.doc.document, elemPath).children as JxMutableNode[]).splice(idx, 1);
|
|
@@ -124,9 +159,13 @@ export function mutateRemoveNode(tab: Tab, path: JxPath) {
|
|
|
124
159
|
* @param {JxPath} path
|
|
125
160
|
*/
|
|
126
161
|
export function mutateDuplicateNode(tab: Tab, path: JxPath) {
|
|
127
|
-
if (!path || path.length < 2)
|
|
162
|
+
if (!path || path.length < 2) {
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
128
165
|
const node = getNodeAtPath(tab.doc.document, path);
|
|
129
|
-
if (!node)
|
|
166
|
+
if (!node) {
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
130
169
|
const elemPath = parentElementPath(path) as JxPath;
|
|
131
170
|
const idx = childIndex(path) as number;
|
|
132
171
|
const clone = structuredClone(toRaw(node));
|
|
@@ -139,15 +178,19 @@ export function mutateDuplicateNode(tab: Tab, path: JxPath) {
|
|
|
139
178
|
* @param {JxPath} path
|
|
140
179
|
* @param {string} wrapperTag
|
|
141
180
|
*/
|
|
142
|
-
export function mutateWrapNode(tab: Tab, path: JxPath, wrapperTag
|
|
143
|
-
if (!path || path.length < 2)
|
|
181
|
+
export function mutateWrapNode(tab: Tab, path: JxPath, wrapperTag = "div") {
|
|
182
|
+
if (!path || path.length < 2) {
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
144
185
|
const node = getNodeAtPath(tab.doc.document, path);
|
|
145
|
-
if (!node)
|
|
186
|
+
if (!node) {
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
146
189
|
const elemPath = parentElementPath(path) as JxPath;
|
|
147
190
|
const idx = childIndex(path) as number;
|
|
148
191
|
const wrapper = {
|
|
149
|
-
tagName: wrapperTag,
|
|
150
192
|
children: [structuredClone(toRaw(node))],
|
|
193
|
+
tagName: wrapperTag,
|
|
151
194
|
};
|
|
152
195
|
(getNodeAtPath(tab.doc.document, elemPath).children as JxMutableNode[]).splice(idx, 1, wrapper);
|
|
153
196
|
tab.session.selection = [...elemPath, "children", idx];
|
|
@@ -163,16 +206,23 @@ export function mutateMoveNode(tab: Tab, fromPath: JxPath, toParentPath: JxPath,
|
|
|
163
206
|
const doc = tab.doc.document;
|
|
164
207
|
const fromParentPath = parentElementPath(fromPath) as JxPath;
|
|
165
208
|
const fromIdx = childIndex(fromPath) as number;
|
|
166
|
-
if (!fromParentPath || typeof fromIdx !== "number")
|
|
209
|
+
if (!fromParentPath || typeof fromIdx !== "number") {
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
167
212
|
const fromParent = getNodeAtPath(doc, fromParentPath);
|
|
168
213
|
const toParent = getNodeAtPath(doc, toParentPath);
|
|
169
|
-
if (!fromParent || !Array.isArray(fromParent.children) || !toParent)
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
214
|
+
if (!fromParent || !Array.isArray(fromParent.children) || !toParent) {
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
const [node] = fromParent.children.splice(fromIdx, 1);
|
|
218
|
+
if (node === undefined) {
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
173
221
|
let adjustedIndex = toIndex;
|
|
174
|
-
if (fromParent === toParent && fromIdx < toIndex)
|
|
175
|
-
|
|
222
|
+
if (fromParent === toParent && fromIdx < toIndex) {
|
|
223
|
+
adjustedIndex -= 1;
|
|
224
|
+
}
|
|
225
|
+
childArray(toParent).splice(adjustedIndex, 0, node);
|
|
176
226
|
|
|
177
227
|
if (pathsEqual(tab.session.selection, fromPath)) {
|
|
178
228
|
let idx = toIndex;
|
|
@@ -191,12 +241,15 @@ export function mutateMoveNode(tab: Tab, fromPath: JxPath, toParentPath: JxPath,
|
|
|
191
241
|
* @param {Tab} tab
|
|
192
242
|
* @param {JxPath} path
|
|
193
243
|
* @param {string} key
|
|
194
|
-
* @param {
|
|
244
|
+
* @param {JxNodeValue} value
|
|
195
245
|
*/
|
|
196
|
-
export function mutateUpdateProperty(tab: Tab, path: JxPath, key: string, value
|
|
246
|
+
export function mutateUpdateProperty(tab: Tab, path: JxPath, key: string, value?: JxNodeValue) {
|
|
197
247
|
const node = getNodeAtPath(tab.doc.document, path);
|
|
198
|
-
if (value === undefined || value === null || value === "")
|
|
199
|
-
|
|
248
|
+
if (value === undefined || value === null || value === "") {
|
|
249
|
+
delete node[key];
|
|
250
|
+
} else {
|
|
251
|
+
node[key] = value;
|
|
252
|
+
}
|
|
200
253
|
}
|
|
201
254
|
|
|
202
255
|
/**
|
|
@@ -207,10 +260,17 @@ export function mutateUpdateProperty(tab: Tab, path: JxPath, key: string, value:
|
|
|
207
260
|
*/
|
|
208
261
|
export function mutateUpdateStyle(tab: Tab, path: JxPath, prop: string, value: string | undefined) {
|
|
209
262
|
const node = getNodeAtPath(tab.doc.document, path);
|
|
210
|
-
if (!node.style)
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
if (
|
|
263
|
+
if (!node.style) {
|
|
264
|
+
node.style = {};
|
|
265
|
+
}
|
|
266
|
+
if (value === undefined || value === "") {
|
|
267
|
+
delete node.style[prop];
|
|
268
|
+
} else {
|
|
269
|
+
node.style[prop] = value;
|
|
270
|
+
}
|
|
271
|
+
if (Object.keys(node.style).length === 0) {
|
|
272
|
+
delete node.style;
|
|
273
|
+
}
|
|
214
274
|
}
|
|
215
275
|
|
|
216
276
|
/**
|
|
@@ -223,13 +283,20 @@ export function mutateUpdateAttribute(
|
|
|
223
283
|
tab: Tab,
|
|
224
284
|
path: JxPath,
|
|
225
285
|
attr: string,
|
|
226
|
-
value
|
|
286
|
+
value?: string | undefined,
|
|
227
287
|
) {
|
|
228
288
|
const node = getNodeAtPath(tab.doc.document, path);
|
|
229
|
-
if (!node.attributes)
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
if (
|
|
289
|
+
if (!node.attributes) {
|
|
290
|
+
node.attributes = {};
|
|
291
|
+
}
|
|
292
|
+
if (value === undefined || value === "") {
|
|
293
|
+
delete node.attributes[attr];
|
|
294
|
+
} else {
|
|
295
|
+
node.attributes[attr] = value;
|
|
296
|
+
}
|
|
297
|
+
if (Object.keys(node.attributes).length === 0) {
|
|
298
|
+
delete node.attributes;
|
|
299
|
+
}
|
|
233
300
|
}
|
|
234
301
|
|
|
235
302
|
/**
|
|
@@ -246,18 +313,26 @@ export function mutateUpdateMediaStyle(
|
|
|
246
313
|
prop: string,
|
|
247
314
|
value: string | undefined,
|
|
248
315
|
) {
|
|
249
|
-
if (!mediaName)
|
|
316
|
+
if (!mediaName) {
|
|
317
|
+
return mutateUpdateStyle(tab, path, prop, value);
|
|
318
|
+
}
|
|
250
319
|
const node = getNodeAtPath(tab.doc.document, path);
|
|
251
|
-
if (!node.style)
|
|
320
|
+
if (!node.style) {
|
|
321
|
+
node.style = {};
|
|
322
|
+
}
|
|
252
323
|
const key = `@${mediaName}`;
|
|
253
|
-
|
|
324
|
+
const media = ensureNestedStyle(node.style, key);
|
|
254
325
|
if (value === undefined || value === "") {
|
|
255
|
-
delete
|
|
256
|
-
if (Object.keys(
|
|
326
|
+
delete media[prop];
|
|
327
|
+
if (Object.keys(media).length === 0) {
|
|
328
|
+
delete node.style[key];
|
|
329
|
+
}
|
|
257
330
|
} else {
|
|
258
|
-
|
|
331
|
+
media[prop] = value;
|
|
332
|
+
}
|
|
333
|
+
if (Object.keys(node.style).length === 0) {
|
|
334
|
+
delete node.style;
|
|
259
335
|
}
|
|
260
|
-
if (Object.keys(node.style).length === 0) delete node.style;
|
|
261
336
|
}
|
|
262
337
|
|
|
263
338
|
/**
|
|
@@ -275,15 +350,21 @@ export function mutateUpdateNestedStyle(
|
|
|
275
350
|
value: string | undefined,
|
|
276
351
|
) {
|
|
277
352
|
const node = getNodeAtPath(tab.doc.document, path);
|
|
278
|
-
if (!node.style)
|
|
279
|
-
|
|
353
|
+
if (!node.style) {
|
|
354
|
+
node.style = {};
|
|
355
|
+
}
|
|
356
|
+
const block = ensureNestedStyle(node.style, selector);
|
|
280
357
|
if (value === undefined || value === "") {
|
|
281
|
-
delete
|
|
282
|
-
if (Object.keys(
|
|
358
|
+
delete block[prop];
|
|
359
|
+
if (Object.keys(block).length === 0) {
|
|
360
|
+
delete node.style[selector];
|
|
361
|
+
}
|
|
283
362
|
} else {
|
|
284
|
-
|
|
363
|
+
block[prop] = value;
|
|
364
|
+
}
|
|
365
|
+
if (Object.keys(node.style).length === 0) {
|
|
366
|
+
delete node.style;
|
|
285
367
|
}
|
|
286
|
-
if (Object.keys(node.style).length === 0) delete node.style;
|
|
287
368
|
}
|
|
288
369
|
|
|
289
370
|
/**
|
|
@@ -303,18 +384,26 @@ export function mutateUpdateMediaNestedStyle(
|
|
|
303
384
|
value: string | undefined,
|
|
304
385
|
) {
|
|
305
386
|
const node = getNodeAtPath(tab.doc.document, path);
|
|
306
|
-
if (!node.style)
|
|
387
|
+
if (!node.style) {
|
|
388
|
+
node.style = {};
|
|
389
|
+
}
|
|
307
390
|
const key = `@${mediaName}`;
|
|
308
|
-
|
|
309
|
-
|
|
391
|
+
const media = ensureNestedStyle(node.style, key);
|
|
392
|
+
const block = ensureNestedStyle(media, selector);
|
|
310
393
|
if (value === undefined || value === "") {
|
|
311
|
-
delete
|
|
312
|
-
if (Object.keys(
|
|
313
|
-
|
|
394
|
+
delete block[prop];
|
|
395
|
+
if (Object.keys(block).length === 0) {
|
|
396
|
+
delete media[selector];
|
|
397
|
+
}
|
|
398
|
+
if (Object.keys(media).length === 0) {
|
|
399
|
+
delete node.style[key];
|
|
400
|
+
}
|
|
314
401
|
} else {
|
|
315
|
-
|
|
402
|
+
block[prop] = value;
|
|
403
|
+
}
|
|
404
|
+
if (Object.keys(node.style).length === 0) {
|
|
405
|
+
delete node.style;
|
|
316
406
|
}
|
|
317
|
-
if (Object.keys(node.style).length === 0) delete node.style;
|
|
318
407
|
}
|
|
319
408
|
|
|
320
409
|
/**
|
|
@@ -335,19 +424,20 @@ export function mutateUpdateNestedStylePath(
|
|
|
335
424
|
value: string | undefined,
|
|
336
425
|
) {
|
|
337
426
|
const node = getNodeAtPath(tab.doc.document, path);
|
|
338
|
-
if (!node.style)
|
|
427
|
+
if (!node.style) {
|
|
428
|
+
node.style = {};
|
|
429
|
+
}
|
|
339
430
|
let obj = node.style;
|
|
340
431
|
for (const seg of stylePath) {
|
|
341
|
-
|
|
342
|
-
obj = obj[seg];
|
|
432
|
+
obj = ensureNestedStyle(obj, seg);
|
|
343
433
|
}
|
|
344
434
|
if (value === undefined || value === "") {
|
|
345
435
|
delete obj[prop];
|
|
346
436
|
// Clean up empty parent objects
|
|
347
|
-
let cur = node.style;
|
|
348
|
-
for (let i = 0; i < stylePath.length; i++) {
|
|
349
|
-
const child = cur
|
|
350
|
-
if (child &&
|
|
437
|
+
let cur: JxStyle | undefined = node.style;
|
|
438
|
+
for (let i = 0; i < stylePath.length && cur; i++) {
|
|
439
|
+
const child = getNestedStyle(cur, stylePath[i]);
|
|
440
|
+
if (child && Object.keys(child).length === 0) {
|
|
351
441
|
delete cur[stylePath[i]];
|
|
352
442
|
break;
|
|
353
443
|
}
|
|
@@ -356,7 +446,9 @@ export function mutateUpdateNestedStylePath(
|
|
|
356
446
|
} else {
|
|
357
447
|
obj[prop] = value;
|
|
358
448
|
}
|
|
359
|
-
if (Object.keys(node.style).length === 0)
|
|
449
|
+
if (Object.keys(node.style).length === 0) {
|
|
450
|
+
delete node.style;
|
|
451
|
+
}
|
|
360
452
|
}
|
|
361
453
|
|
|
362
454
|
/**
|
|
@@ -378,30 +470,35 @@ export function mutateUpdateMediaNestedStylePath(
|
|
|
378
470
|
value: string | undefined,
|
|
379
471
|
) {
|
|
380
472
|
const node = getNodeAtPath(tab.doc.document, path);
|
|
381
|
-
if (!node.style)
|
|
473
|
+
if (!node.style) {
|
|
474
|
+
node.style = {};
|
|
475
|
+
}
|
|
382
476
|
const key = `@${mediaName}`;
|
|
383
|
-
|
|
384
|
-
let obj =
|
|
477
|
+
const media = ensureNestedStyle(node.style, key);
|
|
478
|
+
let obj = media;
|
|
385
479
|
for (const seg of stylePath) {
|
|
386
|
-
|
|
387
|
-
obj = obj[seg];
|
|
480
|
+
obj = ensureNestedStyle(obj, seg);
|
|
388
481
|
}
|
|
389
482
|
if (value === undefined || value === "") {
|
|
390
483
|
delete obj[prop];
|
|
391
|
-
let cur =
|
|
392
|
-
for (let i = 0; i < stylePath.length; i++) {
|
|
393
|
-
const child = cur
|
|
394
|
-
if (child &&
|
|
484
|
+
let cur: JxStyle | undefined = media;
|
|
485
|
+
for (let i = 0; i < stylePath.length && cur; i++) {
|
|
486
|
+
const child = getNestedStyle(cur, stylePath[i]);
|
|
487
|
+
if (child && Object.keys(child).length === 0) {
|
|
395
488
|
delete cur[stylePath[i]];
|
|
396
489
|
break;
|
|
397
490
|
}
|
|
398
491
|
cur = child;
|
|
399
492
|
}
|
|
400
|
-
if (Object.keys(
|
|
493
|
+
if (Object.keys(media).length === 0) {
|
|
494
|
+
delete node.style[key];
|
|
495
|
+
}
|
|
401
496
|
} else {
|
|
402
497
|
obj[prop] = value;
|
|
403
498
|
}
|
|
404
|
-
if (Object.keys(node.style).length === 0)
|
|
499
|
+
if (Object.keys(node.style).length === 0) {
|
|
500
|
+
delete node.style;
|
|
501
|
+
}
|
|
405
502
|
}
|
|
406
503
|
|
|
407
504
|
/**
|
|
@@ -409,11 +506,7 @@ export function mutateUpdateMediaNestedStylePath(
|
|
|
409
506
|
* @param {JxPath} path
|
|
410
507
|
* @param {Record<string, string | undefined> | undefined} style
|
|
411
508
|
*/
|
|
412
|
-
export function mutateReplaceStyle(
|
|
413
|
-
tab: Tab,
|
|
414
|
-
path: JxPath,
|
|
415
|
-
style: Record<string, string | undefined> | undefined,
|
|
416
|
-
) {
|
|
509
|
+
export function mutateReplaceStyle(tab: Tab, path: JxPath, style: JxStyle | undefined) {
|
|
417
510
|
const node = getNodeAtPath(tab.doc.document, path);
|
|
418
511
|
if (style && Object.keys(style).length > 0) {
|
|
419
512
|
node.style = style;
|
|
@@ -429,7 +522,9 @@ export function mutateReplaceStyle(
|
|
|
429
522
|
*/
|
|
430
523
|
export function mutateAddDef(tab: Tab, name: string, def: Record<string, JsonValue>) {
|
|
431
524
|
const doc = tab.doc.document;
|
|
432
|
-
if (!doc.state)
|
|
525
|
+
if (!doc.state) {
|
|
526
|
+
doc.state = {};
|
|
527
|
+
}
|
|
433
528
|
doc.state[name] = def;
|
|
434
529
|
}
|
|
435
530
|
|
|
@@ -441,7 +536,9 @@ export function mutateRemoveDef(tab: Tab, name: string) {
|
|
|
441
536
|
const doc = tab.doc.document;
|
|
442
537
|
if (doc.state) {
|
|
443
538
|
delete doc.state[name];
|
|
444
|
-
if (Object.keys(doc.state).length === 0)
|
|
539
|
+
if (Object.keys(doc.state).length === 0) {
|
|
540
|
+
delete doc.state;
|
|
541
|
+
}
|
|
445
542
|
}
|
|
446
543
|
}
|
|
447
544
|
|
|
@@ -450,18 +547,23 @@ export function mutateRemoveDef(tab: Tab, name: string) {
|
|
|
450
547
|
* @param {string} name
|
|
451
548
|
* @param {Record<string, JsonValue>} updates
|
|
452
549
|
*/
|
|
453
|
-
export function mutateUpdateDef(tab: Tab, name: string, updates: Record<string,
|
|
550
|
+
export function mutateUpdateDef(tab: Tab, name: string, updates: Record<string, unknown>) {
|
|
454
551
|
const doc = tab.doc.document;
|
|
455
|
-
if (!doc.state)
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
552
|
+
if (!doc.state) {
|
|
553
|
+
doc.state = {};
|
|
554
|
+
}
|
|
555
|
+
const existing = doc.state[name];
|
|
556
|
+
const entry: JxStateObject =
|
|
557
|
+
existing == null
|
|
558
|
+
? {}
|
|
559
|
+
: typeof existing !== "object" || Array.isArray(existing)
|
|
560
|
+
? { default: existing }
|
|
561
|
+
: (existing as JxStateObject);
|
|
562
|
+
doc.state[name] = entry;
|
|
563
|
+
Object.assign(entry, updates);
|
|
564
|
+
for (const k of Object.keys(entry)) {
|
|
565
|
+
if (entry[k] === undefined || entry[k] === null) {
|
|
566
|
+
delete entry[k];
|
|
465
567
|
}
|
|
466
568
|
}
|
|
467
569
|
}
|
|
@@ -473,7 +575,9 @@ export function mutateUpdateDef(tab: Tab, name: string, updates: Record<string,
|
|
|
473
575
|
*/
|
|
474
576
|
export function mutateRenameDef(tab: Tab, oldName: string, newName: string) {
|
|
475
577
|
const doc = tab.doc.document;
|
|
476
|
-
if (!doc.state || !doc.state[oldName])
|
|
578
|
+
if (!doc.state || !doc.state[oldName]) {
|
|
579
|
+
return;
|
|
580
|
+
}
|
|
477
581
|
doc.state[newName] = doc.state[oldName];
|
|
478
582
|
delete doc.state[oldName];
|
|
479
583
|
}
|
|
@@ -483,12 +587,16 @@ export function mutateRenameDef(tab: Tab, oldName: string, newName: string) {
|
|
|
483
587
|
* @param {string} name
|
|
484
588
|
* @param {string | undefined} query
|
|
485
589
|
*/
|
|
486
|
-
export function mutateUpdateMedia(tab: Tab, name: string, query
|
|
590
|
+
export function mutateUpdateMedia(tab: Tab, name: string, query?: string | undefined) {
|
|
487
591
|
const doc = tab.doc.document;
|
|
488
|
-
if (!doc.$media)
|
|
592
|
+
if (!doc.$media) {
|
|
593
|
+
doc.$media = {};
|
|
594
|
+
}
|
|
489
595
|
if (query === undefined || query === "") {
|
|
490
596
|
delete doc.$media[name];
|
|
491
|
-
if (Object.keys(doc.$media).length === 0)
|
|
597
|
+
if (Object.keys(doc.$media).length === 0) {
|
|
598
|
+
delete doc.$media;
|
|
599
|
+
}
|
|
492
600
|
} else {
|
|
493
601
|
doc.$media[name] = query;
|
|
494
602
|
}
|
|
@@ -502,10 +610,17 @@ export function mutateUpdateMedia(tab: Tab, name: string, query: string | undefi
|
|
|
502
610
|
*/
|
|
503
611
|
export function mutateUpdateProp(tab: Tab, path: JxPath, propName: string, value: JsonValue) {
|
|
504
612
|
const node = getNodeAtPath(tab.doc.document, path);
|
|
505
|
-
if (!node.$props)
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
if (
|
|
613
|
+
if (!node.$props) {
|
|
614
|
+
node.$props = {};
|
|
615
|
+
}
|
|
616
|
+
if (value === undefined || value === null || value === "") {
|
|
617
|
+
delete node.$props[propName];
|
|
618
|
+
} else {
|
|
619
|
+
node.$props[propName] = value;
|
|
620
|
+
}
|
|
621
|
+
if (Object.keys(node.$props).length === 0) {
|
|
622
|
+
delete node.$props;
|
|
623
|
+
}
|
|
509
624
|
}
|
|
510
625
|
|
|
511
626
|
/**
|
|
@@ -521,7 +636,9 @@ export function mutateAddSwitchCase(
|
|
|
521
636
|
caseDef?: JxMutableNode,
|
|
522
637
|
) {
|
|
523
638
|
const node = getNodeAtPath(tab.doc.document, path);
|
|
524
|
-
if (!node.cases)
|
|
639
|
+
if (!node.cases) {
|
|
640
|
+
node.cases = {};
|
|
641
|
+
}
|
|
525
642
|
node.cases[caseName] = caseDef || { tagName: "div", textContent: caseName };
|
|
526
643
|
}
|
|
527
644
|
|
|
@@ -532,7 +649,9 @@ export function mutateAddSwitchCase(
|
|
|
532
649
|
*/
|
|
533
650
|
export function mutateRemoveSwitchCase(tab: Tab, path: JxPath, caseName: string) {
|
|
534
651
|
const node = getNodeAtPath(tab.doc.document, path);
|
|
535
|
-
if (node.cases)
|
|
652
|
+
if (node.cases) {
|
|
653
|
+
delete node.cases[caseName];
|
|
654
|
+
}
|
|
536
655
|
}
|
|
537
656
|
|
|
538
657
|
/**
|
|
@@ -543,7 +662,9 @@ export function mutateRemoveSwitchCase(tab: Tab, path: JxPath, caseName: string)
|
|
|
543
662
|
*/
|
|
544
663
|
export function mutateRenameSwitchCase(tab: Tab, path: JxPath, oldName: string, newName: string) {
|
|
545
664
|
const node = getNodeAtPath(tab.doc.document, path);
|
|
546
|
-
if (!node.cases || !node.cases[oldName])
|
|
665
|
+
if (!node.cases || !node.cases[oldName]) {
|
|
666
|
+
return;
|
|
667
|
+
}
|
|
547
668
|
node.cases[newName] = node.cases[oldName];
|
|
548
669
|
delete node.cases[oldName];
|
|
549
670
|
}
|
|
@@ -557,7 +678,7 @@ export function mutateRenameSwitchCase(tab: Tab, path: JxPath, oldName: string,
|
|
|
557
678
|
* @param {string} field
|
|
558
679
|
* @param {JsonValue} value
|
|
559
680
|
*/
|
|
560
|
-
export function mutateUpdateFrontmatter(tab: Tab, field: string, value
|
|
681
|
+
export function mutateUpdateFrontmatter(tab: Tab, field: string, value?: JsonValue) {
|
|
561
682
|
if (value === undefined || value === null || value === "") {
|
|
562
683
|
delete tab.doc.content.frontmatter[field];
|
|
563
684
|
} else {
|