@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.
Files changed (53) hide show
  1. package/README.md +82 -0
  2. package/dist/studio.js +5114 -3424
  3. package/dist/studio.js.map +65 -49
  4. package/package.json +6 -3
  5. package/src/browse/browse.js +27 -3
  6. package/src/canvas/canvas-diff.js +184 -0
  7. package/src/canvas/canvas-helpers.js +10 -14
  8. package/src/canvas/canvas-live-render.js +136 -17
  9. package/src/canvas/canvas-render.js +154 -21
  10. package/src/canvas/canvas-utils.js +21 -23
  11. package/src/editor/component-inline-edit.js +54 -41
  12. package/src/editor/content-inline-edit.js +46 -47
  13. package/src/editor/context-menu.js +63 -39
  14. package/src/editor/convert-to-component.js +11 -14
  15. package/src/editor/insertion-helper.js +8 -10
  16. package/src/editor/shortcuts.js +69 -39
  17. package/src/files/components.js +15 -4
  18. package/src/files/files.js +72 -24
  19. package/src/panels/activity-bar.js +29 -7
  20. package/src/panels/block-action-bar.js +104 -80
  21. package/src/panels/canvas-dnd.js +132 -50
  22. package/src/panels/dnd.js +32 -28
  23. package/src/panels/editors.js +7 -14
  24. package/src/panels/elements-panel.js +9 -3
  25. package/src/panels/events-panel.js +44 -39
  26. package/src/panels/git-panel.js +45 -3
  27. package/src/panels/layers-panel.js +16 -11
  28. package/src/panels/left-panel.js +108 -43
  29. package/src/panels/overlays.js +97 -35
  30. package/src/panels/panel-events.js +18 -11
  31. package/src/panels/properties-panel.js +467 -98
  32. package/src/panels/right-panel.js +85 -37
  33. package/src/panels/shared.js +0 -22
  34. package/src/panels/signals-panel.js +125 -54
  35. package/src/panels/statusbar.js +23 -8
  36. package/src/panels/style-inputs.js +4 -2
  37. package/src/panels/style-panel.js +128 -105
  38. package/src/panels/stylebook-panel.js +42 -17
  39. package/src/panels/tab-strip.js +124 -0
  40. package/src/panels/toolbar.js +39 -9
  41. package/src/reactivity.js +28 -0
  42. package/src/settings/content-types-editor.js +78 -8
  43. package/src/settings/defs-editor.js +56 -7
  44. package/src/settings/schema-field-ui.js +99 -11
  45. package/src/site-context.js +105 -0
  46. package/src/state.js +0 -456
  47. package/src/store.js +105 -124
  48. package/src/studio.js +112 -121
  49. package/src/tabs/tab.js +153 -0
  50. package/src/tabs/transact.js +406 -0
  51. package/src/ui/spectrum.js +2 -0
  52. package/src/view.js +3 -0
  53. package/src/workspace/workspace.js +89 -0
@@ -5,61 +5,100 @@
5
5
  */
6
6
 
7
7
  import { html, render as litRender, nothing } from "lit-html";
8
- import { getState, updateUi, rightPanel, subscribe } from "../store.js";
8
+ import { updateUi, rightPanel } from "../store.js";
9
+ import { effect, effectScope } from "../reactivity.js";
10
+ import { activeTab } from "../workspace/workspace.js";
9
11
  import { tabIcon } from "./activity-bar.js";
10
12
  import { eventsSidebarTemplate } from "./events-panel.js";
11
13
  import { isCustomElementDoc } from "./signals-panel.js";
12
- import { ensureLitState } from "./shared.js";
14
+
13
15
  import { isColorPopoverOpen } from "../ui/color-selector.js";
14
16
  import { renderStylePanelTemplate } from "./style-panel.js";
15
17
  import { renderPropertiesPanelTemplate } from "./properties-panel.js";
16
18
 
