@jxsuite/studio 0.23.2 → 0.25.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 (106) hide show
  1. package/dist/studio.js +2311 -718
  2. package/dist/studio.js.map +121 -104
  3. package/package.json +8 -7
  4. package/src/browse/{browse-modal.js → browse-modal.ts} +9 -10
  5. package/src/browse/{browse.js → browse.ts} +109 -84
  6. package/src/canvas/{canvas-diff.js → canvas-diff.ts} +42 -35
  7. package/src/canvas/{canvas-helpers.js → canvas-helpers.ts} +25 -19
  8. package/src/canvas/{canvas-live-render.js → canvas-live-render.ts} +53 -63
  9. package/src/canvas/{canvas-render.js → canvas-render.ts} +110 -121
  10. package/src/canvas/{canvas-utils.js → canvas-utils.ts} +48 -44
  11. package/src/canvas/{nested-site-style.js → nested-site-style.ts} +3 -3
  12. package/src/editor/{component-inline-edit.js → component-inline-edit.ts} +45 -33
  13. package/src/editor/{content-inline-edit.js → content-inline-edit.ts} +27 -45
  14. package/src/editor/{context-menu.js → context-menu.ts} +38 -36
  15. package/src/editor/{convert-targets.js → convert-targets.ts} +15 -12
  16. package/src/editor/{convert-to-component.js → convert-to-component.ts} +25 -26
  17. package/src/editor/{convert-to-repeater.js → convert-to-repeater.ts} +60 -40
  18. package/src/editor/{inline-edit.js → inline-edit.ts} +91 -55
  19. package/src/editor/{inline-format.js → inline-format.ts} +46 -51
  20. package/src/editor/{insertion-helper.js → insertion-helper.ts} +47 -63
  21. package/src/editor/{shortcuts.js → shortcuts.ts} +45 -31
  22. package/src/editor/{slash-menu.js → slash-menu.ts} +34 -35
  23. package/src/files/{components.js → components.ts} +12 -15
  24. package/src/files/{file-ops.js → file-ops.ts} +36 -29
  25. package/src/files/{files.js → files.ts} +102 -84
  26. package/src/github/{github-auth.js → github-auth.ts} +3 -3
  27. package/src/github/{github-publish.js → github-publish.ts} +20 -18
  28. package/src/markdown/{md-allowlist.js → md-allowlist.ts} +5 -2
  29. package/src/markdown/{md-convert.js → md-convert.ts} +146 -169
  30. package/src/new-project/{new-project-modal.js → new-project-modal.ts} +16 -18
  31. package/src/panels/{activity-bar.js → activity-bar.ts} +26 -34
  32. package/src/panels/{ai-panel.js → ai-panel.ts} +30 -30
  33. package/src/panels/{block-action-bar.js → block-action-bar.ts} +64 -73
  34. package/src/panels/{canvas-dnd.js → canvas-dnd.ts} +44 -49
  35. package/src/panels/{data-explorer.js → data-explorer.ts} +21 -15
  36. package/src/panels/dnd.ts +417 -0
  37. package/src/panels/{editors.js → editors.ts} +43 -50
  38. package/src/panels/{elements-panel.js → elements-panel.ts} +30 -27
  39. package/src/panels/{events-panel.js → events-panel.ts} +33 -25
  40. package/src/panels/{git-panel.js → git-panel.ts} +97 -83
  41. package/src/panels/{head-panel.js → head-panel.ts} +117 -139
  42. package/src/panels/{imports-panel.js → imports-panel.ts} +69 -89
  43. package/src/panels/{layers-panel.js → layers-panel.ts} +61 -57
  44. package/src/panels/{left-panel.js → left-panel.ts} +56 -63
  45. package/src/panels/{overlays.js → overlays.ts} +28 -36
  46. package/src/panels/{panel-events.js → panel-events.ts} +53 -51
  47. package/src/panels/{preview-render.js → preview-render.ts} +20 -15
  48. package/src/panels/{properties-panel.js → properties-panel.ts} +212 -246
  49. package/src/panels/{pseudo-preview.js → pseudo-preview.ts} +9 -8
  50. package/src/panels/{quick-search.js → quick-search.ts} +16 -17
  51. package/src/panels/{right-panel.js → right-panel.ts} +41 -59
  52. package/src/panels/{shared.js → shared.ts} +8 -6
  53. package/src/panels/{signals-panel.js → signals-panel.ts} +322 -362
  54. package/src/panels/{statusbar.js → statusbar.ts} +14 -14
  55. package/src/panels/{style-inputs.js → style-inputs.ts} +66 -48
  56. package/src/panels/{style-panel.js → style-panel.ts} +235 -290
  57. package/src/panels/{style-utils.js → style-utils.ts} +32 -31
  58. package/src/panels/{stylebook-layers-panel.js → stylebook-layers-panel.ts} +22 -14
  59. package/src/panels/{stylebook-panel.js → stylebook-panel.ts} +127 -140
  60. package/src/panels/{tab-strip.js → tab-strip.ts} +12 -14
  61. package/src/panels/{toolbar.js → toolbar.ts} +68 -70
  62. package/src/panels/{welcome-screen.js → welcome-screen.ts} +14 -16
  63. package/src/{platform.js → platform.ts} +5 -3
  64. package/src/platforms/{devserver.js → devserver.ts} +53 -44
  65. package/src/{recent-projects.js → recent-projects.ts} +17 -12
  66. package/src/{resize-edges.js → resize-edges.ts} +18 -13
  67. package/src/services/{cem-export.js → cem-export.ts} +14 -7
  68. package/src/services/{code-services.js → code-services.ts} +29 -20
  69. package/src/services/{monaco-setup.js → monaco-setup.ts} +2 -3
  70. package/src/settings/{content-types-editor.js → content-types-editor.ts} +88 -67
  71. package/src/settings/{css-vars-editor.js → css-vars-editor.ts} +78 -51
  72. package/src/settings/{defs-editor.js → defs-editor.ts} +70 -56
  73. package/src/settings/{general-settings.js → general-settings.ts} +29 -28
  74. package/src/settings/{head-editor.js → head-editor.ts} +37 -35
  75. package/src/settings/{schema-field-ui.js → schema-field-ui.ts} +86 -76
  76. package/src/settings/{settings-modal.js → settings-modal.ts} +14 -16
  77. package/src/{site-context.js → site-context.ts} +35 -32
  78. package/src/{state.js → state.ts} +95 -78
  79. package/src/{store.js → store.ts} +60 -63
  80. package/src/{studio.js → studio.ts} +117 -119
  81. package/src/tabs/tab.ts +196 -0
  82. package/src/tabs/{transact.js → transact.ts} +107 -60
  83. package/src/types.ts +185 -0
  84. package/src/ui/{button-group.js → button-group.ts} +21 -21
  85. package/src/ui/{color-selector.js → color-selector.ts} +44 -37
  86. package/src/ui/{expression-editor.js → expression-editor.ts} +65 -40
  87. package/src/ui/{field-row.js → field-row.ts} +19 -2
  88. package/src/ui/{icons.js → icons.ts} +2 -2
  89. package/src/ui/{layers.js → layers.ts} +33 -24
  90. package/src/ui/{media-picker.js → media-picker.ts} +13 -9
  91. package/src/ui/{panel-resize.js → panel-resize.ts} +9 -4
  92. package/src/ui/{spectrum.js → spectrum.ts} +4 -4
  93. package/src/ui/{unit-selector.js → unit-selector.ts} +16 -19
  94. package/src/ui/{value-selector.js → value-selector.ts} +28 -28
  95. package/src/ui/{widgets.js → widgets.ts} +62 -15
  96. package/src/utils/{canvas-media.js → canvas-media.ts} +29 -14
  97. package/src/utils/edit-display.ts +298 -0
  98. package/src/utils/{google-fonts.js → google-fonts.ts} +14 -13
  99. package/src/utils/{inherited-style.js → inherited-style.ts} +15 -10
  100. package/src/utils/{studio-utils.js → studio-utils.ts} +35 -23
  101. package/src/{view.js → view.ts} +53 -55
  102. package/src/workspace/{workspace.js → workspace.ts} +44 -33
  103. package/src/panels/dnd.js +0 -426
  104. package/src/tabs/tab.js +0 -192
  105. package/src/utils/edit-display.js +0 -203
  106. /package/src/{reactivity.js → reactivity.ts} +0 -0
