@jxsuite/studio 0.10.2 → 0.13.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/README.md +82 -0
- package/dist/studio.js +5114 -3424
- package/dist/studio.js.map +65 -49
- package/package.json +6 -3
- package/src/browse/browse.js +27 -3
- package/src/canvas/canvas-diff.js +184 -0
- package/src/canvas/canvas-helpers.js +10 -14
- package/src/canvas/canvas-live-render.js +136 -17
- package/src/canvas/canvas-render.js +154 -21
- package/src/canvas/canvas-utils.js +21 -23
- package/src/editor/component-inline-edit.js +54 -41
- package/src/editor/content-inline-edit.js +46 -47
- package/src/editor/context-menu.js +63 -39
- package/src/editor/convert-to-component.js +11 -14
- package/src/editor/insertion-helper.js +8 -10
- package/src/editor/shortcuts.js +69 -39
- package/src/files/components.js +15 -4
- package/src/files/files.js +72 -24
- package/src/panels/activity-bar.js +29 -7
- package/src/panels/block-action-bar.js +104 -80
- package/src/panels/canvas-dnd.js +132 -50
- package/src/panels/dnd.js +32 -28
- package/src/panels/editors.js +7 -14
- package/src/panels/elements-panel.js +9 -3
- package/src/panels/events-panel.js +44 -39
- package/src/panels/git-panel.js +45 -3
- package/src/panels/layers-panel.js +16 -11
- package/src/panels/left-panel.js +108 -43
- package/src/panels/overlays.js +97 -35
- package/src/panels/panel-events.js +18 -11
- package/src/panels/properties-panel.js +467 -98
- 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 +23 -8
- package/src/panels/style-inputs.js +4 -2
- package/src/panels/style-panel.js +128 -105
- package/src/panels/stylebook-panel.js +42 -17
- package/src/panels/tab-strip.js +124 -0
- package/src/panels/toolbar.js +39 -9
- package/src/reactivity.js +28 -0
- package/src/settings/content-types-editor.js +78 -8
- package/src/settings/defs-editor.js +56 -7
- package/src/settings/schema-field-ui.js +99 -11
- package/src/site-context.js +105 -0
- package/src/state.js +0 -456
- package/src/store.js +105 -124
- package/src/studio.js +112 -121
- package/src/tabs/tab.js +153 -0
- package/src/tabs/transact.js +406 -0
- package/src/ui/spectrum.js +2 -0
- package/src/view.js +3 -0
- package/src/workspace/workspace.js +89 -0
|
@@ -5,16 +5,14 @@
|
|
|
5
5
|
|
|
6
6
|
import {
|
|
7
7
|
getState,
|
|
8
|
-
update,
|
|
9
8
|
renderOnly,
|
|
10
|
-
selectNode,
|
|
11
|
-
insertNode,
|
|
12
|
-
updateProperty,
|
|
13
9
|
getNodeAtPath,
|
|
14
10
|
parentElementPath,
|
|
15
11
|
childIndex,
|
|
16
12
|
canvasPanels,
|
|
17
13
|
} from "../store.js";
|
|
14
|
+
import { activeTab } from "../workspace/workspace.js";
|
|
15
|
+
import { transactDoc, mutateInsertNode, mutateUpdateProperty } from "../tabs/transact.js";
|
|
18
16
|
import { view } from "../view.js";
|
|
19
17
|
import { startEditing, isEditableBlock } from "./inline-edit.js";
|
|
20
18
|
import { restoreTemplateExpressions } from "../utils/edit-display.js";
|
|
@@ -50,28 +48,21 @@ export function enterInlineEdit(el, path) {
|
|
|
50
48
|
const node = getNodeAtPath(S.document, commitPath);
|
|
51
49
|
if (children) {
|
|
52
50
|
if (node && JSON.stringify(node.children) === JSON.stringify(children)) return;
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
51
|
+
transactDoc(activeTab.value, (t) => {
|
|
52
|
+
mutateUpdateProperty(t, commitPath, "textContent", undefined);
|
|
53
|
+
mutateUpdateProperty(t, commitPath, "children", children);
|
|
54
|
+
});
|
|
56
55
|
} else if (textContent != null) {
|
|
57
56
|
if (node && node.textContent === textContent && !node.children) return;
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
57
|
+
transactDoc(activeTab.value, (t) => {
|
|
58
|
+
mutateUpdateProperty(t, commitPath, "children", undefined);
|
|
59
|
+
mutateUpdateProperty(t, commitPath, "textContent", textContent);
|
|
60
|
+
});
|
|
61
61
|
}
|
|
62
62
|
},
|
|
63
63
|
|
|
64
64
|
onSplit(/** @type {any} */ splitPath, /** @type {any} */ before, /** @type {any} */ after) {
|
|
65
65
|
const tag = "p";
|
|
66
|
-
let s = getState();
|
|
67
|
-
|
|
68
|
-
if (before.textContent != null) {
|
|
69
|
-
s = updateProperty(s, splitPath, "children", undefined);
|
|
70
|
-
s = updateProperty(s, splitPath, "textContent", before.textContent);
|
|
71
|
-
} else if (before.children) {
|
|
72
|
-
s = updateProperty(s, splitPath, "textContent", undefined);
|
|
73
|
-
s = updateProperty(s, splitPath, "children", before.children);
|
|
74
|
-
}
|
|
75
66
|
|
|
76
67
|
// Insert new element after with "after" content
|
|
77
68
|
const parentPath = /** @type {any} */ (parentElementPath(splitPath));
|
|
@@ -86,10 +77,19 @@ export function enterInlineEdit(el, path) {
|
|
|
86
77
|
newNode.textContent = "";
|
|
87
78
|
}
|
|
88
79
|
|
|
89
|
-
s = insertNode(s, parentPath, idx + 1, newNode);
|
|
90
80
|
const newPath = [...parentPath, "children", idx + 1];
|
|
91
|
-
|
|
92
|
-
|
|
81
|
+
|
|
82
|
+
transactDoc(activeTab.value, (t) => {
|
|
83
|
+
if (before.textContent != null) {
|
|
84
|
+
mutateUpdateProperty(t, splitPath, "children", undefined);
|
|
85
|
+
mutateUpdateProperty(t, splitPath, "textContent", before.textContent);
|
|
86
|
+
} else if (before.children) {
|
|
87
|
+
mutateUpdateProperty(t, splitPath, "textContent", undefined);
|
|
88
|
+
mutateUpdateProperty(t, splitPath, "children", before.children);
|
|
89
|
+
}
|
|
90
|
+
mutateInsertNode(t, parentPath, idx + 1, newNode);
|
|
91
|
+
t.session.selection = newPath;
|
|
92
|
+
});
|
|
93
93
|
|
|
94
94
|
// Re-enter editing on the new element after render
|
|
95
95
|
requestAnimationFrame(() => {
|
|
@@ -125,17 +125,17 @@ export function enterInlineEdit(el, path) {
|
|
|
125
125
|
|
|
126
126
|
// If the element is empty, swap its tagName instead of inserting after
|
|
127
127
|
if (isEmpty) {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
128
|
+
transactDoc(activeTab.value, (t) => {
|
|
129
|
+
mutateUpdateProperty(t, afterPath, "tagName", cmd.tag);
|
|
130
|
+
mutateUpdateProperty(t, afterPath, "children", undefined);
|
|
131
|
+
const def = defaultDef(cmd.tag);
|
|
132
|
+
if (def.textContent && def.textContent !== "Paragraph text") {
|
|
133
|
+
mutateUpdateProperty(t, afterPath, "textContent", def.textContent);
|
|
134
|
+
} else {
|
|
135
|
+
mutateUpdateProperty(t, afterPath, "textContent", undefined);
|
|
136
|
+
}
|
|
137
|
+
t.session.selection = afterPath;
|
|
138
|
+
});
|
|
139
139
|
|
|
140
140
|
requestAnimationFrame(() => {
|
|
141
141
|
const activePanel = getActivePanel();
|
|
@@ -152,23 +152,22 @@ export function enterInlineEdit(el, path) {
|
|
|
152
152
|
const elementDef = defaultDef(cmd.tag);
|
|
153
153
|
const parentPath = /** @type {any} */ (parentElementPath(afterPath));
|
|
154
154
|
const idx = /** @type {number} */ (childIndex(afterPath));
|
|
155
|
+
const newPath = [...parentPath, "children", idx + 1];
|
|
155
156
|
|
|
156
157
|
// Apply pending commit from inline edit first (batched to avoid double render)
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
158
|
+
transactDoc(activeTab.value, (t) => {
|
|
159
|
+
if (commitData) {
|
|
160
|
+
if (commitData.children) {
|
|
161
|
+
mutateUpdateProperty(t, afterPath, "textContent", undefined);
|
|
162
|
+
mutateUpdateProperty(t, afterPath, "children", commitData.children);
|
|
163
|
+
} else if (commitData.textContent != null) {
|
|
164
|
+
mutateUpdateProperty(t, afterPath, "children", undefined);
|
|
165
|
+
mutateUpdateProperty(t, afterPath, "textContent", commitData.textContent);
|
|
166
|
+
}
|
|
165
167
|
}
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
const newPath = [...parentPath, "children", idx + 1];
|
|
170
|
-
s = selectNode(s, newPath);
|
|
171
|
-
update(s);
|
|
168
|
+
mutateInsertNode(t, parentPath, idx + 1, structuredClone(elementDef));
|
|
169
|
+
t.session.selection = newPath;
|
|
170
|
+
});
|
|
172
171
|
|
|
173
172
|
// If the inserted element is editable, enter editing
|
|
174
173
|
requestAnimationFrame(() => {
|
|
@@ -1,26 +1,32 @@
|
|
|
1
1
|
// ─── Clipboard & Context Menu ─────────────────────────────────────────────────
|
|
2
2
|
import { html, render as litRender } from "lit-html";
|
|
3
|
+
import { getNodeAtPath, parentElementPath, childIndex } from "../store.js";
|
|
4
|
+
import { activeTab } from "../workspace/workspace.js";
|
|
3
5
|
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
getNodeAtPath,
|
|
11
|
-
parentElementPath,
|
|
12
|
-
childIndex,
|
|
13
|
-
} from "../store.js";
|
|
6
|
+
transactDoc,
|
|
7
|
+
mutateInsertNode,
|
|
8
|
+
mutateRemoveNode,
|
|
9
|
+
mutateDuplicateNode,
|
|
10
|
+
mutateWrapNode,
|
|
11
|
+
} from "../tabs/transact.js";
|
|
14
12
|
import { statusMessage } from "../panels/statusbar.js";
|
|
15
13
|
import { convertToComponent } from "./convert-to-component.js";
|
|
16
14
|
import { componentRegistry } from "../files/components.js";
|
|
17
15
|
|
|
18
|
-
/**
|
|
16
|
+
/**
|
|
17
|
+
* @typedef {import("../state.js").StudioState} StudioState
|
|
18
|
+
*
|
|
19
|
+
* @typedef {import("../state.js").JxPath} JxPath
|
|
20
|
+
*
|
|
21
|
+
* @typedef {import("../state.js").JxNode} JxNode
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
/** @type {JxNode | null} */
|
|
19
25
|
let clipboard = null;
|
|
20
26
|
|
|
21
27
|
// ─── Clipboard ────────────────────────────────────────────────────────────────
|
|
22
28
|
|
|
23
|
-
/** @param {
|
|
29
|
+
/** @param {StudioState} S */
|
|
24
30
|
export function copyNode(S) {
|
|
25
31
|
if (!S.selection) return;
|
|
26
32
|
const node = getNodeAtPath(S.document, S.selection);
|
|
@@ -29,32 +35,32 @@ export function copyNode(S) {
|
|
|
29
35
|
statusMessage("Copied");
|
|
30
36
|
}
|
|
31
37
|
|
|
32
|
-
/** @param {
|
|
38
|
+
/** @param {StudioState} S */
|
|
33
39
|
export function cutNode(S) {
|
|
34
40
|
if (!S.selection || S.selection.length < 2) return;
|
|
35
|
-
const
|
|
41
|
+
const sel = S.selection;
|
|
42
|
+
const node = getNodeAtPath(S.document, sel);
|
|
36
43
|
if (!node) return;
|
|
37
44
|
clipboard = structuredClone(node);
|
|
38
|
-
|
|
45
|
+
transactDoc(activeTab.value, (t) => mutateRemoveNode(t, sel));
|
|
39
46
|
statusMessage("Cut");
|
|
40
47
|
}
|
|
41
48
|
|
|
42
|
-
/** @param {
|
|
49
|
+
/** @param {StudioState} S */
|
|
43
50
|
export function pasteNode(S) {
|
|
44
51
|
if (!clipboard) return;
|
|
52
|
+
const clip = clipboard;
|
|
45
53
|
const pPath = S.selection || [];
|
|
46
54
|
const parent = getNodeAtPath(S.document, pPath);
|
|
47
55
|
if (!parent) return;
|
|
48
56
|
|
|
49
57
|
if (S.selection && S.selection.length >= 2) {
|
|
50
|
-
|
|
51
|
-
const pp = /** @type {any} */ (parentElementPath(S.selection));
|
|
58
|
+
const pp = /** @type {JxPath} */ (parentElementPath(S.selection));
|
|
52
59
|
const idx = /** @type {number} */ (childIndex(S.selection));
|
|
53
|
-
|
|
60
|
+
transactDoc(activeTab.value, (t) => mutateInsertNode(t, pp, idx + 1, structuredClone(clip)));
|
|
54
61
|
} else {
|
|
55
|
-
// Paste as last child of root/selected
|
|
56
62
|
const idx = parent.children ? parent.children.length : 0;
|
|
57
|
-
|
|
63
|
+
transactDoc(activeTab.value, (t) => mutateInsertNode(t, pPath, idx, structuredClone(clip)));
|
|
58
64
|
}
|
|
59
65
|
statusMessage("Pasted");
|
|
60
66
|
}
|
|
@@ -77,9 +83,9 @@ export function dismissContextMenu() {
|
|
|
77
83
|
}
|
|
78
84
|
|
|
79
85
|
/**
|
|
80
|
-
* @param {
|
|
81
|
-
* @param {
|
|
82
|
-
* @param {
|
|
86
|
+
* @param {MouseEvent} e
|
|
87
|
+
* @param {JxPath} path
|
|
88
|
+
* @param {StudioState} S
|
|
83
89
|
* @param {{ onEditComponent?: (path: string) => void }} [opts]
|
|
84
90
|
*/
|
|
85
91
|
export function showContextMenu(e, path, S, opts = {}) {
|
|
@@ -90,7 +96,7 @@ export function showContextMenu(e, path, S, opts = {}) {
|
|
|
90
96
|
if (!node) return;
|
|
91
97
|
|
|
92
98
|
// Select the node
|
|
93
|
-
|
|
99
|
+
activeTab.value.session.selection = path;
|
|
94
100
|
|
|
95
101
|
/** @type {{ label: string; action?: () => void; danger?: boolean }[]} */
|
|
96
102
|
const items = [];
|
|
@@ -98,64 +104,82 @@ export function showContextMenu(e, path, S, opts = {}) {
|
|
|
98
104
|
items.push({ label: "Copy", action: () => copyNode(S) });
|
|
99
105
|
if (path.length >= 2) {
|
|
100
106
|
items.push({ label: "Cut", action: () => cutNode(S) });
|
|
101
|
-
items.push({
|
|
107
|
+
items.push({
|
|
108
|
+
label: "Duplicate",
|
|
109
|
+
action: () => transactDoc(activeTab.value, (t) => mutateDuplicateNode(t, path)),
|
|
110
|
+
});
|
|
102
111
|
items.push({ label: "—" }); // separator
|
|
103
112
|
items.push({
|
|
104
113
|
label: "Insert before",
|
|
105
114
|
action: () => {
|
|
106
|
-
const pp = /** @type {
|
|
115
|
+
const pp = /** @type {JxPath} */ (parentElementPath(path));
|
|
107
116
|
const idx = /** @type {number} */ (childIndex(path));
|
|
108
|
-
|
|
117
|
+
transactDoc(activeTab.value, (t) =>
|
|
118
|
+
mutateInsertNode(t, pp, idx, { tagName: "p", children: [] }),
|
|
119
|
+
);
|
|
109
120
|
},
|
|
110
121
|
});
|
|
111
122
|
items.push({
|
|
112
123
|
label: "Insert after",
|
|
113
124
|
action: () => {
|
|
114
|
-
const pp = /** @type {
|
|
125
|
+
const pp = /** @type {JxPath} */ (parentElementPath(path));
|
|
115
126
|
const idx = /** @type {number} */ (childIndex(path));
|
|
116
|
-
|
|
127
|
+
transactDoc(activeTab.value, (t) =>
|
|
128
|
+
mutateInsertNode(t, pp, idx + 1, { tagName: "p", children: [] }),
|
|
129
|
+
);
|
|
117
130
|
},
|
|
118
131
|
});
|
|
119
132
|
items.push({
|
|
120
133
|
label: "Wrap in Div",
|
|
121
|
-
action: () =>
|
|
134
|
+
action: () => transactDoc(activeTab.value, (t) => mutateWrapNode(t, path)),
|
|
122
135
|
});
|
|
123
136
|
if (node.tagName) {
|
|
124
137
|
const isComponent =
|
|
125
138
|
node.tagName.includes("-") &&
|
|
126
|
-
componentRegistry.some(
|
|
139
|
+
componentRegistry.some(
|
|
140
|
+
(/** @type {{ tagName: string }} */ c) => c.tagName === node.tagName,
|
|
141
|
+
);
|
|
127
142
|
if (isComponent && opts.onEditComponent) {
|
|
128
|
-
const comp = componentRegistry.find(
|
|
143
|
+
const comp = componentRegistry.find(
|
|
144
|
+
(/** @type {{ tagName: string; path: string }} */ c) => c.tagName === node.tagName,
|
|
145
|
+
);
|
|
129
146
|
items.push({
|
|
130
147
|
label: "Edit Component",
|
|
131
|
-
action: () => opts.onEditComponent?.(comp
|
|
148
|
+
action: () => opts.onEditComponent?.(/** @type {string} */ (comp?.path)),
|
|
132
149
|
});
|
|
133
150
|
} else if (!isComponent) {
|
|
134
151
|
items.push({
|
|
135
152
|
label: "Convert to Component",
|
|
136
|
-
action: () => convertToComponent(
|
|
153
|
+
action: () => convertToComponent(),
|
|
137
154
|
});
|
|
138
155
|
}
|
|
139
156
|
}
|
|
140
157
|
items.push({ label: "—" }); // separator
|
|
141
|
-
items.push({
|
|
158
|
+
items.push({
|
|
159
|
+
label: "Delete",
|
|
160
|
+
action: () => transactDoc(activeTab.value, (t) => mutateRemoveNode(t, path)),
|
|
161
|
+
danger: true,
|
|
162
|
+
});
|
|
142
163
|
}
|
|
143
164
|
if (clipboard) {
|
|
165
|
+
const clip = clipboard;
|
|
144
166
|
items.push({ label: "—" });
|
|
145
167
|
items.push({
|
|
146
168
|
label: "Paste inside",
|
|
147
169
|
action: () => {
|
|
148
170
|
const idx = node.children ? node.children.length : 0;
|
|
149
|
-
|
|
171
|
+
transactDoc(activeTab.value, (t) => mutateInsertNode(t, path, idx, structuredClone(clip)));
|
|
150
172
|
},
|
|
151
173
|
});
|
|
152
174
|
if (path.length >= 2) {
|
|
153
175
|
items.push({
|
|
154
176
|
label: "Paste after",
|
|
155
177
|
action: () => {
|
|
156
|
-
const pp = /** @type {
|
|
178
|
+
const pp = /** @type {JxPath} */ (parentElementPath(path));
|
|
157
179
|
const idx = /** @type {number} */ (childIndex(path));
|
|
158
|
-
|
|
180
|
+
transactDoc(activeTab.value, (t) =>
|
|
181
|
+
mutateInsertNode(t, pp, idx + 1, structuredClone(clip)),
|
|
182
|
+
);
|
|
159
183
|
},
|
|
160
184
|
});
|
|
161
185
|
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
// ─── Convert to Component ─────────────────────────────────────────────────────
|
|
2
2
|
import { html, render as litRender } from "lit-html";
|
|
3
|
-
import {
|
|
3
|
+
import { getNodeAtPath, parentElementPath, childIndex } from "../store.js";
|
|
4
|
+
import { activeTab } from "../workspace/workspace.js";
|
|
5
|
+
import { transact } from "../tabs/transact.js";
|
|
4
6
|
import {
|
|
5
7
|
computeRelativePath,
|
|
6
8
|
loadComponentRegistry,
|
|
@@ -11,15 +13,12 @@ import { statusMessage } from "../panels/statusbar.js";
|
|
|
11
13
|
|
|
12
14
|
const VALID_NAME = /^[a-z][a-z0-9]*(-[a-z0-9]+)+$/;
|
|
13
15
|
|
|
14
|
-
/**
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
*/
|
|
19
|
-
export async function convertToComponent(S) {
|
|
20
|
-
if (!S.selection || S.selection.length < 2) return;
|
|
16
|
+
/** Convert the currently selected element into a reusable component. */
|
|
17
|
+
export async function convertToComponent() {
|
|
18
|
+
const tab = activeTab.value;
|
|
19
|
+
if (!tab?.session.selection || tab.session.selection.length < 2) return;
|
|
21
20
|
|
|
22
|
-
const node = getNodeAtPath(
|
|
21
|
+
const node = getNodeAtPath(tab.doc.document, tab.session.selection);
|
|
23
22
|
if (!node || !node.tagName) return;
|
|
24
23
|
|
|
25
24
|
const defaultName = deriveDefaultName(node);
|
|
@@ -32,11 +31,11 @@ export async function convertToComponent(S) {
|
|
|
32
31
|
|
|
33
32
|
// Compute paths
|
|
34
33
|
const componentFile = "components/" + name + ".json";
|
|
35
|
-
const refPath = computeRelativePath(
|
|
34
|
+
const refPath = computeRelativePath(tab.documentPath, componentFile);
|
|
36
35
|
|
|
37
36
|
// Single atomic mutation: replace node + add $elements ref
|
|
38
|
-
const selectionPath =
|
|
39
|
-
|
|
37
|
+
const selectionPath = tab.session.selection;
|
|
38
|
+
transact(tab, (doc) => {
|
|
40
39
|
// Navigate to parent's children array and replace the node
|
|
41
40
|
const pp = parentElementPath(selectionPath) ?? [];
|
|
42
41
|
const idx = childIndex(selectionPath);
|
|
@@ -54,8 +53,6 @@ export async function convertToComponent(S) {
|
|
|
54
53
|
}
|
|
55
54
|
});
|
|
56
55
|
|
|
57
|
-
update(newState);
|
|
58
|
-
|
|
59
56
|
// Write component file and refresh registry
|
|
60
57
|
try {
|
|
61
58
|
const platform = getPlatform();
|
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { showSlashMenu } from "./slash-menu.js";
|
|
9
|
+
import { activeTab } from "../workspace/workspace.js";
|
|
10
|
+
import { transactDoc, mutateInsertNode } from "../tabs/transact.js";
|
|
9
11
|
|
|
10
12
|
// ─── Types ────────────────────────────────────────────────────────────────────
|
|
11
13
|
|
|
@@ -32,15 +34,11 @@ import { showSlashMenu } from "./slash-menu.js";
|
|
|
32
34
|
/**
|
|
33
35
|
* @typedef {Object} InsertionHelperContext
|
|
34
36
|
* @property {() => any} getState - Returns the current editor state.
|
|
35
|
-
* @property {(state: any) => void} update - Commits a new state.
|
|
36
37
|
* @property {() => string} getCanvasMode - Returns the active canvas mode.
|
|
37
38
|
* @property {(fn: Function) => any} withPanelPointerEvents - Executes fn with pointer-events
|
|
38
39
|
* temporarily enabled on the canvas.
|
|
39
40
|
* @property {() => number} effectiveZoom - Returns the current zoom scale factor.
|
|
40
41
|
* @property {(tag: string) => Object} defaultDef - Creates a default element definition for a tag.
|
|
41
|
-
* @property {(s: any, path: any[], idx: number, def: Object) => any} insertNode - Inserts a node
|
|
42
|
-
* into the document tree.
|
|
43
|
-
* @property {(s: any, path: any[]) => any} selectNode - Sets the selection to the given path.
|
|
44
42
|
* @property {(path: any[]) => any[] | null} parentElementPath - Returns the parent element path, or
|
|
45
43
|
* null for root.
|
|
46
44
|
* @property {(path: any[]) => string | number} childIndex - Returns the child index within the
|
|
@@ -284,18 +282,18 @@ function onHelperClick(/** @type {MouseEvent} */ e) {
|
|
|
284
282
|
function onSlashSelect(cmd, point) {
|
|
285
283
|
if (!_ctx) return;
|
|
286
284
|
|
|
287
|
-
const {
|
|
288
|
-
const S = getState();
|
|
285
|
+
const { defaultDef } = _ctx;
|
|
289
286
|
const { parentPath, idx, edge } = point;
|
|
290
287
|
|
|
291
288
|
const newDef = defaultDef(cmd.tag);
|
|
292
289
|
const insertPath = edge === "center" ? point.path : parentPath;
|
|
293
290
|
const insertIdx = edge === "center" ? 0 : idx;
|
|
294
|
-
|
|
295
|
-
let s = insertNode(S, insertPath, insertIdx, newDef);
|
|
296
291
|
const newPath = [...insertPath, "children", insertIdx];
|
|
297
|
-
|
|
298
|
-
|
|
292
|
+
|
|
293
|
+
transactDoc(activeTab.value, (t) => {
|
|
294
|
+
mutateInsertNode(t, insertPath, insertIdx, newDef);
|
|
295
|
+
t.session.selection = newPath;
|
|
296
|
+
});
|
|
299
297
|
|
|
300
298
|
hide();
|
|
301
299
|
}
|