@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
|
@@ -8,6 +8,7 @@ import { ref } from "lit-html/directives/ref.js";
|
|
|
8
8
|
import * as monaco from "monaco-editor/esm/vs/editor/editor.api.js";
|
|
9
9
|
|
|
10
10
|
import { canvasWrap, canvasPanels, updateCanvas } from "../store.js";
|
|
11
|
+
import { activeTab } from "../workspace/workspace.js";
|
|
11
12
|
import { view } from "../view.js";
|
|
12
13
|
import {
|
|
13
14
|
canvasPanelTemplate,
|
|
@@ -29,6 +30,7 @@ import { renderCanvasLive } from "./canvas-live-render.js";
|
|
|
29
30
|
import { renderCanvasNode } from "../panels/preview-render.js";
|
|
30
31
|
import { registerPanelDnD } from "../panels/canvas-dnd.js";
|
|
31
32
|
import { registerPanelEvents } from "../panels/panel-events.js";
|
|
33
|
+
import { computeDocumentDiff } from "./canvas-diff.js";
|
|
32
34
|
import { updateForcedPseudoPreview } from "../panels/pseudo-preview.js";
|
|
33
35
|
import { renderStylebookMode } from "../panels/stylebook-panel.js";
|
|
34
36
|
import { dismissLinkPopover, dismissBlockActionBar } from "../panels/block-action-bar.js";
|
|
@@ -49,10 +51,10 @@ let _ctx = null;
|
|
|
49
51
|
* @param {{
|
|
50
52
|
* getCanvasMode: () => string;
|
|
51
53
|
* setCanvasMode: (mode: string) => void;
|
|
52
|
-
* getState: () => any;
|
|
53
|
-
* update: (s: any) => void;
|
|
54
54
|
* openFileFromTree: (path: string) => void;
|
|
55
55
|
* exportFile: () => void;
|
|
56
|
+
* gitDiffState: any;
|
|
57
|
+
* setGitDiffState: (state: any) => void;
|
|
56
58
|
* }} ctx
|
|
57
59
|
*/
|
|
58
60
|
export function initCanvasRender(ctx) {
|
|
@@ -60,7 +62,9 @@ export function initCanvasRender(ctx) {
|
|
|
60
62
|
}
|
|
61
63
|
|
|
62
64
|
export function renderCanvas() {
|
|
63
|
-
const
|
|
65
|
+
const tab = activeTab.value;
|
|
66
|
+
if (!tab) return;
|
|
67
|
+
const S = { document: tab.doc.document, ui: tab.session.ui, mode: tab.doc.mode };
|
|
64
68
|
const canvasMode = _ctx.getCanvasMode();
|
|
65
69
|
|
|
66
70
|
// Advance render generation so stale async renders from the previous cycle bail out
|
|
@@ -230,7 +234,9 @@ export function renderCanvas() {
|
|
|
230
234
|
debounce = setTimeout(() => {
|
|
231
235
|
try {
|
|
232
236
|
const parsed = JSON.parse(view.monacoEditor.getValue());
|
|
233
|
-
|
|
237
|
+
const tab = activeTab.value;
|
|
238
|
+
tab.doc.document = parsed;
|
|
239
|
+
tab.doc.dirty = true;
|
|
234
240
|
} catch {
|
|
235
241
|
// Invalid JSON — don't update state
|
|
236
242
|
}
|
|
@@ -239,6 +245,75 @@ export function renderCanvas() {
|
|
|
239
245
|
return;
|
|
240
246
|
}
|
|
241
247
|
|
|
248
|
+
// Git diff mode — render original (left) and current (right) side-by-side
|
|
249
|
+
if (canvasMode === "git-diff") {
|
|
250
|
+
if (!_ctx.gitDiffState) {
|
|
251
|
+
// Fallback to design mode if diff state is missing
|
|
252
|
+
_ctx.setCanvasMode("design");
|
|
253
|
+
renderCanvas();
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
canvasWrap.style.padding = "0";
|
|
258
|
+
canvasWrap.style.overflow = "hidden";
|
|
259
|
+
|
|
260
|
+
const gitDiffState = _ctx.gitDiffState;
|
|
261
|
+
const { tpl: origPanelTpl, panel: origPanel } = canvasPanelTemplate(
|
|
262
|
+
"git-diff-original",
|
|
263
|
+
"Original",
|
|
264
|
+
false,
|
|
265
|
+
"50%",
|
|
266
|
+
);
|
|
267
|
+
const { tpl: currPanelTpl, panel: currPanel } = canvasPanelTemplate(
|
|
268
|
+
"git-diff-current",
|
|
269
|
+
"Current",
|
|
270
|
+
false,
|
|
271
|
+
"50%",
|
|
272
|
+
);
|
|
273
|
+
|
|
274
|
+
const diffTpl = html`
|
|
275
|
+
<div style="display: flex; height: 100%; gap: 0;">
|
|
276
|
+
<div style="flex: 1; overflow: hidden; display: flex; flex-direction: column;">
|
|
277
|
+
${origPanelTpl}
|
|
278
|
+
</div>
|
|
279
|
+
<div style="flex: 1; overflow: hidden; display: flex; flex-direction: column;">
|
|
280
|
+
${currPanelTpl}
|
|
281
|
+
</div>
|
|
282
|
+
</div>
|
|
283
|
+
`;
|
|
284
|
+
|
|
285
|
+
litRender(diffTpl, canvasWrap);
|
|
286
|
+
canvasPanels.push(/** @type {any} */ (origPanel));
|
|
287
|
+
canvasPanels.push(/** @type {any} */ (currPanel));
|
|
288
|
+
|
|
289
|
+
// Parse original document from git content
|
|
290
|
+
let originalDoc = null;
|
|
291
|
+
try {
|
|
292
|
+
if (gitDiffState.isMarkdown) {
|
|
293
|
+
// For markdown, we need to convert it to Jx format
|
|
294
|
+
// For now, use a simple placeholder until we have markdown parsing
|
|
295
|
+
originalDoc = {
|
|
296
|
+
tagName: "div",
|
|
297
|
+
children: [{ tagName: "p", textContent: "Original (markdown)" }],
|
|
298
|
+
};
|
|
299
|
+
} else {
|
|
300
|
+
// Parse as JSON
|
|
301
|
+
originalDoc = JSON.parse(gitDiffState.originalContent);
|
|
302
|
+
}
|
|
303
|
+
} catch {
|
|
304
|
+
originalDoc = {
|
|
305
|
+
tagName: "div",
|
|
306
|
+
children: [{ tagName: "p", textContent: "Failed to parse original" }],
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
const currDoc = S.document;
|
|
311
|
+
|
|
312
|
+
renderCanvasIntoPanel(origPanel, new Set(), S.ui.featureToggles, originalDoc, gitDiffState);
|
|
313
|
+
renderCanvasIntoPanel(currPanel, new Set(), S.ui.featureToggles, currDoc, gitDiffState);
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
|
|
242
317
|
// Edit (content) mode — centered column, no panzoom, always 100%
|
|
243
318
|
if (canvasMode === "edit") {
|
|
244
319
|
if (modeChanged) {
|
|
@@ -249,14 +324,15 @@ export function renderCanvas() {
|
|
|
249
324
|
resetZoomIndicator();
|
|
250
325
|
}
|
|
251
326
|
|
|
327
|
+
const { baseWidth } = parseMediaEntries(getEffectiveMedia(S.document.$media));
|
|
252
328
|
const { tpl: panelTpl, panel } = canvasPanelTemplate(null, null, true);
|
|
253
329
|
const editTpl = html`
|
|
254
330
|
<div class="content-edit-canvas">
|
|
255
|
-
<div class="content-edit-column">${panelTpl}</div>
|
|
331
|
+
<div class="content-edit-column" style="max-width:${baseWidth}px">${panelTpl}</div>
|
|
256
332
|
</div>
|
|
257
333
|
`;
|
|
258
334
|
litRender(editTpl, canvasWrap);
|
|
259
|
-
canvasPanels.push(panel);
|
|
335
|
+
canvasPanels.push(/** @type {any} */ (panel));
|
|
260
336
|
renderCanvasIntoPanel(panel, new Set(), S.ui.featureToggles);
|
|
261
337
|
return;
|
|
262
338
|
}
|
|
@@ -301,7 +377,7 @@ export function renderCanvas() {
|
|
|
301
377
|
`,
|
|
302
378
|
canvasWrap,
|
|
303
379
|
);
|
|
304
|
-
canvasPanels.push(panel);
|
|
380
|
+
canvasPanels.push(/** @type {any} */ (panel));
|
|
305
381
|
renderCanvasIntoPanel(panel, new Set(), featureToggles);
|
|
306
382
|
applyTransform();
|
|
307
383
|
if (modeChanged) {
|
|
@@ -353,7 +429,7 @@ export function renderCanvas() {
|
|
|
353
429
|
);
|
|
354
430
|
|
|
355
431
|
for (const { panel, activeSet } of panelEntries) {
|
|
356
|
-
canvasPanels.push(panel);
|
|
432
|
+
canvasPanels.push(/** @type {any} */ (panel));
|
|
357
433
|
renderCanvasIntoPanel(panel, activeSet, featureToggles);
|
|
358
434
|
}
|
|
359
435
|
|
|
@@ -375,29 +451,51 @@ export function renderCanvas() {
|
|
|
375
451
|
* structural preview.
|
|
376
452
|
*
|
|
377
453
|
* @param {any} panel
|
|
378
|
-
* @param {
|
|
454
|
+
* @param {Set<string>} activeBreakpoints
|
|
379
455
|
* @param {any} featureToggles
|
|
456
|
+
* @param {any} [docOverride] - Optional document to render (for diff mode). Uses active tab doc if
|
|
457
|
+
* not provided.
|
|
458
|
+
* @param {any} [gitDiffState] - Optional diff state. If provided, computes and applies diff
|
|
459
|
+
* highlighting.
|
|
380
460
|
*/
|
|
381
|
-
function renderCanvasIntoPanel(
|
|
461
|
+
function renderCanvasIntoPanel(
|
|
462
|
+
panel,
|
|
463
|
+
activeBreakpoints,
|
|
464
|
+
featureToggles,
|
|
465
|
+
docOverride = null,
|
|
466
|
+
gitDiffState = null,
|
|
467
|
+
) {
|
|
382
468
|
const gen = view.renderGeneration;
|
|
383
|
-
const
|
|
384
|
-
|
|
469
|
+
const tab = activeTab.value;
|
|
470
|
+
const docToRender = docOverride || tab?.doc.document;
|
|
471
|
+
|
|
472
|
+
renderCanvasLive(gen, docToRender, panel.canvas).then((/** @type {any} */ scope) => {
|
|
385
473
|
// Skip post-render setup if a newer render has started
|
|
386
474
|
if (gen !== view.renderGeneration) return;
|
|
387
475
|
if (scope) {
|
|
388
476
|
updateCanvas({ status: "ready", scope, error: null });
|
|
389
477
|
applyCanvasMediaOverrides(panel.canvas, activeBreakpoints);
|
|
390
478
|
statusMessage("Runtime render OK", 1500);
|
|
479
|
+
|
|
480
|
+
// Apply diff highlighting if in git-diff mode
|
|
481
|
+
if (gitDiffState && docOverride) {
|
|
482
|
+
// Determine which document is original and which is current
|
|
483
|
+
const isOriginal = docOverride === (gitDiffState.originalDoc || gitDiffState.original);
|
|
484
|
+
const origDoc = isOriginal ? docOverride : gitDiffState.currentDoc || tab?.doc.document;
|
|
485
|
+
const currDoc = isOriginal ? gitDiffState.currentDoc || tab?.doc.document : docOverride;
|
|
486
|
+
|
|
487
|
+
const { byPath: diffMap } = computeDocumentDiff(origDoc, currDoc);
|
|
488
|
+
|
|
489
|
+
// Can't iterate WeakMap, so apply styling by walking the canvas
|
|
490
|
+
const { elToPath } = scope;
|
|
491
|
+
if (elToPath instanceof WeakMap) {
|
|
492
|
+
applyDiffHighlightToCanvas(panel.canvas, diffMap);
|
|
493
|
+
}
|
|
494
|
+
}
|
|
391
495
|
} else {
|
|
392
496
|
// Fallback to structural preview
|
|
393
497
|
updateCanvas({ status: "ready", scope: null, error: null });
|
|
394
|
-
renderCanvasNode(
|
|
395
|
-
_ctx.getState().document,
|
|
396
|
-
[],
|
|
397
|
-
panel.canvas,
|
|
398
|
-
activeBreakpoints,
|
|
399
|
-
featureToggles,
|
|
400
|
-
);
|
|
498
|
+
renderCanvasNode(docToRender, [], panel.canvas, activeBreakpoints, featureToggles);
|
|
401
499
|
}
|
|
402
500
|
registerPanelDnD(panel);
|
|
403
501
|
registerPanelEvents(panel);
|
|
@@ -406,6 +504,40 @@ function renderCanvasIntoPanel(panel, activeBreakpoints, featureToggles) {
|
|
|
406
504
|
});
|
|
407
505
|
}
|
|
408
506
|
|
|
507
|
+
/**
|
|
508
|
+
* Apply diff highlighting to canvas elements based on elToPath mapping. Walks the canvas DOM and
|
|
509
|
+
* applies classes based on diff status.
|
|
510
|
+
*
|
|
511
|
+
* @param {HTMLElement} canvas
|
|
512
|
+
* @param {Map<string, "added" | "removed" | "modified">} diffMap
|
|
513
|
+
*/
|
|
514
|
+
function applyDiffHighlightToCanvas(canvas, diffMap) {
|
|
515
|
+
if (!diffMap || diffMap.size === 0) return;
|
|
516
|
+
|
|
517
|
+
// Walk all elements in canvas and check their data attributes or other markers
|
|
518
|
+
const walkCanvas = (/** @type {HTMLElement} */ el, /** @type {string} */ path = "") => {
|
|
519
|
+
const pathKey = path || "/";
|
|
520
|
+
|
|
521
|
+
if (diffMap.has(pathKey)) {
|
|
522
|
+
const status = diffMap.get(pathKey);
|
|
523
|
+
if (status === "added") el.classList.add("element-diff-added");
|
|
524
|
+
else if (status === "removed") el.classList.add("element-diff-removed");
|
|
525
|
+
else if (status === "modified") el.classList.add("element-diff-modified");
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
// Check for child elements (heuristic: children array markers)
|
|
529
|
+
let childIdx = 0;
|
|
530
|
+
for (const child of el.children) {
|
|
531
|
+
const childPath =
|
|
532
|
+
pathKey === "/" ? `children/${childIdx}` : `${pathKey}/children/${childIdx}`;
|
|
533
|
+
walkCanvas(/** @type {HTMLElement} */ (child), childPath);
|
|
534
|
+
childIdx++;
|
|
535
|
+
}
|
|
536
|
+
};
|
|
537
|
+
|
|
538
|
+
walkCanvas(canvas, "");
|
|
539
|
+
}
|
|
540
|
+
|
|
409
541
|
/**
|
|
410
542
|
* Apply media query overrides as inline styles on matching canvas elements. Needed because the
|
|
411
543
|
* runtime renders base styles as inline — @media CSS rules in the injected stylesheet can't win
|
|
@@ -416,8 +548,9 @@ function renderCanvasIntoPanel(panel, activeBreakpoints, featureToggles) {
|
|
|
416
548
|
*/
|
|
417
549
|
function applyCanvasMediaOverrides(canvasEl, activeBreakpoints) {
|
|
418
550
|
if (!activeBreakpoints.size) return;
|
|
419
|
-
const
|
|
420
|
-
|
|
551
|
+
const tab = activeTab.value;
|
|
552
|
+
if (!tab) return;
|
|
553
|
+
const docMedia = getEffectiveMedia(tab.doc.document.$media || {});
|
|
421
554
|
const validBreakpoints = new Set();
|
|
422
555
|
for (const name of activeBreakpoints) {
|
|
423
556
|
if (docMedia[name]) validBreakpoints.add(name);
|
|
@@ -8,8 +8,8 @@ import { ref } from "lit-html/directives/ref.js";
|
|
|
8
8
|
import { styleMap } from "lit-html/directives/style-map.js";
|
|
9
9
|
import { ifDefined } from "lit-html/directives/if-defined.js";
|
|
10
10
|
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
11
|
+
import { renderOnly, updateUi, canvasWrap, canvasPanels } from "../store.js";
|
|
12
|
+
import { activeTab } from "../workspace/workspace.js";
|
|
13
13
|
import { view } from "../view.js";
|
|
14
14
|
|
|
15
15
|
/** @type {any} */
|
|
@@ -44,14 +44,14 @@ export function initCanvasUtils(ctx) {
|
|
|
44
44
|
export function canvasPanelTemplate(mediaName, label, fullWidth, width = null) {
|
|
45
45
|
/**
|
|
46
46
|
* @type {{
|
|
47
|
-
* mediaName:
|
|
48
|
-
* element:
|
|
49
|
-
* canvas:
|
|
50
|
-
* overlay:
|
|
51
|
-
* overlayClk:
|
|
52
|
-
* viewport:
|
|
53
|
-
* dropLine:
|
|
54
|
-
* _width:
|
|
47
|
+
* mediaName: string;
|
|
48
|
+
* element: HTMLElement | null;
|
|
49
|
+
* canvas: HTMLElement | null;
|
|
50
|
+
* overlay: HTMLElement | null;
|
|
51
|
+
* overlayClk: HTMLElement | null;
|
|
52
|
+
* viewport: HTMLElement | null;
|
|
53
|
+
* dropLine: HTMLElement | null;
|
|
54
|
+
* _width: number | null;
|
|
55
55
|
* }}
|
|
56
56
|
*/
|
|
57
57
|
const panel = {
|
|
@@ -69,7 +69,7 @@ export function canvasPanelTemplate(mediaName, label, fullWidth, width = null) {
|
|
|
69
69
|
class=${`canvas-panel${fullWidth ? " full-width" : ""}`}
|
|
70
70
|
data-media=${ifDefined(mediaName !== null ? mediaName : undefined)}
|
|
71
71
|
${ref((el) => {
|
|
72
|
-
if (el) panel.element = el;
|
|
72
|
+
if (el) panel.element = /** @type {HTMLElement} */ (el);
|
|
73
73
|
})}
|
|
74
74
|
>
|
|
75
75
|
${label
|
|
@@ -88,34 +88,34 @@ export function canvasPanelTemplate(mediaName, label, fullWidth, width = null) {
|
|
|
88
88
|
class="canvas-panel-viewport"
|
|
89
89
|
style=${styleMap({ width: width && !fullWidth ? `${width}px` : "" })}
|
|
90
90
|
${ref((el) => {
|
|
91
|
-
if (el) panel.viewport = el;
|
|
91
|
+
if (el) panel.viewport = /** @type {HTMLElement} */ (el);
|
|
92
92
|
})}
|
|
93
93
|
>
|
|
94
94
|
<div
|
|
95
95
|
class="canvas-panel-canvas"
|
|
96
96
|
style=${styleMap({ width: width ? `${width}px` : "" })}
|
|
97
97
|
${ref((el) => {
|
|
98
|
-
if (el) panel.canvas = el;
|
|
98
|
+
if (el) panel.canvas = /** @type {HTMLElement} */ (el);
|
|
99
99
|
})}
|
|
100
100
|
></div>
|
|
101
101
|
<div
|
|
102
102
|
class="canvas-panel-overlay"
|
|
103
103
|
${ref((el) => {
|
|
104
|
-
if (el) panel.overlay = el;
|
|
104
|
+
if (el) panel.overlay = /** @type {HTMLElement} */ (el);
|
|
105
105
|
})}
|
|
106
106
|
>
|
|
107
107
|
<div
|
|
108
108
|
class="canvas-drop-indicator"
|
|
109
109
|
style="display:none"
|
|
110
110
|
${ref((el) => {
|
|
111
|
-
if (el) panel.dropLine = el;
|
|
111
|
+
if (el) panel.dropLine = /** @type {HTMLElement} */ (el);
|
|
112
112
|
})}
|
|
113
113
|
></div>
|
|
114
114
|
</div>
|
|
115
115
|
<div
|
|
116
116
|
class="canvas-panel-click"
|
|
117
117
|
${ref((el) => {
|
|
118
|
-
if (el) panel.overlayClk = el;
|
|
118
|
+
if (el) panel.overlayClk = /** @type {HTMLElement} */ (el);
|
|
119
119
|
})}
|
|
120
120
|
></div>
|
|
121
121
|
</div>
|
|
@@ -207,7 +207,6 @@ export function fitToScreen() {
|
|
|
207
207
|
|
|
208
208
|
/** Reset the zoom indicator (clear its content). Called when switching to non-panzoom modes. */
|
|
209
209
|
export function resetZoomIndicator() {
|
|
210
|
-
ensureLitState(zoomIndicatorHost);
|
|
211
210
|
litRender(nothing, zoomIndicatorHost);
|
|
212
211
|
}
|
|
213
212
|
|
|
@@ -220,7 +219,6 @@ export function renderZoomIndicator() {
|
|
|
220
219
|
if (!zoomIndicatorHost.isConnected) {
|
|
221
220
|
document.body.appendChild(zoomIndicatorHost);
|
|
222
221
|
}
|
|
223
|
-
ensureLitState(zoomIndicatorHost);
|
|
224
222
|
litRender(
|
|
225
223
|
html`
|
|
226
224
|
<div class="zoom-indicator">
|
|
@@ -263,14 +261,14 @@ export function positionZoomIndicator() {
|
|
|
263
261
|
|
|
264
262
|
/** Toggle "active" class on canvas panel headers based on activeMedia. */
|
|
265
263
|
export function updateActivePanelHeaders() {
|
|
266
|
-
const
|
|
264
|
+
const activeMedia = activeTab.value?.session.ui.activeMedia ?? null;
|
|
267
265
|
for (const p of canvasPanels) {
|
|
268
|
-
const header = p.element
|
|
266
|
+
const header = p.element?.querySelector(".canvas-panel-header");
|
|
269
267
|
if (header) {
|
|
270
268
|
const isActive =
|
|
271
|
-
(
|
|
272
|
-
(
|
|
273
|
-
|
|
269
|
+
(activeMedia === null && p.mediaName === "base") ||
|
|
270
|
+
(activeMedia === null && p.mediaName === null) ||
|
|
271
|
+
activeMedia === p.mediaName;
|
|
274
272
|
header.classList.toggle("active", isActive);
|
|
275
273
|
}
|
|
276
274
|
}
|
|
@@ -5,19 +5,21 @@
|
|
|
5
5
|
|
|
6
6
|
import {
|
|
7
7
|
getState,
|
|
8
|
-
update,
|
|
9
8
|
updateUi,
|
|
10
9
|
renderOnly,
|
|
11
10
|
getNodeAtPath,
|
|
12
|
-
selectNode,
|
|
13
|
-
removeNode,
|
|
14
|
-
insertNode,
|
|
15
|
-
updateProperty,
|
|
16
11
|
parentElementPath,
|
|
17
12
|
childIndex,
|
|
18
13
|
canvasPanels,
|
|
19
14
|
elToPath,
|
|
20
15
|
} from "../store.js";
|
|
16
|
+
import { activeTab } from "../workspace/workspace.js";
|
|
17
|
+
import {
|
|
18
|
+
transactDoc,
|
|
19
|
+
mutateRemoveNode,
|
|
20
|
+
mutateInsertNode,
|
|
21
|
+
mutateUpdateProperty,
|
|
22
|
+
} from "../tabs/transact.js";
|
|
21
23
|
import { view } from "../view.js";
|
|
22
24
|
import { isSlashMenuOpen, showSlashMenu, dismissSlashMenu } from "./slash-menu.js";
|
|
23
25
|
import { renderBlockActionBar } from "../panels/block-action-bar.js";
|
|
@@ -59,7 +61,9 @@ export function enterComponentInlineEdit(el, path) {
|
|
|
59
61
|
if (voids.has(node.tagName)) return;
|
|
60
62
|
|
|
61
63
|
for (const p of canvasPanels) {
|
|
62
|
-
const boxes =
|
|
64
|
+
const boxes = /** @type {NodeListOf<HTMLElement>} */ (
|
|
65
|
+
p.overlay.querySelectorAll(".overlay-box")
|
|
66
|
+
);
|
|
63
67
|
for (const box of boxes) {
|
|
64
68
|
box.style.border = "none";
|
|
65
69
|
}
|
|
@@ -104,10 +108,11 @@ export function enterComponentInlineEdit(el, path) {
|
|
|
104
108
|
if (view.blockActionBarEl && view.blockActionBarEl.contains(evt.target)) return;
|
|
105
109
|
document.removeEventListener("mousedown", outsideHandler, true);
|
|
106
110
|
|
|
111
|
+
/** @type {(string | number)[] | null} */
|
|
107
112
|
let hitPath = null,
|
|
108
113
|
hitMedia = null;
|
|
109
114
|
for (const p of canvasPanels) {
|
|
110
|
-
const els = p.canvas.querySelectorAll("*");
|
|
115
|
+
const els = /** @type {NodeListOf<HTMLElement>} */ (p.canvas.querySelectorAll("*"));
|
|
111
116
|
for (const el of els) el.style.pointerEvents = "auto";
|
|
112
117
|
p.overlayClk.style.display = "none";
|
|
113
118
|
const found = document.elementsFromPoint(evt.clientX, evt.clientY);
|
|
@@ -132,37 +137,44 @@ export function enterComponentInlineEdit(el, path) {
|
|
|
132
137
|
|
|
133
138
|
const isEmpty = !newText;
|
|
134
139
|
const pPath = parentElementPath(editPath);
|
|
135
|
-
const S = getState();
|
|
136
140
|
|
|
137
141
|
if (hitPath) {
|
|
142
|
+
let hp = hitPath;
|
|
138
143
|
const media = hitMedia === "base" ? null : (hitMedia ?? null);
|
|
139
|
-
updateUi("pendingInlineEdit", { path:
|
|
140
|
-
|
|
144
|
+
updateUi("pendingInlineEdit", { path: hp, mediaName: hitMedia });
|
|
145
|
+
activeTab.value.session.ui.activeMedia = media;
|
|
141
146
|
if (isEmpty && pPath) {
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
147
|
+
transactDoc(activeTab.value, (t) => {
|
|
148
|
+
mutateRemoveNode(t, editPath);
|
|
149
|
+
const removedIdx = /** @type {number} */ (childIndex(editPath));
|
|
150
|
+
const hitIdx = /** @type {number} */ (childIndex(hp));
|
|
151
|
+
const hitParent = parentElementPath(hp);
|
|
152
|
+
if (
|
|
153
|
+
hitParent &&
|
|
154
|
+
pPath &&
|
|
155
|
+
hitParent.join("/") === pPath.join("/") &&
|
|
156
|
+
hitIdx > removedIdx
|
|
157
|
+
) {
|
|
158
|
+
hp = [...pPath, "children", hitIdx - 1];
|
|
159
|
+
updateUi("pendingInlineEdit", { path: hp, mediaName: hitMedia });
|
|
160
|
+
}
|
|
161
|
+
t.session.selection = hp;
|
|
162
|
+
});
|
|
151
163
|
} else if (newText !== originalText) {
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
),
|
|
157
|
-
);
|
|
164
|
+
transactDoc(activeTab.value, (t) => {
|
|
165
|
+
mutateUpdateProperty(t, editPath, "textContent", newText || undefined);
|
|
166
|
+
t.session.selection = hp;
|
|
167
|
+
});
|
|
158
168
|
} else {
|
|
159
|
-
|
|
169
|
+
activeTab.value.session.selection = hp;
|
|
160
170
|
}
|
|
161
171
|
} else {
|
|
162
172
|
if (isEmpty && pPath) {
|
|
163
|
-
|
|
173
|
+
transactDoc(activeTab.value, (t) => mutateRemoveNode(t, editPath));
|
|
164
174
|
} else if (newText !== originalText) {
|
|
165
|
-
|
|
175
|
+
transactDoc(activeTab.value, (t) =>
|
|
176
|
+
mutateUpdateProperty(t, editPath, "textContent", newText || undefined),
|
|
177
|
+
);
|
|
166
178
|
} else {
|
|
167
179
|
renderOnly("canvas");
|
|
168
180
|
renderOnly("overlays");
|
|
@@ -219,13 +231,13 @@ function splitParagraph() {
|
|
|
219
231
|
|
|
220
232
|
cleanupComponentInlineEdit(el);
|
|
221
233
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
234
|
+
transactDoc(activeTab.value, (t) => {
|
|
235
|
+
mutateUpdateProperty(t, path, "textContent", textBefore || undefined);
|
|
236
|
+
mutateInsertNode(t, pPath, idx + 1, newDef);
|
|
237
|
+
t.session.selection = newPath;
|
|
238
|
+
});
|
|
226
239
|
|
|
227
240
|
updateUi("pendingInlineEdit", { path: newPath, mediaName });
|
|
228
|
-
update(s);
|
|
229
241
|
}
|
|
230
242
|
|
|
231
243
|
function _commitComponentInlineEdit() {
|
|
@@ -235,12 +247,13 @@ function _commitComponentInlineEdit() {
|
|
|
235
247
|
|
|
236
248
|
cleanupComponentInlineEdit(el);
|
|
237
249
|
|
|
238
|
-
const S = getState();
|
|
239
250
|
const pPath = parentElementPath(path);
|
|
240
251
|
if (!newText && pPath) {
|
|
241
|
-
|
|
252
|
+
transactDoc(activeTab.value, (t) => mutateRemoveNode(t, path));
|
|
242
253
|
} else if (newText !== originalText) {
|
|
243
|
-
|
|
254
|
+
transactDoc(activeTab.value, (t) =>
|
|
255
|
+
mutateUpdateProperty(t, path, "textContent", newText || undefined),
|
|
256
|
+
);
|
|
244
257
|
} else {
|
|
245
258
|
renderOnly("canvas");
|
|
246
259
|
renderOnly("overlays");
|
|
@@ -303,15 +316,15 @@ function handleComponentSlashSelect(cmd) {
|
|
|
303
316
|
|
|
304
317
|
cleanupComponentInlineEdit(el);
|
|
305
318
|
|
|
306
|
-
const S = getState();
|
|
307
319
|
const newDef = defaultDef(cmd.tag);
|
|
308
320
|
const newPath = [...pPath, "children", idx];
|
|
309
321
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
322
|
+
transactDoc(activeTab.value, (t) => {
|
|
323
|
+
mutateRemoveNode(t, path);
|
|
324
|
+
mutateInsertNode(t, pPath, idx, newDef);
|
|
325
|
+
t.session.selection = newPath;
|
|
326
|
+
});
|
|
313
327
|
|
|
314
328
|
const hasText = newDef.textContent != null;
|
|
315
329
|
if (hasText) updateUi("pendingInlineEdit", { path: newPath, mediaName });
|
|
316
|
-
update(s);
|
|
317
330
|
}
|