@jxsuite/studio 0.33.0 → 0.35.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/iframe-entry.js +6238 -0
- package/dist/iframe-entry.js.map +35 -0
- package/dist/studio.js +30972 -17222
- package/dist/studio.js.map +328 -83
- package/package.json +9 -9
- package/src/browse/browse.ts +11 -4
- package/src/canvas/canvas-helpers.ts +2 -56
- package/src/canvas/canvas-live-render.ts +102 -435
- package/src/canvas/canvas-origin.ts +66 -0
- package/src/canvas/canvas-patcher.ts +63 -403
- package/src/canvas/canvas-render.ts +70 -212
- package/src/canvas/canvas-utils.ts +37 -65
- package/src/canvas/iframe-channel.ts +154 -0
- package/src/canvas/iframe-drop.ts +484 -0
- package/src/canvas/iframe-entry.ts +600 -0
- package/src/canvas/iframe-host.ts +1373 -0
- package/src/canvas/iframe-inline-edit.ts +367 -0
- package/src/canvas/iframe-insert.ts +164 -0
- package/src/canvas/iframe-interaction.ts +176 -0
- package/src/canvas/iframe-keys.ts +85 -0
- package/src/canvas/iframe-overlay.ts +218 -0
- package/src/canvas/iframe-patch.ts +363 -0
- package/src/canvas/iframe-protocol.ts +361 -0
- package/src/canvas/iframe-render.ts +458 -0
- package/src/canvas/iframe-slash.ts +114 -0
- package/src/canvas/iframe-subtree.ts +113 -0
- package/src/canvas/path-mapping.ts +86 -0
- package/src/canvas/serialize-scope.ts +65 -0
- package/src/editor/canvas-context-menu.ts +40 -0
- package/src/editor/canvas-slash-bridge.ts +21 -0
- package/src/editor/context-menu.ts +2 -1
- package/src/editor/inline-edit-apply.ts +183 -0
- package/src/editor/inline-edit.ts +99 -21
- package/src/editor/inline-link.ts +89 -0
- package/src/editor/insert-zone-action.ts +35 -0
- package/src/editor/merge-tags.ts +26 -2
- package/src/editor/repeater-scope.ts +144 -0
- package/src/editor/shortcuts.ts +14 -28
- package/src/editor/slash-menu.ts +73 -42
- package/src/files/files.ts +5 -1
- package/src/new-project/design-fields.ts +260 -0
- package/src/new-project/import-tab.ts +322 -0
- package/src/new-project/new-project-modal.ts +472 -89
- package/src/new-project/templates.ts +61 -0
- package/src/packages/ensure-deps.ts +6 -0
- package/src/packages/pull-package-sync.ts +339 -0
- package/src/page-params.ts +383 -0
- package/src/panels/ai-chat/attached-context.ts +49 -0
- package/src/panels/ai-chat/chat-markdown.ts +38 -0
- package/src/panels/ai-chat/chat-view.ts +250 -0
- package/src/panels/ai-chat/composer.ts +315 -0
- package/src/panels/ai-chat/sessions-view.ts +98 -0
- package/src/panels/ai-panel.ts +189 -457
- package/src/panels/block-action-bar.ts +296 -138
- package/src/panels/canvas-dnd-bridge.ts +397 -0
- package/src/panels/component-preview.ts +56 -0
- package/src/panels/dnd.ts +41 -17
- package/src/panels/drag-ghost.ts +62 -0
- package/src/panels/editors.ts +1 -1
- package/src/panels/git-panel.ts +16 -1
- package/src/panels/overlays.ts +10 -125
- package/src/panels/properties-panel.ts +210 -0
- package/src/panels/right-panel.ts +21 -7
- package/src/panels/signals-panel.ts +136 -22
- package/src/panels/stylebook-doc.ts +373 -0
- package/src/panels/stylebook-panel.ts +46 -689
- package/src/panels/tab-bar.ts +159 -13
- package/src/panels/toolbar.ts +3 -2
- package/src/platforms/devserver.ts +45 -1
- package/src/services/agent-seed.ts +91 -0
- package/src/services/ai-models.ts +64 -0
- package/src/services/ai-session-store.ts +250 -0
- package/src/services/ai-settings.ts +5 -0
- package/src/services/automation.ts +140 -0
- package/src/services/document-assistant.ts +98 -38
- package/src/services/import-client.ts +134 -0
- package/src/services/monaco-setup.ts +12 -0
- package/src/services/render-critic.ts +9 -9
- package/src/services/settings-store.ts +93 -0
- package/src/settings/css-vars-editor.ts +2 -2
- package/src/store.ts +4 -62
- package/src/studio.ts +115 -40
- package/src/tabs/doc-op-apply.ts +89 -0
- package/src/tabs/patch-ops.ts +6 -2
- package/src/tabs/tab.ts +23 -4
- package/src/tabs/transact.ts +2 -74
- package/src/types.ts +98 -18
- package/src/ui/ai-credentials-form.ts +205 -0
- package/src/ui/jx-theme.ts +63 -0
- package/src/ui/media-picker.ts +6 -4
- package/src/ui/spectrum.ts +13 -0
- package/src/utils/canvas-media.ts +0 -137
- package/src/utils/edit-display.ts +23 -3
- package/src/utils/geometry.ts +43 -0
- package/src/utils/link-target.ts +93 -0
- package/src/utils/strip-events.ts +54 -0
- package/src/view.ts +0 -23
- package/src/workspace/workspace.ts +14 -1
- package/src/canvas/canvas-subtree-render.ts +0 -113
- package/src/editor/component-inline-edit.ts +0 -349
- package/src/editor/content-inline-edit.ts +0 -207
- package/src/editor/insertion-helper.ts +0 -308
- package/src/panels/canvas-dnd.ts +0 -329
- package/src/panels/panel-events.ts +0 -306
- package/src/panels/preview-render.ts +0 -132
- package/src/panels/pseudo-preview.ts +0 -75
|
@@ -1,306 +0,0 @@
|
|
|
1
|
-
/// <reference lib="dom" />
|
|
2
|
-
/**
|
|
3
|
-
* Panel events — extracted from studio.js (Phase 4m). Unified event handler system for canvas
|
|
4
|
-
* panels: click-to-select, double-click inline edit, context menu, hover tracking, insertion
|
|
5
|
-
* helper.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import {
|
|
9
|
-
childIndex,
|
|
10
|
-
elToPath,
|
|
11
|
-
getNodeAtPath,
|
|
12
|
-
parentElementPath,
|
|
13
|
-
pathsEqual,
|
|
14
|
-
renderOnly,
|
|
15
|
-
updateUi,
|
|
16
|
-
} from "../store";
|
|
17
|
-
import { activeTab } from "../workspace/workspace";
|
|
18
|
-
import { view } from "../view";
|
|
19
|
-
import { isEditableBlock, isEditing, stopEditing } from "../editor/inline-edit";
|
|
20
|
-
import { showContextMenu } from "../editor/context-menu";
|
|
21
|
-
import * as insertionHelper from "../editor/insertion-helper";
|
|
22
|
-
import { defaultDef } from "../panels/shared";
|
|
23
|
-
import {
|
|
24
|
-
bubbleInlinePath,
|
|
25
|
-
effectiveZoom,
|
|
26
|
-
findCanvasElement,
|
|
27
|
-
panelMediaToActiveMedia,
|
|
28
|
-
} from "../canvas/canvas-helpers";
|
|
29
|
-
import { activeLayoutPath, layoutElements } from "../canvas/canvas-live-render";
|
|
30
|
-
import type { CanvasPanel } from "./canvas-dnd";
|
|
31
|
-
import type { JxPath } from "../state";
|
|
32
|
-
|
|
33
|
-
interface PanelEventsCtx {
|
|
34
|
-
getCanvasMode: () => string;
|
|
35
|
-
enterInlineEdit: (el: HTMLElement, path: JxPath) => void;
|
|
36
|
-
navigateToComponent: (path: string) => void;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
let _ctx: PanelEventsCtx | null = null;
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Initialize the panel events module.
|
|
43
|
-
*
|
|
44
|
-
* @param {PanelEventsCtx} ctx
|
|
45
|
-
*/
|
|
46
|
-
export function initPanelEvents(ctx: PanelEventsCtx) {
|
|
47
|
-
_ctx = ctx;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/** @param {import("../canvas/canvas-render.js").CanvasPanel} panel */
|
|
51
|
-
export function registerPanelEvents(panel: CanvasPanel) {
|
|
52
|
-
const ctx = _ctx as PanelEventsCtx;
|
|
53
|
-
const canvas = panel.canvas as HTMLElement;
|
|
54
|
-
const overlayClk = panel.overlayClk as HTMLElement;
|
|
55
|
-
const { mediaName } = panel;
|
|
56
|
-
const ac = new AbortController();
|
|
57
|
-
const opts = { signal: ac.signal };
|
|
58
|
-
view.canvasEventCleanups.push(() => ac.abort());
|
|
59
|
-
|
|
60
|
-
/** @param {() => unknown} fn */
|
|
61
|
-
function withPanelPointerEvents(fn: () => unknown) {
|
|
62
|
-
const els = canvas.querySelectorAll("*") as NodeListOf<HTMLElement>;
|
|
63
|
-
for (const el of els) {
|
|
64
|
-
el.style.pointerEvents = "auto";
|
|
65
|
-
}
|
|
66
|
-
overlayClk.style.display = "none";
|
|
67
|
-
const result = fn();
|
|
68
|
-
overlayClk.style.display = "";
|
|
69
|
-
for (const el of els) {
|
|
70
|
-
el.style.pointerEvents = "none";
|
|
71
|
-
}
|
|
72
|
-
return result;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
overlayClk.addEventListener(
|
|
76
|
-
"click",
|
|
77
|
-
(e: MouseEvent) => {
|
|
78
|
-
const barInner = view.blockActionBarEl?.firstElementChild;
|
|
79
|
-
if (barInner) {
|
|
80
|
-
const r = barInner.getBoundingClientRect();
|
|
81
|
-
if (
|
|
82
|
-
e.clientX >= r.left &&
|
|
83
|
-
e.clientX <= r.right &&
|
|
84
|
-
e.clientY >= r.top &&
|
|
85
|
-
e.clientY <= r.bottom
|
|
86
|
-
) {
|
|
87
|
-
return;
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
if (isEditing()) {
|
|
91
|
-
stopEditing();
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
const tab = activeTab.value;
|
|
95
|
-
const canvasMode = ctx.getCanvasMode();
|
|
96
|
-
|
|
97
|
-
const elements = withPanelPointerEvents(() =>
|
|
98
|
-
document.elementsFromPoint(e.clientX, e.clientY),
|
|
99
|
-
) as Element[];
|
|
100
|
-
|
|
101
|
-
if (!tab) {
|
|
102
|
-
return;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
for (const el of elements) {
|
|
106
|
-
if (canvas.contains(el) && el !== canvas) {
|
|
107
|
-
// Layout element clicked — show layout info instead of selecting in page doc
|
|
108
|
-
if (layoutElements.has(el)) {
|
|
109
|
-
view.layoutSelection = { el, layoutPath: activeLayoutPath };
|
|
110
|
-
tab.session.selection = null;
|
|
111
|
-
renderOnly("rightPanel");
|
|
112
|
-
return;
|
|
113
|
-
}
|
|
114
|
-
view.layoutSelection = null;
|
|
115
|
-
|
|
116
|
-
const originalPath = elToPath.get(el);
|
|
117
|
-
if (originalPath) {
|
|
118
|
-
const path = bubbleInlinePath(tab.doc.document, originalPath);
|
|
119
|
-
const newMedia = panelMediaToActiveMedia(mediaName);
|
|
120
|
-
|
|
121
|
-
const resolvedEl = (
|
|
122
|
-
path === originalPath
|
|
123
|
-
? el
|
|
124
|
-
: (findCanvasElement(path, canvas) as HTMLElement | null) || el
|
|
125
|
-
) as HTMLElement;
|
|
126
|
-
|
|
127
|
-
if (
|
|
128
|
-
pathsEqual(path, tab.session.selection) &&
|
|
129
|
-
isEditableBlock(resolvedEl) &&
|
|
130
|
-
(canvasMode === "edit" || tab.doc.mode === "content")
|
|
131
|
-
) {
|
|
132
|
-
tab.session.ui.activeMedia = newMedia;
|
|
133
|
-
ctx.enterInlineEdit(resolvedEl, path);
|
|
134
|
-
return;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
if (canvasMode === "design" && tab.doc.mode !== "content") {
|
|
138
|
-
updateUi("pendingInlineEdit", { mediaName, path });
|
|
139
|
-
tab.session.ui.activeMedia = newMedia;
|
|
140
|
-
tab.session.selection = path;
|
|
141
|
-
return;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
tab.session.ui.activeMedia = newMedia;
|
|
145
|
-
tab.session.selection = path;
|
|
146
|
-
return;
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
tab.session.selection = null;
|
|
151
|
-
},
|
|
152
|
-
opts,
|
|
153
|
-
);
|
|
154
|
-
|
|
155
|
-
overlayClk.addEventListener(
|
|
156
|
-
"dblclick",
|
|
157
|
-
(e: MouseEvent) => {
|
|
158
|
-
const barInner = view.blockActionBarEl?.firstElementChild;
|
|
159
|
-
if (barInner) {
|
|
160
|
-
const r = barInner.getBoundingClientRect();
|
|
161
|
-
if (
|
|
162
|
-
e.clientX >= r.left &&
|
|
163
|
-
e.clientX <= r.right &&
|
|
164
|
-
e.clientY >= r.top &&
|
|
165
|
-
e.clientY <= r.bottom
|
|
166
|
-
) {
|
|
167
|
-
return;
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
const canvasMode = ctx.getCanvasMode();
|
|
171
|
-
if (canvasMode !== "edit" && canvasMode !== "design") {
|
|
172
|
-
return;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
const tab = activeTab.value;
|
|
176
|
-
const elements = withPanelPointerEvents(() =>
|
|
177
|
-
document.elementsFromPoint(e.clientX, e.clientY),
|
|
178
|
-
) as Element[];
|
|
179
|
-
|
|
180
|
-
if (!tab) {
|
|
181
|
-
return;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
for (const el of elements) {
|
|
185
|
-
if (canvas.contains(el) && el !== canvas) {
|
|
186
|
-
const originalPath = elToPath.get(el);
|
|
187
|
-
if (originalPath) {
|
|
188
|
-
const path = bubbleInlinePath(tab.doc.document, originalPath);
|
|
189
|
-
const resolvedEl = (
|
|
190
|
-
path === originalPath
|
|
191
|
-
? el
|
|
192
|
-
: (findCanvasElement(path, canvas) as HTMLElement | null) || el
|
|
193
|
-
) as HTMLElement;
|
|
194
|
-
if (isEditableBlock(resolvedEl)) {
|
|
195
|
-
const newMedia = panelMediaToActiveMedia(mediaName);
|
|
196
|
-
tab.session.ui.activeMedia = newMedia;
|
|
197
|
-
tab.session.selection = path;
|
|
198
|
-
ctx.enterInlineEdit(resolvedEl, path);
|
|
199
|
-
return;
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
},
|
|
205
|
-
opts,
|
|
206
|
-
);
|
|
207
|
-
|
|
208
|
-
overlayClk.addEventListener(
|
|
209
|
-
"contextmenu",
|
|
210
|
-
(e: MouseEvent) => {
|
|
211
|
-
const barInner = view.blockActionBarEl?.firstElementChild;
|
|
212
|
-
if (barInner) {
|
|
213
|
-
const r = barInner.getBoundingClientRect();
|
|
214
|
-
if (
|
|
215
|
-
e.clientX >= r.left &&
|
|
216
|
-
e.clientX <= r.right &&
|
|
217
|
-
e.clientY >= r.top &&
|
|
218
|
-
e.clientY <= r.bottom
|
|
219
|
-
) {
|
|
220
|
-
return;
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
const tab = activeTab.value;
|
|
224
|
-
const elements = withPanelPointerEvents(() =>
|
|
225
|
-
document.elementsFromPoint(e.clientX, e.clientY),
|
|
226
|
-
) as Element[];
|
|
227
|
-
for (const el of elements) {
|
|
228
|
-
if (canvas.contains(el) && el !== canvas) {
|
|
229
|
-
let path = elToPath.get(el);
|
|
230
|
-
if (path) {
|
|
231
|
-
path = bubbleInlinePath(tab?.doc.document, path);
|
|
232
|
-
showContextMenu(e, path, {
|
|
233
|
-
onEditComponent: ctx.navigateToComponent,
|
|
234
|
-
});
|
|
235
|
-
return;
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
e.preventDefault();
|
|
240
|
-
},
|
|
241
|
-
opts,
|
|
242
|
-
);
|
|
243
|
-
|
|
244
|
-
overlayClk.addEventListener(
|
|
245
|
-
"mousemove",
|
|
246
|
-
(e: MouseEvent) => {
|
|
247
|
-
const barInner = view.blockActionBarEl?.firstElementChild;
|
|
248
|
-
if (barInner) {
|
|
249
|
-
const r = barInner.getBoundingClientRect();
|
|
250
|
-
if (
|
|
251
|
-
e.clientX >= r.left &&
|
|
252
|
-
e.clientX <= r.right &&
|
|
253
|
-
e.clientY >= r.top &&
|
|
254
|
-
e.clientY <= r.bottom
|
|
255
|
-
) {
|
|
256
|
-
return;
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
const tab = activeTab.value;
|
|
260
|
-
if (!tab) {
|
|
261
|
-
return;
|
|
262
|
-
}
|
|
263
|
-
const el = withPanelPointerEvents(() =>
|
|
264
|
-
document.elementFromPoint(e.clientX, e.clientY),
|
|
265
|
-
) as Element | null;
|
|
266
|
-
if (el && canvas.contains(el) && el !== canvas) {
|
|
267
|
-
let path = elToPath.get(el);
|
|
268
|
-
if (path) {
|
|
269
|
-
path = bubbleInlinePath(tab.doc.document, path);
|
|
270
|
-
if (!pathsEqual(path, tab.session.hover)) {
|
|
271
|
-
tab.session.hover = path;
|
|
272
|
-
renderOnly("overlays");
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
} else if (tab.session.hover) {
|
|
276
|
-
tab.session.hover = null;
|
|
277
|
-
renderOnly("overlays");
|
|
278
|
-
}
|
|
279
|
-
},
|
|
280
|
-
opts,
|
|
281
|
-
);
|
|
282
|
-
|
|
283
|
-
overlayClk.addEventListener(
|
|
284
|
-
"mouseleave",
|
|
285
|
-
() => {
|
|
286
|
-
if (activeTab.value?.session.hover) {
|
|
287
|
-
activeTab.value.session.hover = null;
|
|
288
|
-
renderOnly("overlays");
|
|
289
|
-
}
|
|
290
|
-
},
|
|
291
|
-
opts,
|
|
292
|
-
);
|
|
293
|
-
|
|
294
|
-
insertionHelper.mount({
|
|
295
|
-
childIndex: (path: JxPath) => childIndex(path) ?? 0,
|
|
296
|
-
defaultDef,
|
|
297
|
-
effectiveZoom,
|
|
298
|
-
elToPath,
|
|
299
|
-
getCanvasMode: ctx.getCanvasMode,
|
|
300
|
-
getNodeAtPath,
|
|
301
|
-
panel: panel as unknown as Parameters<typeof insertionHelper.mount>[0]["panel"],
|
|
302
|
-
parentElementPath,
|
|
303
|
-
withPanelPointerEvents,
|
|
304
|
-
});
|
|
305
|
-
view.canvasEventCleanups.push(() => insertionHelper.unmount());
|
|
306
|
-
}
|
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
/// <reference lib="dom" />
|
|
2
|
-
/**
|
|
3
|
-
* Preview render — extracted from studio.js (Phase 4m). Structural preview renderer that creates
|
|
4
|
-
* DOM from Jx node trees as a fallback when runtime rendering fails.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { elToPath } from "../store";
|
|
8
|
-
import { isMappedArray, isRef } from "@jxsuite/schema/guards";
|
|
9
|
-
import { activeTab } from "../workspace/workspace";
|
|
10
|
-
import { applyCanvasStyle } from "../utils/canvas-media";
|
|
11
|
-
import { resolveDefaultForCanvas } from "../panels/signals-panel";
|
|
12
|
-
import type { JxPath } from "../state";
|
|
13
|
-
import type { JxMutableNode } from "@jxsuite/schema/types";
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Recursively render a Jx node to the canvas DOM. Media-aware: applies base styles + active
|
|
17
|
-
* breakpoint/feature overrides.
|
|
18
|
-
*
|
|
19
|
-
* @param {JxMutableNode | string | number | boolean | null | undefined} node
|
|
20
|
-
* @param {JxPath} path
|
|
21
|
-
* @param {HTMLElement} parent
|
|
22
|
-
* @param {Set<string>} activeBreakpoints
|
|
23
|
-
* @param {Record<string, boolean>} featureToggles
|
|
24
|
-
*/
|
|
25
|
-
export function renderCanvasNode(
|
|
26
|
-
node: JxMutableNode | string | number | boolean | null | undefined,
|
|
27
|
-
path: JxPath,
|
|
28
|
-
parent: HTMLElement,
|
|
29
|
-
activeBreakpoints: Set<string>,
|
|
30
|
-
featureToggles: Record<string, boolean>,
|
|
31
|
-
) {
|
|
32
|
-
if (typeof node === "string" || typeof node === "number" || typeof node === "boolean") {
|
|
33
|
-
parent.append(document.createTextNode(String(node)));
|
|
34
|
-
return;
|
|
35
|
-
}
|
|
36
|
-
if (!node || typeof node !== "object") {
|
|
37
|
-
return;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// Array pseudo-element: render as a repeater perimeter at the array's own path, with a single
|
|
41
|
-
// Template instance inside (matches the edit-mode visual).
|
|
42
|
-
if (isMappedArray(node)) {
|
|
43
|
-
const wrapper = document.createElement("div");
|
|
44
|
-
wrapper.className = "repeater-perimeter";
|
|
45
|
-
elToPath.set(wrapper, path);
|
|
46
|
-
const template = node.map;
|
|
47
|
-
if (template && typeof template === "object") {
|
|
48
|
-
renderCanvasNode(template, [...path, "map"], wrapper, activeBreakpoints, featureToggles);
|
|
49
|
-
}
|
|
50
|
-
wrapper.style.pointerEvents = "none";
|
|
51
|
-
parent.append(wrapper);
|
|
52
|
-
return wrapper;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
const tag = node.tagName || "div";
|
|
56
|
-
const el = document.createElement(tag);
|
|
57
|
-
|
|
58
|
-
elToPath.set(el, path);
|
|
59
|
-
|
|
60
|
-
if (typeof node.textContent === "string") {
|
|
61
|
-
el.textContent = node.textContent;
|
|
62
|
-
} else if (isRef(node.textContent)) {
|
|
63
|
-
const tc = node.textContent;
|
|
64
|
-
const resolved = resolveDefaultForCanvas(tc, activeTab.value?.doc.document?.state);
|
|
65
|
-
el.textContent = typeof resolved === "string" ? resolved : null;
|
|
66
|
-
el.style.opacity = "0.7";
|
|
67
|
-
el.style.fontStyle = "italic";
|
|
68
|
-
el.title = `Bound: ${tc.$ref}`;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
if (node.id) {
|
|
72
|
-
el.id = node.id;
|
|
73
|
-
}
|
|
74
|
-
if (node.className) {
|
|
75
|
-
el.className = node.className;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
applyCanvasStyle(el, node.style, activeBreakpoints, featureToggles);
|
|
79
|
-
|
|
80
|
-
if (node.attributes && typeof node.attributes === "object") {
|
|
81
|
-
for (const [attr, val] of Object.entries(node.attributes)) {
|
|
82
|
-
try {
|
|
83
|
-
if (isRef(val)) {
|
|
84
|
-
const resolved = resolveDefaultForCanvas(val, activeTab.value?.doc.document?.state);
|
|
85
|
-
el.setAttribute(attr, String(resolved ?? ""));
|
|
86
|
-
} else {
|
|
87
|
-
el.setAttribute(attr, String(val));
|
|
88
|
-
}
|
|
89
|
-
} catch {}
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
if (Array.isArray(node.children)) {
|
|
94
|
-
for (let i = 0; i < node.children.length; i++) {
|
|
95
|
-
renderCanvasNode(
|
|
96
|
-
node.children[i],
|
|
97
|
-
[...path, "children", i],
|
|
98
|
-
el,
|
|
99
|
-
activeBreakpoints,
|
|
100
|
-
featureToggles,
|
|
101
|
-
);
|
|
102
|
-
}
|
|
103
|
-
} else if (isMappedArray(node.children)) {
|
|
104
|
-
const template = node.children.map;
|
|
105
|
-
if (template && typeof template === "object") {
|
|
106
|
-
const wrapper = document.createElement("div");
|
|
107
|
-
wrapper.className = "repeater-perimeter";
|
|
108
|
-
elToPath.set(wrapper, [...path, "children"]);
|
|
109
|
-
renderCanvasNode(
|
|
110
|
-
template,
|
|
111
|
-
[...path, "children", "map"],
|
|
112
|
-
wrapper,
|
|
113
|
-
activeBreakpoints,
|
|
114
|
-
featureToggles,
|
|
115
|
-
);
|
|
116
|
-
el.append(wrapper);
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
if (node.$switch && node.cases && typeof node.cases === "object") {
|
|
121
|
-
const keys = Object.keys(node.cases);
|
|
122
|
-
const placeholder = document.createElement("div");
|
|
123
|
-
placeholder.textContent = `[$switch: ${keys.join(" | ")}]`;
|
|
124
|
-
placeholder.style.cssText =
|
|
125
|
-
"font-family:var(--font-mono);font-size:var(--spectrum-font-size-50,11px);padding:6px 10px;background:color-mix(in srgb, var(--danger) 8%, transparent);border:1px dashed color-mix(in srgb, var(--danger) 40%, transparent);border-radius:var(--radius);color:var(--danger);font-style:italic";
|
|
126
|
-
el.append(placeholder);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
el.style.pointerEvents = "none";
|
|
130
|
-
parent.append(el);
|
|
131
|
-
return el;
|
|
132
|
-
}
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
/// <reference lib="dom" />
|
|
2
|
-
/**
|
|
3
|
-
* Pseudo-state preview — extracted from studio.js (Phase 4m). When a pseudo-selector (:hover,
|
|
4
|
-
* :focus, etc.) is active in the style sidebar, force those styles onto the selected element.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { getNodeAtPath } from "../store";
|
|
8
|
-
import { getNestedStyle } from "@jxsuite/schema/guards";
|
|
9
|
-
import { activeTab } from "../workspace/workspace";
|
|
10
|
-
import { view } from "../view";
|
|
11
|
-
import { findCanvasElement, getActivePanel } from "../canvas/canvas-helpers";
|
|
12
|
-
|
|
13
|
-
import type { JxStyle } from "@jxsuite/schema/types";
|
|
14
|
-
|
|
15
|
-
const pseudoStyleHost = document.createElement("div");
|
|
16
|
-
pseudoStyleHost.style.display = "contents";
|
|
17
|
-
(document.querySelector("sp-theme") || document.body).append(pseudoStyleHost);
|
|
18
|
-
|
|
19
|
-
export function updateForcedPseudoPreview() {
|
|
20
|
-
if (view.forcedStyleTag) {
|
|
21
|
-
view.forcedStyleTag.remove();
|
|
22
|
-
view.forcedStyleTag = null;
|
|
23
|
-
}
|
|
24
|
-
if (view.forcedAttrEl) {
|
|
25
|
-
delete view.forcedAttrEl.dataset.studioForced;
|
|
26
|
-
view.forcedAttrEl = null;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const tab = activeTab.value;
|
|
30
|
-
const sel = tab?.session.ui?.activeSelector;
|
|
31
|
-
if (!sel || !sel.startsWith(":") || !tab?.session.selection) {
|
|
32
|
-
return;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const panel = getActivePanel();
|
|
36
|
-
if (!panel) {
|
|
37
|
-
return;
|
|
38
|
-
}
|
|
39
|
-
const el = findCanvasElement(tab.session.selection, panel.canvas);
|
|
40
|
-
if (!el) {
|
|
41
|
-
return;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const node = getNodeAtPath(tab.doc.document, tab.session.selection);
|
|
45
|
-
if (!node?.style) {
|
|
46
|
-
return;
|
|
47
|
-
}
|
|
48
|
-
const { activeMedia } = tab.session.ui;
|
|
49
|
-
const ctx: JxStyle = activeMedia
|
|
50
|
-
? (getNestedStyle(node.style, `@${activeMedia}`) ?? {})
|
|
51
|
-
: node.style;
|
|
52
|
-
const rules = getNestedStyle(ctx, sel);
|
|
53
|
-
if (!rules) {
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
const cssProps = Object.entries(rules)
|
|
58
|
-
.filter(([k]) => typeof rules[k] === "string" || typeof rules[k] === "number")
|
|
59
|
-
.map(
|
|
60
|
-
([k, v]) =>
|
|
61
|
-
`${k.replaceAll(/[A-Z]/g, (c: string) => `-${c.toLowerCase()}`)}: ${v} !important`,
|
|
62
|
-
)
|
|
63
|
-
.join("; ");
|
|
64
|
-
if (!cssProps) {
|
|
65
|
-
return;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
el.dataset.studioForced = "1";
|
|
69
|
-
view.forcedAttrEl = el;
|
|
70
|
-
|
|
71
|
-
const tag = document.createElement("style");
|
|
72
|
-
tag.textContent = `[data-studio-forced] { ${cssProps} }`;
|
|
73
|
-
pseudoStyleHost.append(tag);
|
|
74
|
-
view.forcedStyleTag = tag;
|
|
75
|
-
}
|