@jxsuite/studio 0.11.0 → 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/dist/studio.js +4248 -3412
- package/dist/studio.js.map +49 -43
- 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 +3 -2
- package/src/canvas/canvas-render.js +152 -20
- 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/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 +91 -41
- package/src/panels/panel-events.js +9 -12
- package/src/panels/properties-panel.js +179 -104
- 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 +11 -15
- 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 +56 -7
- package/src/settings/defs-editor.js +56 -7
- package/src/settings/schema-field-ui.js +60 -25
- package/src/state.js +0 -456
- package/src/store.js +97 -124
- package/src/studio.js +112 -121
- package/src/tabs/tab.js +153 -0
- package/src/tabs/transact.js +406 -0
- package/src/workspace/workspace.js +89 -0
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import { getNodeAtPath
|
|
1
|
+
import { getNodeAtPath } from "../store.js";
|
|
2
2
|
import { html, nothing } from "lit-html";
|
|
3
3
|
import { live } from "lit-html/directives/live.js";
|
|
4
|
+
import { activeTab } from "../workspace/workspace.js";
|
|
5
|
+
import { transactDoc, mutateUpdateProperty } from "../tabs/transact.js";
|
|
4
6
|
|
|
5
7
|
export const EVENT_NAMES = [
|
|
6
8
|
"onclick",
|
|
@@ -15,17 +17,17 @@ export const EVENT_NAMES = [
|
|
|
15
17
|
"onmouseleave",
|
|
16
18
|
];
|
|
17
19
|
|
|
18
|
-
/**
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
const
|
|
24
|
-
if (!
|
|
25
|
-
const node = getNodeAtPath(
|
|
20
|
+
/** @param {{ isCustomElementDoc: () => boolean }} helpers */
|
|
21
|
+
export function eventsSidebarTemplate(helpers) {
|
|
22
|
+
const { isCustomElementDoc } = helpers;
|
|
23
|
+
const tab = activeTab.value;
|
|
24
|
+
const selection = tab?.session.selection;
|
|
25
|
+
const document = tab?.doc.document;
|
|
26
|
+
if (!selection) return html`<div class="empty-state">Select an element to edit events</div>`;
|
|
27
|
+
const node = getNodeAtPath(document, selection);
|
|
26
28
|
if (!node) return html`<div class="empty-state">Node not found</div>`;
|
|
27
29
|
|
|
28
|
-
const defs =
|
|
30
|
+
const defs = document.state || {};
|
|
29
31
|
const functionDefs = Object.entries(defs).filter(
|
|
30
32
|
([, d]) => d.$prototype === "Function" || d.$handler,
|
|
31
33
|
);
|
|
@@ -87,9 +89,10 @@ export function eventsSidebarTemplate(S, helpers) {
|
|
|
87
89
|
@change=${(/** @type {any} */ e) => {
|
|
88
90
|
const newKey = e.target.value;
|
|
89
91
|
if (newKey && newKey !== evKey) {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
92
|
+
transactDoc(activeTab.value, (t) => {
|
|
93
|
+
mutateUpdateProperty(t, selection, evKey, undefined);
|
|
94
|
+
mutateUpdateProperty(t, selection, newKey, node[evKey]);
|
|
95
|
+
});
|
|
93
96
|
}
|
|
94
97
|
}}
|
|
95
98
|
>
|
|
@@ -103,8 +106,8 @@ export function eventsSidebarTemplate(S, helpers) {
|
|
|
103
106
|
.value=${live(isInline ? "inline" : "ref")}
|
|
104
107
|
@change=${(/** @type {any} */ e) => {
|
|
105
108
|
if (e.target.value === "inline") {
|
|
106
|
-
|
|
107
|
-
|
|
109
|
+
transactDoc(activeTab.value, (t) =>
|
|
110
|
+
mutateUpdateProperty(t, selection, evKey, {
|
|
108
111
|
$prototype: "Function",
|
|
109
112
|
body: "",
|
|
110
113
|
parameters: [],
|
|
@@ -112,10 +115,10 @@ export function eventsSidebarTemplate(S, helpers) {
|
|
|
112
115
|
);
|
|
113
116
|
} else {
|
|
114
117
|
const firstFn = functionDefs[0];
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
118
|
+
transactDoc(activeTab.value, (t) =>
|
|
119
|
+
mutateUpdateProperty(
|
|
120
|
+
t,
|
|
121
|
+
selection,
|
|
119
122
|
evKey,
|
|
120
123
|
firstFn ? { $ref: `#/state/${firstFn[0]}` } : { $ref: "" },
|
|
121
124
|
),
|
|
@@ -129,7 +132,10 @@ export function eventsSidebarTemplate(S, helpers) {
|
|
|
129
132
|
<sp-action-button
|
|
130
133
|
size="xs"
|
|
131
134
|
quiet
|
|
132
|
-
@click=${() =>
|
|
135
|
+
@click=${() =>
|
|
136
|
+
transactDoc(activeTab.value, (t) =>
|
|
137
|
+
mutateUpdateProperty(t, selection, evKey, undefined),
|
|
138
|
+
)}
|
|
133
139
|
>
|
|
134
140
|
<sp-icon-delete slot="icon"></sp-icon-delete>
|
|
135
141
|
</sp-action-button>
|
|
@@ -144,8 +150,8 @@ export function eventsSidebarTemplate(S, helpers) {
|
|
|
144
150
|
placeholder="// handler body"
|
|
145
151
|
.value=${live(evVal.body || "")}
|
|
146
152
|
@input=${(/** @type {any} */ e) => {
|
|
147
|
-
|
|
148
|
-
|
|
153
|
+
transactDoc(activeTab.value, (t) =>
|
|
154
|
+
mutateUpdateProperty(t, selection, evKey, {
|
|
149
155
|
$prototype: "Function",
|
|
150
156
|
body: e.target.value,
|
|
151
157
|
parameters: evVal.parameters || [],
|
|
@@ -159,18 +165,11 @@ export function eventsSidebarTemplate(S, helpers) {
|
|
|
159
165
|
quiet
|
|
160
166
|
title="Open in editor"
|
|
161
167
|
@click=${() => {
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
editingFunction: {
|
|
167
|
-
type: "event",
|
|
168
|
-
path: S.selection,
|
|
169
|
-
eventKey: evKey,
|
|
170
|
-
},
|
|
171
|
-
},
|
|
168
|
+
tab.session.ui.editingFunction = {
|
|
169
|
+
type: "event",
|
|
170
|
+
path: selection,
|
|
171
|
+
eventKey: evKey,
|
|
172
172
|
};
|
|
173
|
-
renderCanvas();
|
|
174
173
|
}}
|
|
175
174
|
>
|
|
176
175
|
<sp-icon-code slot="icon"></sp-icon-code>
|
|
@@ -184,9 +183,13 @@ export function eventsSidebarTemplate(S, helpers) {
|
|
|
184
183
|
.value=${live(evVal.$ref || "__none__")}
|
|
185
184
|
@change=${(/** @type {any} */ e) => {
|
|
186
185
|
if (e.target.value && e.target.value !== "__none__") {
|
|
187
|
-
|
|
186
|
+
transactDoc(activeTab.value, (t) =>
|
|
187
|
+
mutateUpdateProperty(t, selection, evKey, { $ref: e.target.value }),
|
|
188
|
+
);
|
|
188
189
|
} else {
|
|
189
|
-
|
|
190
|
+
transactDoc(activeTab.value, (t) =>
|
|
191
|
+
mutateUpdateProperty(t, selection, evKey, undefined),
|
|
192
|
+
);
|
|
190
193
|
}
|
|
191
194
|
}}
|
|
192
195
|
>
|
|
@@ -212,12 +215,14 @@ export function eventsSidebarTemplate(S, helpers) {
|
|
|
212
215
|
}
|
|
213
216
|
}
|
|
214
217
|
if (functionDefs.length > 0) {
|
|
215
|
-
|
|
216
|
-
|
|
218
|
+
transactDoc(activeTab.value, (t) =>
|
|
219
|
+
mutateUpdateProperty(t, selection, evName, {
|
|
220
|
+
$ref: `#/state/${functionDefs[0][0]}`,
|
|
221
|
+
}),
|
|
217
222
|
);
|
|
218
223
|
} else {
|
|
219
|
-
|
|
220
|
-
|
|
224
|
+
transactDoc(activeTab.value, (t) =>
|
|
225
|
+
mutateUpdateProperty(t, selection, evName, {
|
|
221
226
|
$prototype: "Function",
|
|
222
227
|
body: "",
|
|
223
228
|
parameters: [],
|
package/src/panels/git-panel.js
CHANGED
|
@@ -44,8 +44,11 @@ async function gitAction(action, body) {
|
|
|
44
44
|
|
|
45
45
|
let _pollTimer = /** @type {any} */ (null);
|
|
46
46
|
|
|
47
|
-
/**
|
|
48
|
-
|
|
47
|
+
/**
|
|
48
|
+
* @param {any} S
|
|
49
|
+
* @param {any} ctx
|
|
50
|
+
*/
|
|
51
|
+
export function renderGitPanel(S, ctx) {
|
|
49
52
|
const status = S.ui.gitStatus;
|
|
50
53
|
const branches = S.ui.gitBranches;
|
|
51
54
|
const loading = S.ui.gitLoading;
|
|
@@ -156,9 +159,48 @@ export function renderGitPanel(S) {
|
|
|
156
159
|
const parts = file.path.split("/");
|
|
157
160
|
const name = parts.pop();
|
|
158
161
|
const dir = parts.join("/");
|
|
162
|
+
|
|
163
|
+
const onFileClick = async () => {
|
|
164
|
+
// Only support diff for modified/added files
|
|
165
|
+
if (file.status !== "M" && file.status !== "A") return;
|
|
166
|
+
if (!file.path.endsWith(".md") && !file.path.endsWith(".json")) return;
|
|
167
|
+
|
|
168
|
+
try {
|
|
169
|
+
const plat = getPlatform();
|
|
170
|
+
updateUi("gitLoading", true);
|
|
171
|
+
const originalContent = await plat.gitShow({ path: file.path, ref: "HEAD" });
|
|
172
|
+
|
|
173
|
+
// Determine if it's markdown or JSON
|
|
174
|
+
const isMarkdown = file.path.endsWith(".md");
|
|
175
|
+
|
|
176
|
+
// For now, we'll need to pass the original content to the canvas
|
|
177
|
+
// This requires state management which we'll add to studio.js
|
|
178
|
+
updateUi("gitDiffState", {
|
|
179
|
+
filePath: file.path,
|
|
180
|
+
originalContent,
|
|
181
|
+
isMarkdown,
|
|
182
|
+
fileStatus: file.status,
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
// Trigger diff mode via context
|
|
186
|
+
if (ctx?.setCanvasMode) {
|
|
187
|
+
ctx.setCanvasMode("git-diff");
|
|
188
|
+
}
|
|
189
|
+
} catch (/** @type {any} */ e) {
|
|
190
|
+
updateUi("gitError", `Failed to load diff: ${e.message}`);
|
|
191
|
+
} finally {
|
|
192
|
+
updateUi("gitLoading", false);
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
|
|
159
196
|
return html`
|
|
160
197
|
<div class="git-file-row">
|
|
161
|
-
<span
|
|
198
|
+
<span
|
|
199
|
+
class="git-file-info"
|
|
200
|
+
style="cursor: pointer; flex: 1;"
|
|
201
|
+
title="Click to view diff"
|
|
202
|
+
@click=${onFileClick}
|
|
203
|
+
>
|
|
162
204
|
<span class="git-file-name" title=${file.path}>${name}</span>
|
|
163
205
|
${dir ? html`<span class="git-file-dir">${dir}</span>` : nothing}
|
|
164
206
|
</span>
|
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
import { html, nothing } from "lit-html";
|
|
7
7
|
import {
|
|
8
8
|
getState,
|
|
9
|
-
update,
|
|
10
9
|
flattenTree,
|
|
11
10
|
getNodeAtPath,
|
|
12
11
|
pathKey,
|
|
@@ -14,11 +13,10 @@ import {
|
|
|
14
13
|
parentElementPath,
|
|
15
14
|
childIndex,
|
|
16
15
|
nodeLabel,
|
|
17
|
-
selectNode,
|
|
18
|
-
moveNode,
|
|
19
|
-
removeNode,
|
|
20
16
|
VOID_ELEMENTS,
|
|
21
17
|
} from "../store.js";
|
|
18
|
+
import { activeTab } from "../workspace/workspace.js";
|
|
19
|
+
import { transactDoc, mutateMoveNode, mutateRemoveNode } from "../tabs/transact.js";
|
|
22
20
|
import { view } from "../view.js";
|
|
23
21
|
import { isInlineElement } from "../editor/inline-edit.js";
|
|
24
22
|
import { showContextMenu } from "../editor/context-menu.js";
|
|
@@ -34,7 +32,8 @@ export function renderLayersTemplate(ctx) {
|
|
|
34
32
|
view.dndCleanups = [];
|
|
35
33
|
|
|
36
34
|
const rows = flattenTree(S.document);
|
|
37
|
-
const
|
|
35
|
+
const _S = /** @type {any} */ (S);
|
|
36
|
+
const collapsed = _S._collapsed || (_S._collapsed = new Set());
|
|
38
37
|
|
|
39
38
|
/** @type {any[]} */
|
|
40
39
|
const layerRows = [];
|
|
@@ -142,7 +141,7 @@ export function renderLayersTemplate(ctx) {
|
|
|
142
141
|
data-dnd-row=${isElement ? key : nothing}
|
|
143
142
|
data-dnd-depth=${isElement ? depth : nothing}
|
|
144
143
|
data-dnd-void=${isElement && isVoidEl ? "" : nothing}
|
|
145
|
-
@click=${() =>
|
|
144
|
+
@click=${() => (activeTab.value.session.selection = path)}
|
|
146
145
|
@contextmenu=${isElement
|
|
147
146
|
? (/** @type {any} */ e) =>
|
|
148
147
|
showContextMenu(e, path, getState(), {
|
|
@@ -175,7 +174,9 @@ export function renderLayersTemplate(ctx) {
|
|
|
175
174
|
@click=${(/** @type {any} */ e) => {
|
|
176
175
|
e.stopPropagation();
|
|
177
176
|
/** @type {HTMLElement} */ (e.currentTarget).blur();
|
|
178
|
-
|
|
177
|
+
transactDoc(activeTab.value, (t) =>
|
|
178
|
+
mutateMoveNode(t, path, parentPath, idx - 1),
|
|
179
|
+
);
|
|
179
180
|
}}
|
|
180
181
|
>
|
|
181
182
|
<sp-icon-arrow-up slot="icon"></sp-icon-arrow-up>
|
|
@@ -189,7 +190,9 @@ export function renderLayersTemplate(ctx) {
|
|
|
189
190
|
@click=${(/** @type {any} */ e) => {
|
|
190
191
|
e.stopPropagation();
|
|
191
192
|
/** @type {HTMLElement} */ (e.currentTarget).blur();
|
|
192
|
-
|
|
193
|
+
transactDoc(activeTab.value, (t) =>
|
|
194
|
+
mutateMoveNode(t, path, parentPath, idx + 2),
|
|
195
|
+
);
|
|
193
196
|
}}
|
|
194
197
|
>
|
|
195
198
|
<sp-icon-arrow-down slot="icon"></sp-icon-arrow-down>
|
|
@@ -206,7 +209,7 @@ export function renderLayersTemplate(ctx) {
|
|
|
206
209
|
const prevPath = [...parentPath, idx - 1];
|
|
207
210
|
const prev = getNodeAtPath(getState().document, prevPath);
|
|
208
211
|
const len = prev?.children?.length || 0;
|
|
209
|
-
|
|
212
|
+
transactDoc(activeTab.value, (t) => mutateMoveNode(t, path, prevPath, len));
|
|
210
213
|
}}
|
|
211
214
|
>
|
|
212
215
|
<sp-icon-arrow-right slot="icon"></sp-icon-arrow-right>
|
|
@@ -221,7 +224,9 @@ export function renderLayersTemplate(ctx) {
|
|
|
221
224
|
e.stopPropagation();
|
|
222
225
|
/** @type {HTMLElement} */ (e.currentTarget).blur();
|
|
223
226
|
const parentIdx = /** @type {number} */ (childIndex(parentPath));
|
|
224
|
-
|
|
227
|
+
transactDoc(activeTab.value, (t) =>
|
|
228
|
+
mutateMoveNode(t, path, grandparentPath, parentIdx + 1),
|
|
229
|
+
);
|
|
225
230
|
}}
|
|
226
231
|
>
|
|
227
232
|
<sp-icon-arrow-left slot="icon"></sp-icon-arrow-left>
|
|
@@ -234,7 +239,7 @@ export function renderLayersTemplate(ctx) {
|
|
|
234
239
|
title="Delete"
|
|
235
240
|
@click=${(/** @type {any} */ e) => {
|
|
236
241
|
e.stopPropagation();
|
|
237
|
-
|
|
242
|
+
transactDoc(activeTab.value, (t) => mutateRemoveNode(t, path));
|
|
238
243
|
}}
|
|
239
244
|
>
|
|
240
245
|
<sp-icon-close slot="icon"></sp-icon-close>
|
package/src/panels/left-panel.js
CHANGED
|
@@ -7,40 +7,94 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import { html, render as litRender, nothing } from "lit-html";
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
applyMutation,
|
|
16
|
-
updateFrontmatter,
|
|
17
|
-
} from "../store.js";
|
|
18
|
-
import { ensureLitState } from "./shared.js";
|
|
10
|
+
import { leftPanel, updateSession } from "../store.js";
|
|
11
|
+
import { effect, effectScope } from "../reactivity.js";
|
|
12
|
+
import { activeTab } from "../workspace/workspace.js";
|
|
13
|
+
import { transact, mutateUpdateFrontmatter } from "../tabs/transact.js";
|
|
14
|
+
|
|
19
15
|
import { renderLayersTemplate } from "./layers-panel.js";
|
|
20
16
|
import { renderStylebookLayersTemplate } from "./stylebook-layers-panel.js";
|
|
21
17
|
import { renderElementsTemplate } from "./elements-panel.js";
|
|
22
18
|
import { selectStylebookTag, stylebookMeta } from "./stylebook-panel.js";
|
|
23
19
|
|
|
24
|
-
/** @
|
|
20
|
+
/** @typedef {import("lit-html").TemplateResult} TemplateResult */
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @typedef {{
|
|
24
|
+
* getCanvasMode: () => string;
|
|
25
|
+
* setCanvasMode: (mode: string) => void;
|
|
26
|
+
* renderImportsTemplate: (...args: any[]) => TemplateResult;
|
|
27
|
+
* renderFilesTemplate: () => TemplateResult;
|
|
28
|
+
* renderSignalsTemplate: (...args: any[]) => TemplateResult;
|
|
29
|
+
* renderDataExplorerTemplate: (...args: any[]) => TemplateResult;
|
|
30
|
+
* renderHeadTemplate: (...args: any[]) => TemplateResult;
|
|
31
|
+
* renderGitPanel: (...args: any[]) => TemplateResult;
|
|
32
|
+
* renderCanvas: () => void;
|
|
33
|
+
* defCategory: (tag: string) => string;
|
|
34
|
+
* defBadgeLabel: (tag: string) => string;
|
|
35
|
+
* navigateToComponent: (path: string) => void;
|
|
36
|
+
* webdata: object;
|
|
37
|
+
* defaultDef: (tag: string) => object;
|
|
38
|
+
* registerLayersDnD: () => void;
|
|
39
|
+
* registerElementsDnD: () => void;
|
|
40
|
+
* registerComponentsDnD: () => void;
|
|
41
|
+
* setupTreeKeyboard: (tree: HTMLElement) => void;
|
|
42
|
+
* }} LeftPanelCtx
|
|
43
|
+
*/
|
|
44
|
+
|
|
45
|
+
/** @type {LeftPanelCtx | null} */
|
|
25
46
|
let _ctx = null;
|
|
26
47
|
|
|
48
|
+
/** @type {import("@vue/reactivity").EffectScope | null} */
|
|
49
|
+
let _scope = null;
|
|
50
|
+
|
|
51
|
+
let _rendering = false;
|
|
52
|
+
let _scheduled = false;
|
|
53
|
+
|
|
27
54
|
/**
|
|
28
55
|
* Mount the left panel orchestrator.
|
|
29
56
|
*
|
|
30
|
-
* @param {
|
|
57
|
+
* @param {LeftPanelCtx} ctx
|
|
31
58
|
*/
|
|
32
59
|
export function mount(ctx) {
|
|
33
60
|
_ctx = ctx;
|
|
61
|
+
_scope = effectScope();
|
|
62
|
+
_scope.run(() => {
|
|
63
|
+
effect(() => {
|
|
64
|
+
const tab = activeTab.value;
|
|
65
|
+
if (!tab) return;
|
|
66
|
+
// Track properties the left panel reads
|
|
67
|
+
void tab.doc.document;
|
|
68
|
+
void tab.doc.mode;
|
|
69
|
+
void tab.session.selection;
|
|
70
|
+
void tab.session.ui.leftTab;
|
|
71
|
+
void tab.session.ui.settingsTab;
|
|
72
|
+
render();
|
|
73
|
+
});
|
|
74
|
+
});
|
|
34
75
|
}
|
|
35
76
|
|
|
36
77
|
export function unmount() {
|
|
78
|
+
_scope?.stop();
|
|
79
|
+
_scope = null;
|
|
37
80
|
_ctx = null;
|
|
38
81
|
}
|
|
39
82
|
|
|
40
83
|
export function render() {
|
|
41
84
|
if (!_ctx) return;
|
|
85
|
+
if (_rendering) return;
|
|
86
|
+
if (!_scheduled) {
|
|
87
|
+
_scheduled = true;
|
|
88
|
+
queueMicrotask(_flush);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function _flush() {
|
|
93
|
+
_scheduled = false;
|
|
94
|
+
if (!_ctx) return;
|
|
95
|
+
if (_rendering) return;
|
|
96
|
+
_rendering = true;
|
|
42
97
|
try {
|
|
43
|
-
ensureLitState(leftPanel);
|
|
44
98
|
_render();
|
|
45
99
|
} catch (e) {
|
|
46
100
|
console.error("left-panel render error:", e);
|
|
@@ -52,90 +106,101 @@ export function render() {
|
|
|
52
106
|
} catch (e2) {
|
|
53
107
|
console.error("left-panel retry failed:", e2);
|
|
54
108
|
}
|
|
109
|
+
} finally {
|
|
110
|
+
_rendering = false;
|
|
55
111
|
}
|
|
56
112
|
}
|
|
57
113
|
|
|
58
114
|
function _render() {
|
|
59
|
-
const
|
|
115
|
+
const ctx = /** @type {LeftPanelCtx} */ (_ctx);
|
|
116
|
+
const aTab = activeTab.value;
|
|
117
|
+
if (!aTab) return;
|
|
118
|
+
const S = /** @type {any} */ ({
|
|
119
|
+
ui: aTab.session.ui,
|
|
120
|
+
document: aTab.doc.document,
|
|
121
|
+
mode: aTab.doc.mode,
|
|
122
|
+
selection: aTab.session.selection,
|
|
123
|
+
});
|
|
60
124
|
const tab = S.ui.leftTab;
|
|
61
125
|
|
|
62
|
-
/** @type {
|
|
126
|
+
/** @type {TemplateResult | typeof nothing} */
|
|
63
127
|
let content;
|
|
64
128
|
if (tab === "layers")
|
|
65
129
|
content =
|
|
66
|
-
|
|
130
|
+
ctx.getCanvasMode() === "settings"
|
|
67
131
|
? renderStylebookLayersTemplate({
|
|
68
132
|
selectStylebookTag,
|
|
69
133
|
stylebookMeta,
|
|
70
134
|
})
|
|
71
135
|
: renderLayersTemplate({
|
|
72
|
-
navigateToComponent:
|
|
136
|
+
navigateToComponent: ctx.navigateToComponent,
|
|
73
137
|
rerender: render,
|
|
74
138
|
});
|
|
75
139
|
else if (tab === "imports")
|
|
76
|
-
content =
|
|
140
|
+
content = ctx.renderImportsTemplate({
|
|
77
141
|
renderLeftPanel: render,
|
|
78
142
|
documentPath: S.documentPath,
|
|
79
143
|
documentElements: S.document.$elements || [],
|
|
80
|
-
applyMutation: (/** @type {
|
|
81
|
-
|
|
144
|
+
applyMutation: (/** @type {(doc: object) => void} */ fn) => {
|
|
145
|
+
transact(activeTab.value, fn);
|
|
82
146
|
},
|
|
83
147
|
});
|
|
84
|
-
else if (tab === "files") content =
|
|
148
|
+
else if (tab === "files") content = ctx.renderFilesTemplate();
|
|
85
149
|
else if (tab === "blocks")
|
|
86
150
|
content = renderElementsTemplate({
|
|
87
|
-
webdata:
|
|
88
|
-
defaultDef:
|
|
151
|
+
webdata: ctx.webdata,
|
|
152
|
+
defaultDef: ctx.defaultDef,
|
|
89
153
|
rerender: render,
|
|
90
154
|
});
|
|
91
155
|
else if (tab === "state")
|
|
92
|
-
content =
|
|
156
|
+
content = ctx.renderSignalsTemplate(S, {
|
|
93
157
|
renderLeftPanel: render,
|
|
94
|
-
renderCanvas:
|
|
158
|
+
renderCanvas: ctx.renderCanvas,
|
|
95
159
|
updateSession,
|
|
96
160
|
});
|
|
97
161
|
else if (tab === "data")
|
|
98
|
-
content =
|
|
99
|
-
renderCanvas:
|
|
162
|
+
content = ctx.renderDataExplorerTemplate(S.document.state, S.canvas?.scope ?? null, {
|
|
163
|
+
renderCanvas: ctx.renderCanvas,
|
|
100
164
|
renderLeftPanel: render,
|
|
101
|
-
defCategory:
|
|
102
|
-
defBadgeLabel:
|
|
165
|
+
defCategory: ctx.defCategory,
|
|
166
|
+
defBadgeLabel: ctx.defBadgeLabel,
|
|
103
167
|
});
|
|
104
168
|
else if (tab === "head") {
|
|
105
169
|
const isContent = S.mode === "content";
|
|
106
170
|
const fm = S.content?.frontmatter ?? {};
|
|
107
171
|
const headDoc = isContent ? { ...S.document, title: fm.title, $head: fm.$head } : S.document;
|
|
108
|
-
content =
|
|
172
|
+
content = ctx.renderHeadTemplate({
|
|
109
173
|
document: headDoc,
|
|
110
174
|
applyMutation: isContent
|
|
111
|
-
? (/** @type {
|
|
175
|
+
? (/** @type {(doc: object) => void} */ fn) => {
|
|
176
|
+
const tab = activeTab.value;
|
|
177
|
+
const fm = /** @type {Record<string, any>} */ (tab.doc.content?.frontmatter ?? {});
|
|
112
178
|
const tmp = { title: fm.title, $head: fm.$head ? [...fm.$head] : undefined };
|
|
113
179
|
fn(tmp);
|
|
114
|
-
|
|
115
|
-
|
|
180
|
+
if (tmp.title !== fm.title)
|
|
181
|
+
mutateUpdateFrontmatter(tab, "title", /** @type {any} */ (tmp.title));
|
|
116
182
|
const newHead = tmp.$head && tmp.$head.length > 0 ? tmp.$head : undefined;
|
|
117
|
-
|
|
118
|
-
update(s);
|
|
183
|
+
mutateUpdateFrontmatter(tab, "$head", newHead);
|
|
119
184
|
}
|
|
120
|
-
: (/** @type {
|
|
121
|
-
|
|
185
|
+
: (/** @type {(doc: object) => void} */ fn) => {
|
|
186
|
+
transact(activeTab.value, fn);
|
|
122
187
|
},
|
|
123
188
|
renderLeftPanel: render,
|
|
124
189
|
});
|
|
125
|
-
} else if (tab === "git") content =
|
|
190
|
+
} else if (tab === "git") content = ctx.renderGitPanel(S, ctx);
|
|
126
191
|
else content = nothing;
|
|
127
192
|
|
|
128
|
-
litRender(html`<div class="panel-body">${content}</div>`,
|
|
193
|
+
litRender(html`<div class="panel-body">${content}</div>`, leftPanel);
|
|
129
194
|
|
|
130
195
|
// Post-render side effects
|
|
131
|
-
if (tab === "layers" &&
|
|
196
|
+
if (tab === "layers" && ctx.getCanvasMode() !== "settings") ctx.registerLayersDnD();
|
|
132
197
|
else if (tab === "imports") {
|
|
133
198
|
/* no post-render DnD needed */
|
|
134
199
|
} else if (tab === "blocks") {
|
|
135
|
-
|
|
136
|
-
|
|
200
|
+
ctx.registerElementsDnD();
|
|
201
|
+
ctx.registerComponentsDnD();
|
|
137
202
|
} else if (tab === "files") {
|
|
138
|
-
const tree = /** @type {
|
|
139
|
-
if (tree)
|
|
203
|
+
const tree = /** @type {HTMLElement | null} */ (leftPanel.querySelector(".file-tree"));
|
|
204
|
+
if (tree) ctx.setupTreeKeyboard(tree);
|
|
140
205
|
}
|
|
141
206
|
}
|