17
- /** @type {any} */
19
+ /**
20
+ * @typedef {{
21
+ * navigateToComponent: (path: string) => void;
22
+ * getCanvasMode: () => string;
23
+ * renderCanvas: () => void;
24
+ * updateForcedPseudoPreview: () => void;
25
+ * }} RightPanelCtx
26
+ */
27
+
28
+ /** @type {RightPanelCtx | null} */
18
29
  let _ctx = null;
19
30
 
20
- /** @type {(() => void) | null} */
21
- let _unsub = null;
31
+ /** @type {import("@vue/reactivity").EffectScope | null} */
32
+ let _scope = null;
33
+
34
+ let _rendering = false;
35
+ let _scheduled = false;
22
36
 
23
37
  /**
24
38
  * Mount the right panel.
25
39
  *
26
- * @param {any} ctx — { propertiesSidebarTemplate, getCanvasMode, renderCanvas,
27
- * updateForcedPseudoPreview }
40
+ * @param {RightPanelCtx} ctx
28
41
  */
29
42
  export function mount(ctx) {
30
43
  _ctx = ctx;
31
- _unsub = subscribe((change) => {
32
- if (!change.selection && !change.ui && !change.doc) return;
33
-
34
- const colorPopoverOpen = isColorPopoverOpen();
35
- const activeTag = document.activeElement?.tagName;
36
- const rightHasFocus =
37
- !colorPopoverOpen &&
38
- rightPanel.contains(document.activeElement) &&
39
- (activeTag === "INPUT" ||
40
- activeTag === "TEXTAREA" ||
41
- activeTag === "SP-TEXTFIELD" ||
42
- activeTag === "SP-NUMBER-FIELD" ||
43
- activeTag === "SP-PICKER" ||
44
- activeTag === "SP-COMBOBOX" ||
45
- activeTag === "SP-SEARCH");
46
-
47
- if (!rightHasFocus || change.selection || change.ui) {
48
- render();
49
- }
44
+ _scope = effectScope();
45
+ _scope.run(() => {
46
+ effect(() => {
47
+ const tab = activeTab.value;
48
+ if (!tab) return;
49
+ // Track properties the right panel reads
50
+ void tab.doc.document;
51
+ void tab.session.selection;
52
+ void tab.session.ui.rightTab;
53
+ void tab.session.ui.activeMedia;
54
+ void tab.session.ui.activeSelector;
55
+ void tab.session.ui.styleSections;
56
+ void tab.session.ui.styleShorthands;
57
+ void tab.session.ui.styleFilter;
58
+ void tab.session.ui.styleFilterActive;
59
+ void tab.session.ui.inspectorSections;
60
+
61
+ const colorPopoverOpen = isColorPopoverOpen();
62
+ const activeTag = document.activeElement?.tagName;
63
+ const rightHasFocus =
64
+ !colorPopoverOpen &&
65
+ rightPanel.contains(document.activeElement) &&
66
+ (activeTag === "INPUT" ||
67
+ activeTag === "TEXTAREA" ||
68
+ activeTag === "SP-TEXTFIELD" ||
69
+ activeTag === "SP-NUMBER-FIELD" ||
70
+ activeTag === "SP-PICKER" ||
71
+ activeTag === "SP-COMBOBOX" ||
72
+ activeTag === "SP-SEARCH");
73
+
74
+ if (!rightHasFocus) {
75
+ render();
76
+ }
77
+ });
50
78
  });
51
79
  }
52
80
 
53
81
  export function unmount() {
54
- _unsub?.();
55
- _unsub = null;
82
+ _scope?.stop();
83
+ _scope = null;
56
84
  _ctx = null;
57
85
  }
58
86
 
59
87
  export function render() {
60
88
  if (!_ctx) return;
89
+ if (_rendering) return;
90
+ if (!_scheduled) {
91
+ _scheduled = true;
92
+ queueMicrotask(_flush);
93
+ }
94
+ }
95
+
96
+ function _flush() {
97
+ _scheduled = false;
98
+ if (!_ctx) return;
99
+ if (_rendering) return;
100
+ _rendering = true;
61
101
  try {
62
- ensureLitState(rightPanel);
63
102
  litRender(rightPanelTemplate(), rightPanel);
64
103
  } catch (e) {
65
104
  console.error("right-panel render error:", e);
@@ -71,12 +110,22 @@ export function render() {
71
110
  } catch (e2) {
72
111
  console.error("right-panel retry failed:", e2);
73
112
  }
113
+ } finally {
114
+ _rendering = false;
74
115
  }
75
116
  _ctx.updateForcedPseudoPreview();
76
117
  }
77
118
 
78
119
  function rightPanelTemplate() {
79
- const S = getState();
120
+ const ctx = /** @type {RightPanelCtx} */ (_ctx);
121
+ const aTab = activeTab.value;
122
+ if (!aTab) return nothing;
123
+ const S = /** @type {any} */ ({
124
+ ui: aTab.session.ui,
125
+ document: aTab.doc.document,
126
+ mode: aTab.doc.mode,
127
+ selection: aTab.session.selection,
128
+ });
80
129
  const tab = S.ui.rightTab;
81
130
 
82
131
  const panelTabs = [
@@ -90,7 +139,7 @@ function rightPanelTemplate() {
90
139
  <sp-tabs
91
140
  selected=${tab}
92
141
  quiet
93
- @change=${(/** @type {any} */ e) => {
142
+ @change=${(/** @type {Event & { target: { selected: string } }} */ e) => {
94
143
  const sel = e.target.selected;
95
144
  if (sel && sel !== tab) {
96
145
  updateUi("rightTab", sel);
@@ -108,19 +157,18 @@ function rightPanelTemplate() {
108
157
  </div>
109
158
  `;
110
159
 
111
- /** @type {any} */
160
+ /** @type {import("lit-html").TemplateResult | typeof nothing} */
112
161
  let bodyT = nothing;
113
162
  if (tab === "properties") {
114
- bodyT = renderPropertiesPanelTemplate({ navigateToComponent: _ctx.navigateToComponent });
163
+ bodyT = renderPropertiesPanelTemplate({ navigateToComponent: ctx.navigateToComponent });
115
164
  } else if (tab === "events") {
116
- bodyT = eventsSidebarTemplate(S, {
165
+ bodyT = eventsSidebarTemplate({
117
166
  isCustomElementDoc: () => isCustomElementDoc(S),
118
- renderCanvas: _ctx.renderCanvas,
119
167
  });
120
168
  } else if (tab === "style") {
121
169
  try {
122
- bodyT = renderStylePanelTemplate({ getCanvasMode: _ctx.getCanvasMode });
123
- } catch (/** @type {any} */ e) {
170
+ bodyT = renderStylePanelTemplate({ getCanvasMode: ctx.getCanvasMode });
171
+ } catch (/** @type {unknown} */ e) {
124
172
  console.error("[renderStylePanelTemplate]", e);
125
173
  }
126
174
  }
@@ -18,28 +18,6 @@ export function mediaDisplayName(name) {
18
18
  );
19
19
  }
20
20
 
21
- /**
22
- * Ensure Lit's internal ChildPart markers are valid. If corrupted, clears the container so Lit
23
- * rebuilds from scratch on the next render.
24
- *
25
- * @param {HTMLElement} container
26
- */
27
- export function ensureLitState(container) {
28
- // @ts-ignore — Lit stores a ChildPart on this private property
29
- const part = container["_$litPart$"];
30
- if (!part) return;
31
- const start = part._$startNode;
32
- const end = part._$endNode;
33
- const startBad = start && start.parentNode !== container;
34
- const endBad = (end && end !== container && end.parentNode !== container) || (!end && start);
35
- if (startBad || endBad) {
36
- console.warn("ensureLitState: clearing corrupted Lit state on", container.id || container);
37
- container.textContent = "";
38
- // @ts-ignore
39
- delete container["_$litPart$"];
40
- }
41
- }
42
-
43
21
  export const unsafeTags = new Set(["script", "style", "link", "iframe", "object", "embed"]);
44
22
 
45
23
  /**