package/src/tabs/tab.js DELETED
@@ -1,192 +0,0 @@
1
- import { reactive, effectScope } from "../reactivity.js";
2
-
3
- /**
4
- * @typedef {{
5
- * rightTab: string;
6
- * canvasMode: string;
7
- * zoom: number;
8
- * activeMedia: string | null;
9
- * activeSelector: string | null;
10
- * editingFunction: FunctionEditDef | null;
11
- * featureToggles: Record<string, boolean>;
12
- * styleSections: Record<string, boolean>;
13
- * inspectorSections: Record<string, boolean>;
14
- * styleShorthands: Record<string, boolean>;
15
- * styleFilter: string;
16
- * styleFilterActive: boolean;
17
- * stylebookSelection: string | null;
18
- * stylebookTab: string;
19
- * stylebookFilter: string;
20
- * stylebookCustomizedOnly: boolean;
21
- * settingsTab: string;
22
- * gitStatus: GitStatusResult | null;
23
- * gitBranches: GitBranchesResult | null;
24
- * gitCommitMessage: string;
25
- * gitLoading: boolean;
26
- * gitError: string | null;
27
- * gitDiffState: GitDiffState | null;
28
- * pendingInlineEdit: InlineEditDef | null;
29
- * }} TabUi
30
- */
31
-
32
- /**
33
- * @typedef {{
34
- * document: Record<string, unknown>;
35
- * selection: (string | number)[] | null;
36
- * }} HistorySnapshot
37
- */
38
-
39
- /**
40
- * @typedef {{
41
- * id: string;
42
- * documentPath: string | null;
43
- * fileHandle: FileSystemFileHandle | null;
44
- * capabilities: { modes: string[] };
45
- * scope: { stop(): void; run<T>(fn: () => T): T | undefined; [k: string]: unknown };
46
- * doc: {
47
- * document: JxMutableNode;
48
- * content: { frontmatter: Record<string, unknown> };
49
- * mode: string;
50
- * sourceFormat: string | null;
51
- * handlersSource: string | null;
52
- * dirty: boolean;
53
- * };
54
- * session: {
55
- * selection: (string | number)[] | null;
56
- * hover: (string | number)[] | null;
57
- * clipboard: JxMutableNode | null;
58
- * documentStack: DocumentStackEntry[];
59
- * ui: TabUi;
60
- * canvas: {
61
- * status: string;
62
- * scope: { stop(): void; [k: string]: unknown } | null;
63
- * error: string | null;
64
- * pendingInlineEdit: InlineEditDef | null;
65
- * };
66
- * };
67
- * history: {
68
- * snapshots: HistorySnapshot[];
69
- * index: number;
70
- * };
71
- * }} Tab
72
- */
73
-
74
- /** @returns {TabUi} */
75
- function createDefaultUi() {
76
- return {
77
- rightTab: "properties",
78
- canvasMode: "edit",
79
- zoom: 1,
80
- activeMedia: null,
81
- activeSelector: null,
82
- editingFunction: null,
83
- featureToggles: {},
84
- styleSections: {},
85
- inspectorSections: {},
86
- styleShorthands: {},
87
- styleFilter: "",
88
- styleFilterActive: false,
89
- stylebookSelection: null,
90
- stylebookTab: "elements",
91
- stylebookFilter: "",
92
- stylebookCustomizedOnly: false,
93
- settingsTab: "stylebook",
94
- gitStatus: null,
95
- gitBranches: null,
96
- gitCommitMessage: "",
97
- gitLoading: false,
98
- gitError: null,
99
- gitDiffState: null,
100
- pendingInlineEdit: null,
101
- };
102
- }
103
-
104
- const ALL_MODES = ["edit", "design", "preview", "source", "stylebook"];
105
-
106
- /**
107
- * Create a new tab with reactive doc/session/history trees, owned by an effectScope.
108
- *
109
- * @param {{
110
- * id: string;
111
- * documentPath?: string | null;
112
- * fileHandle?: FileSystemFileHandle | null;
113
- * document: Record<string, unknown>;
114
- * frontmatter?: Record<string, unknown>;
115
- * sourceFormat?: string | null;
116
- * capabilities?: { modes?: string[] };
117
- * }} opts
118
- * @returns {Tab}
119
- */
120
- export function createTab({
121
- id,
122
- documentPath = null,
123
- fileHandle = null,
124
- document,
125
- frontmatter,
126
- sourceFormat = null,
127
- capabilities,
128
- }) {
129
- const scope = effectScope();
130
-
131
- const resolvedModes = capabilities?.modes ?? inferModes(documentPath, sourceFormat);
132
-
133
- const tab = /** @type {Tab} */ (
134
- /** @type {unknown} */ (
135
- scope.run(() => ({
136
- id,
137
- documentPath,
138
- fileHandle,
139
- capabilities: { modes: resolvedModes },
140
- scope,
141
- doc: reactive({
142
- document,
143
- sourceFormat,
144
- content: { frontmatter: frontmatter || {} },
145
- mode:
146
- sourceFormat === "md"
147
- ? "content"
148
- : documentPath?.endsWith(".md")
149
- ? "content"
150
- : "component",
151
- handlersSource: null,
152
- dirty: false,
153
- }),
154
- session: reactive({
155
- selection: null,
156
- hover: null,
157
- clipboard: null,
158
- documentStack: [],
159
- ui: createDefaultUi(),
160
- canvas: { status: "idle", scope: null, error: null, pendingInlineEdit: null },
161
- }),
162
- history: reactive({
163
- snapshots: [{ document: structuredClone(document), selection: null }],
164
- index: 0,
165
- }),
166
- }))
167
- )
168
- );
169
-
170
- return tab;
171
- }
172
-
173
- /**
174
- * @param {string | null | undefined} documentPath
175
- * @param {string | null} sourceFormat
176
- * @returns {string[]}
177
- */
178
- function inferModes(documentPath, sourceFormat) {
179
- if (documentPath === "project.json") return ["stylebook", "source"];
180
- if (sourceFormat === "md" || documentPath?.endsWith(".md"))
181
- return ["edit", "design", "preview", "source"];
182
- return ALL_MODES;
183
- }
184
-
185
- /**
186
- * Dispose a tab — stops its effectScope, killing all effects created within it.
187
- *
188
- * @param {Tab} tab
189
- */
190
- export function disposeTab(tab) {
191
- tab.scope.stop();
192
- }
@@ -1,203 +0,0 @@
1
- /**
2
- * Edit-mode display transforms — extracted from studio.js (Phase 4i). Pure stateless functions that
3
- * convert document trees for visual editing (template expressions, $map, $switch, empty
4
- * placeholders).
5
- */
6
-
7
- /**
8
- * Convert a template string to a displayable expression for edit mode. Replaces ${expr} with ❮ expr
9
- * ❯ so the runtime renders it as literal text.
10
- *
11
- * @param {string} str
12
- */
13
- export function templateToEditDisplay(str) {
14
- return str.replace(/\$\{([^}]+)\}/g, "\u276A $1 \u276B");
15
- }
16
-
17
- /**
18
- * Reverse templateToEditDisplay: walk all text nodes in `el` and replace ❪ expr ❫ back to ${expr}
19
- * so the user edits raw template syntax.
20
- *
21
- * @param {HTMLElement} el
22
- */
23
- export function restoreTemplateExpressions(el) {
24
- const walker = document.createTreeWalker(el, NodeFilter.SHOW_TEXT);
25
- while (walker.nextNode()) {
26
- const node = /** @type {Text} */ (walker.currentNode);
27
- if (node.data.includes("\u276A")) {
28
- node.data = node.data.replace(/\u276A\s*(.*?)\s*\u276B/g, "${$1}");
29
- }
30
- }
31
- }
32
-
33
- /**
34
- * Prepare a document for edit-mode rendering. Replaces template strings with readable literal text,
35
- * $prototype:Array with placeholders, and $ref bindings with display labels. Preserves state so the
36
- * runtime can still initialise scope.
37
- *
38
- * @param {JxMutableNode} node
39
- * @returns {JxMutableNode}
40
- */
41
- export function prepareForEditMode(node) {
42
- if (!node || typeof node !== "object") return node;
43
- if (Array.isArray(node)) return node.map(prepareForEditMode);
44
-
45
- const /** @type {Record<string, unknown>} */ obj = /** @type {Record<string, unknown>} */ (node);
46
-
47
- /** @type {Record<string, unknown>} */
48
- const out = {};
49
- for (const [k, v] of Object.entries(obj)) {
50
- if (k === "state" || k === "$media" || k === "$props" || k === "$elements") {
51
- out[k] = v; // preserve as-is for runtime resolution
52
- } else if (k === "children") {
53
- if (Array.isArray(v)) {
54
- out.children = v.map(prepareForEditMode);
55
- } else if (
56
- v &&
57
- typeof v === "object" &&
58
- /** @type {Record<string, unknown>} */ (v).$prototype === "Array"
59
- ) {
60
- // Wrap the map template in a visual repeater perimeter
61
- const vObj = /** @type {Record<string, unknown>} */ (v);
62
- const template = vObj.map;
63
- if (template && typeof template === "object") {
64
- out.children = [
65
- {
66
- tagName: "div",
67
- className: "repeater-perimeter",
68
- children: [prepareForEditMode(/** @type {JxMutableNode} */ (template))],
69
- },
70
- ];
71
- } else {
72
- out.children = [];
73
- }
74
- } else {
75
- out.children = prepareForEditMode(/** @type {JxMutableNode} */ (v));
76
- }
77
- } else if (k === "cases" && obj.$switch && v && typeof v === "object") {
78
- // Replace $switch cases with a placeholder showing the first case or a label
79
- const caseKeys = Object.keys(v);
80
- if (caseKeys.length > 0) {
81
- const firstCase = /** @type {Record<string, unknown>} */ (v)[caseKeys[0]];
82
- if (
83
- firstCase &&
84
- typeof firstCase === "object" &&
85
- !(/** @type {Record<string, unknown>} */ (firstCase).$ref)
86
- ) {
87
- out.children = [prepareForEditMode(firstCase)];
88
- } else {
89
- out.children = [
90
- {
91
- tagName: "div",
92
- textContent: `[$switch: ${caseKeys.join(" | ")}]`,
93
- style: {
94
- fontFamily: "'SF Mono', 'Fira Code', monospace",
95
- fontSize: "11px",
96
- padding: "6px 10px",
97
- background: "color-mix(in srgb, var(--danger) 8%, transparent)",
98
- border: "1px dashed color-mix(in srgb, var(--danger) 40%, transparent)",
99
- borderRadius: "4px",
100
- color: "var(--danger)",
101
- fontStyle: "italic",
102
- },
103
- },
104
- ];
105
- }
106
- }
107
- } else if (k === "style") {
108
- // Replace template strings in style values with empty strings
109
- if (v && typeof v === "object") {
110
- /** @type {Record<string, unknown>} */
111
- const s = {};
112
- for (const [sk, sv] of Object.entries(v)) {
113
- s[sk] = typeof sv === "string" && sv.includes("${") ? "" : sv;
114
- }
115
- out.style = s;
116
- } else {
117
- out.style = v;
118
- }
119
- } else if (typeof v === "string" && v.includes("${")) {
120
- // Template string in a display property → show raw expression
121
- out[k] = templateToEditDisplay(v);
122
- } else if (v && typeof v === "object" && /** @type {Record<string, unknown>} */ (v).$ref) {
123
- // $ref binding → show ref path as literal text
124
- const ref = /** @type {string} */ (/** @type {Record<string, unknown>} */ (v).$ref);
125
- const label = ref.startsWith("#/state/") ? ref.slice(8) : ref;
126
- out[k] = `{${label}}`;
127
- } else {
128
- out[k] = prepareForEditMode(/** @type {JxMutableNode} */ (v));
129
- }
130
- }
131
-
132
- // Mark empty elements with placeholder classes for design-mode visibility
133
- if (out.tagName && !out.textContent && !out.innerHTML) {
134
- const hasChildren = Array.isArray(out.children) && out.children.length > 0;
135
- if (!hasChildren) {
136
- const tag = /** @type {string} */ (out.tagName);
137
- const textTags = new Set([
138
- "p",
139
- "h1",
140
- "h2",
141
- "h3",
142
- "h4",
143
- "h5",
144
- "h6",
145
- "blockquote",
146
- "li",
147
- "dt",
148
- "dd",
149
- "th",
150
- "td",
151
- "span",
152
- "strong",
153
- "em",
154
- "small",
155
- "mark",
156
- "code",
157
- "abbr",
158
- "q",
159
- "sub",
160
- "sup",
161
- "time",
162
- "a",
163
- "button",
164
- "label",
165
- "legend",
166
- "caption",
167
- "summary",
168
- "pre",
169
- "option",
170
- ]);
171
- const containerTags = new Set([
172
- "div",
173
- "section",
174
- "article",
175
- "aside",
176
- "header",
177
- "footer",
178
- "main",
179
- "nav",
180
- "figure",
181
- "figcaption",
182
- "details",
183
- "fieldset",
184
- "form",
185
- "ul",
186
- "ol",
187
- "dl",
188
- "table",
189
- ]);
190
- if (textTags.has(tag)) {
191
- out.className = out.className
192
- ? out.className + " empty-text-placeholder"
193
- : "empty-text-placeholder";
194
- } else if (containerTags.has(tag)) {
195
- out.className = out.className
196
- ? out.className + " empty-container-placeholder"
197
- : "empty-container-placeholder";
198
- }
199
- }
200
- }
201
-
202
- return out;
203
- }
File without changes