@jxsuite/studio 0.23.2 → 0.24.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 (105) hide show
  1. package/dist/studio.js +2230 -714
  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} +7 -5
  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.js → edit-display.ts} +23 -20
  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/{reactivity.js → reactivity.ts} +0 -0
@@ -1,13 +1,12 @@
1
- import { toRaw } from "../reactivity.js";
2
- import { getNodeAtPath, parentElementPath, childIndex, pathsEqual, isAncestor } from "../state.js";
1
+ /// <reference lib="dom" />
2
+ import { toRaw } from "../reactivity";
3
+ import { getNodeAtPath, parentElementPath, childIndex, pathsEqual, isAncestor } from "../state";
3
4
 
4
- /**
5
- * @typedef {import("../tabs/tab.js").Tab} Tab
6
- *
7
- * @typedef {import("../state.js").JxPath} JxPath
8
- *
9
- * @typedef {string | number | boolean | object | null | undefined} JsonValue
10
- */
5
+ import type { Tab } from "../tabs/tab";
6
+ import type { JxPath } from "../state";
7
+
8
+ import type { JsonValue } from "../types";
9
+ import type { JxMutableNode } from "@jxsuite/schema/types";
11
10
 
12
11
  const HISTORY_LIMIT = 100;
13
12
 
@@ -21,7 +20,11 @@ const HISTORY_LIMIT = 100;
21
20
  * @param {(tab: Tab) => void} mutationFn
22
21
  * @param {{ skipHistory?: boolean }} [opts]
23
22
  */
24
- export function transactDoc(tab, mutationFn, { skipHistory = false } = {}) {
23
+ export function transactDoc(
24
+ tab: Tab | null,
25
+ mutationFn: (tab: Tab) => void,
26
+ { skipHistory = false }: { skipHistory?: boolean } = {},
27
+ ) {
25
28
  if (!tab) return;
26
29
  mutationFn(tab);
27
30
 
@@ -52,14 +55,18 @@ export function transactDoc(tab, mutationFn, { skipHistory = false } = {}) {
52
55
  * @param {(doc: JxMutableNode) => void} fn
53
56
  * @param {{ skipHistory?: boolean }} [opts]
54
57
  */
55
- export function transact(tab, fn, opts) {
58
+ export function transact(
59
+ tab: Tab | null,
60
+ fn: (doc: JxMutableNode) => void,
61
+ opts?: { skipHistory?: boolean },
62
+ ) {
56
63
  transactDoc(tab, (t) => fn(t.doc.document), opts);
57
64
  }
58
65
 
59
66
  // ─── Undo / Redo ─────────────────────────────────────────────────────────────
60
67
 
61
68
  /** @param {Tab} tab */
62
- export function undo(tab) {
69
+ export function undo(tab: Tab) {
63
70
  if (tab.history.index <= 0) return;
64
71
  tab.history.index--;
65
72
  const snap = tab.history.snapshots[tab.history.index];
@@ -69,7 +76,7 @@ export function undo(tab) {
69
76
  }
70
77
 
71
78
  /** @param {Tab} tab */
72
- export function redo(tab) {
79
+ export function redo(tab: Tab) {
73
80
  if (tab.history.index >= tab.history.snapshots.length - 1) return;
74
81
  tab.history.index++;
75
82
  const snap = tab.history.snapshots[tab.history.index];
@@ -86,7 +93,12 @@ export function redo(tab) {
86
93
  * @param {number} index
87
94
  * @param {JxMutableNode} nodeDef
88
95
  */
89
- export function mutateInsertNode(tab, parentPath, index, nodeDef) {
96
+ export function mutateInsertNode(
97
+ tab: Tab,
98
+ parentPath: JxPath,
99
+ index: number,
100
+ nodeDef: JxMutableNode,
101
+ ) {
90
102
  const parent = getNodeAtPath(tab.doc.document, parentPath);
91
103
  if (!parent.children) parent.children = [];
92
104
  parent.children.splice(index, 0, structuredClone(nodeDef));
@@ -96,14 +108,11 @@ export function mutateInsertNode(tab, parentPath, index, nodeDef) {
96
108
  * @param {Tab} tab
97
109
  * @param {JxPath} path
98
110
  */
99
- export function mutateRemoveNode(tab, path) {
111
+ export function mutateRemoveNode(tab: Tab, path: JxPath) {
100
112
  if (!path || path.length < 2) return;
101
- const elemPath = /** @type {JxPath} */ (parentElementPath(path));
102
- const idx = /** @type {number} */ (childIndex(path));
103
- /** @type {JxMutableNode[]} */ (getNodeAtPath(tab.doc.document, elemPath).children).splice(
104
- idx,
105
- 1,
106
- );
113
+ const elemPath = parentElementPath(path) as JxPath;
114
+ const idx = childIndex(path) as number;
115
+ (getNodeAtPath(tab.doc.document, elemPath).children as JxMutableNode[]).splice(idx, 1);
107
116
  if (tab.session.selection && isAncestor(path, tab.session.selection)) {
108
117
  tab.session.selection = null;
109
118
  }
@@ -113,18 +122,14 @@ export function mutateRemoveNode(tab, path) {
113
122
  * @param {Tab} tab
114
123
  * @param {JxPath} path
115
124
  */
116
- export function mutateDuplicateNode(tab, path) {
125
+ export function mutateDuplicateNode(tab: Tab, path: JxPath) {
117
126
  if (!path || path.length < 2) return;
118
127
  const node = getNodeAtPath(tab.doc.document, path);
119
128
  if (!node) return;
120
- const elemPath = /** @type {JxPath} */ (parentElementPath(path));
121
- const idx = /** @type {number} */ (childIndex(path));
129
+ const elemPath = parentElementPath(path) as JxPath;
130
+ const idx = childIndex(path) as number;
122
131
  const clone = structuredClone(toRaw(node));
123
- /** @type {JxMutableNode[]} */ (getNodeAtPath(tab.doc.document, elemPath).children).splice(
124
- idx + 1,
125
- 0,
126
- clone,
127
- );
132
+ (getNodeAtPath(tab.doc.document, elemPath).children as JxMutableNode[]).splice(idx + 1, 0, clone);
128
133
  tab.session.selection = [...elemPath, "children", idx + 1];
129
134
  }
130
135
 
@@ -133,18 +138,14 @@ export function mutateDuplicateNode(tab, path) {
133
138
  * @param {JxPath} path
134
139
  * @param {string} wrapperTag
135
140
  */
136
- export function mutateWrapNode(tab, path, wrapperTag = "div") {
141
+ export function mutateWrapNode(tab: Tab, path: JxPath, wrapperTag: string = "div") {
137
142
  if (!path || path.length < 2) return;
138
143
  const node = getNodeAtPath(tab.doc.document, path);
139
144
  if (!node) return;
140
- const elemPath = /** @type {JxPath} */ (parentElementPath(path));
141
- const idx = /** @type {number} */ (childIndex(path));
145
+ const elemPath = parentElementPath(path) as JxPath;
146
+ const idx = childIndex(path) as number;
142
147
  const wrapper = { tagName: wrapperTag, children: [structuredClone(toRaw(node))] };
143
- /** @type {JxMutableNode[]} */ (getNodeAtPath(tab.doc.document, elemPath).children).splice(
144
- idx,
145
- 1,
146
- wrapper,
147
- );
148
+ (getNodeAtPath(tab.doc.document, elemPath).children as JxMutableNode[]).splice(idx, 1, wrapper);
148
149
  tab.session.selection = [...elemPath, "children", idx];
149
150
  }
150
151
 
@@ -154,12 +155,12 @@ export function mutateWrapNode(tab, path, wrapperTag = "div") {
154
155
  * @param {JxPath} toParentPath
155
156
  * @param {number} toIndex
156
157
  */
157
- export function mutateMoveNode(tab, fromPath, toParentPath, toIndex) {
158
+ export function mutateMoveNode(tab: Tab, fromPath: JxPath, toParentPath: JxPath, toIndex: number) {
158
159
  const doc = tab.doc.document;
159
- const fromParentPath = /** @type {JxPath} */ (parentElementPath(fromPath));
160
- const fromIdx = /** @type {number} */ (childIndex(fromPath));
160
+ const fromParentPath = parentElementPath(fromPath) as JxPath;
161
+ const fromIdx = childIndex(fromPath) as number;
161
162
  const fromParent = getNodeAtPath(doc, fromParentPath);
162
- const [node] = /** @type {JxMutableNode[]} */ (fromParent.children).splice(fromIdx, 1);
163
+ const [node] = (fromParent.children as JxMutableNode[]).splice(fromIdx, 1);
163
164
  const toParent = getNodeAtPath(doc, toParentPath);
164
165
  if (!toParent.children) toParent.children = [];
165
166
  let adjustedIndex = toIndex;
@@ -185,7 +186,7 @@ export function mutateMoveNode(tab, fromPath, toParentPath, toIndex) {
185
186
  * @param {string} key
186
187
  * @param {JsonValue} value
187
188
  */
188
- export function mutateUpdateProperty(tab, path, key, value) {
189
+ export function mutateUpdateProperty(tab: Tab, path: JxPath, key: string, value: JsonValue) {
189
190
  const node = getNodeAtPath(tab.doc.document, path);
190
191
  if (value === undefined || value === null || value === "") delete node[key];
191
192
  else node[key] = value;
@@ -197,7 +198,7 @@ export function mutateUpdateProperty(tab, path, key, value) {
197
198
  * @param {string} prop
198
199
  * @param {string | undefined} value
199
200
  */
200
- export function mutateUpdateStyle(tab, path, prop, value) {
201
+ export function mutateUpdateStyle(tab: Tab, path: JxPath, prop: string, value: string | undefined) {
201
202
  const node = getNodeAtPath(tab.doc.document, path);
202
203
  if (!node.style) node.style = {};
203
204
  if (value === undefined || value === "") delete node.style[prop];
@@ -211,7 +212,12 @@ export function mutateUpdateStyle(tab, path, prop, value) {
211
212
  * @param {string} attr
212
213
  * @param {string | undefined} value
213
214
  */
214
- export function mutateUpdateAttribute(tab, path, attr, value) {
215
+ export function mutateUpdateAttribute(
216
+ tab: Tab,
217
+ path: JxPath,
218
+ attr: string,
219
+ value: string | undefined,
220
+ ) {
215
221
  const node = getNodeAtPath(tab.doc.document, path);
216
222
  if (!node.attributes) node.attributes = {};
217
223
  if (value === undefined || value === "") delete node.attributes[attr];
@@ -226,7 +232,13 @@ export function mutateUpdateAttribute(tab, path, attr, value) {
226
232
  * @param {string} prop
227
233
  * @param {string | undefined} value
228
234
  */
229
- export function mutateUpdateMediaStyle(tab, path, mediaName, prop, value) {
235
+ export function mutateUpdateMediaStyle(
236
+ tab: Tab,
237
+ path: JxPath,
238
+ mediaName: string,
239
+ prop: string,
240
+ value: string | undefined,
241
+ ) {
230
242
  if (!mediaName) return mutateUpdateStyle(tab, path, prop, value);
231
243
  const node = getNodeAtPath(tab.doc.document, path);
232
244
  if (!node.style) node.style = {};
@@ -248,7 +260,13 @@ export function mutateUpdateMediaStyle(tab, path, mediaName, prop, value) {
248
260
  * @param {string} prop
249
261
  * @param {string | undefined} value
250
262
  */
251
- export function mutateUpdateNestedStyle(tab, path, selector, prop, value) {
263
+ export function mutateUpdateNestedStyle(
264
+ tab: Tab,
265
+ path: JxPath,
266
+ selector: string,
267
+ prop: string,
268
+ value: string | undefined,
269
+ ) {
252
270
  const node = getNodeAtPath(tab.doc.document, path);
253
271
  if (!node.style) node.style = {};
254
272
  if (!node.style[selector]) node.style[selector] = {};
@@ -269,7 +287,14 @@ export function mutateUpdateNestedStyle(tab, path, selector, prop, value) {
269
287
  * @param {string} prop
270
288
  * @param {string | undefined} value
271
289
  */
272
- export function mutateUpdateMediaNestedStyle(tab, path, mediaName, selector, prop, value) {
290
+ export function mutateUpdateMediaNestedStyle(
291
+ tab: Tab,
292
+ path: JxPath,
293
+ mediaName: string,
294
+ selector: string,
295
+ prop: string,
296
+ value: string | undefined,
297
+ ) {
273
298
  const node = getNodeAtPath(tab.doc.document, path);
274
299
  if (!node.style) node.style = {};
275
300
  const key = `@${mediaName}`;
@@ -295,7 +320,13 @@ export function mutateUpdateMediaNestedStyle(tab, path, mediaName, selector, pro
295
320
  * @param {string} prop
296
321
  * @param {string | undefined} value
297
322
  */
298
- export function mutateUpdateNestedStylePath(tab, path, stylePath, prop, value) {
323
+ export function mutateUpdateNestedStylePath(
324
+ tab: Tab,
325
+ path: JxPath,
326
+ stylePath: string[],
327
+ prop: string,
328
+ value: string | undefined,
329
+ ) {
299
330
  const node = getNodeAtPath(tab.doc.document, path);
300
331
  if (!node.style) node.style = {};
301
332
  let obj = node.style;
@@ -331,7 +362,14 @@ export function mutateUpdateNestedStylePath(tab, path, stylePath, prop, value) {
331
362
  * @param {string} prop
332
363
  * @param {string | undefined} value
333
364
  */
334
- export function mutateUpdateMediaNestedStylePath(tab, path, mediaName, stylePath, prop, value) {
365
+ export function mutateUpdateMediaNestedStylePath(
366
+ tab: Tab,
367
+ path: JxPath,
368
+ mediaName: string,
369
+ stylePath: string[],
370
+ prop: string,
371
+ value: string | undefined,
372
+ ) {
335
373
  const node = getNodeAtPath(tab.doc.document, path);
336
374
  if (!node.style) node.style = {};
337
375
  const key = `@${mediaName}`;
@@ -364,7 +402,11 @@ export function mutateUpdateMediaNestedStylePath(tab, path, mediaName, stylePath
364
402
  * @param {JxPath} path
365
403
  * @param {Record<string, string | undefined> | undefined} style
366
404
  */
367
- export function mutateReplaceStyle(tab, path, style) {
405
+ export function mutateReplaceStyle(
406
+ tab: Tab,
407
+ path: JxPath,
408
+ style: Record<string, string | undefined> | undefined,
409
+ ) {
368
410
  const node = getNodeAtPath(tab.doc.document, path);
369
411
  if (style && Object.keys(style).length > 0) {
370
412
  node.style = style;
@@ -378,7 +420,7 @@ export function mutateReplaceStyle(tab, path, style) {
378
420
  * @param {string} name
379
421
  * @param {Record<string, JsonValue>} def
380
422
  */
381
- export function mutateAddDef(tab, name, def) {
423
+ export function mutateAddDef(tab: Tab, name: string, def: Record<string, JsonValue>) {
382
424
  const doc = tab.doc.document;
383
425
  if (!doc.state) doc.state = {};
384
426
  doc.state[name] = def;
@@ -388,7 +430,7 @@ export function mutateAddDef(tab, name, def) {
388
430
  * @param {Tab} tab
389
431
  * @param {string} name
390
432
  */
391
- export function mutateRemoveDef(tab, name) {
433
+ export function mutateRemoveDef(tab: Tab, name: string) {
392
434
  const doc = tab.doc.document;
393
435
  if (doc.state) {
394
436
  delete doc.state[name];
@@ -401,7 +443,7 @@ export function mutateRemoveDef(tab, name) {
401
443
  * @param {string} name
402
444
  * @param {Record<string, JsonValue>} updates
403
445
  */
404
- export function mutateUpdateDef(tab, name, updates) {
446
+ export function mutateUpdateDef(tab: Tab, name: string, updates: Record<string, JsonValue>) {
405
447
  const doc = tab.doc.document;
406
448
  if (!doc.state) doc.state = {};
407
449
  if (doc.state[name] == null) {
@@ -422,7 +464,7 @@ export function mutateUpdateDef(tab, name, updates) {
422
464
  * @param {string} oldName
423
465
  * @param {string} newName
424
466
  */
425
- export function mutateRenameDef(tab, oldName, newName) {
467
+ export function mutateRenameDef(tab: Tab, oldName: string, newName: string) {
426
468
  const doc = tab.doc.document;
427
469
  if (!doc.state || !doc.state[oldName]) return;
428
470
  doc.state[newName] = doc.state[oldName];
@@ -434,7 +476,7 @@ export function mutateRenameDef(tab, oldName, newName) {
434
476
  * @param {string} name
435
477
  * @param {string | undefined} query
436
478
  */
437
- export function mutateUpdateMedia(tab, name, query) {
479
+ export function mutateUpdateMedia(tab: Tab, name: string, query: string | undefined) {
438
480
  const doc = tab.doc.document;
439
481
  if (!doc.$media) doc.$media = {};
440
482
  if (query === undefined || query === "") {
@@ -451,7 +493,7 @@ export function mutateUpdateMedia(tab, name, query) {
451
493
  * @param {string} propName
452
494
  * @param {JsonValue} value
453
495
  */
454
- export function mutateUpdateProp(tab, path, propName, value) {
496
+ export function mutateUpdateProp(tab: Tab, path: JxPath, propName: string, value: JsonValue) {
455
497
  const node = getNodeAtPath(tab.doc.document, path);
456
498
  if (!node.$props) node.$props = {};
457
499
  if (value === undefined || value === null || value === "") delete node.$props[propName];
@@ -465,7 +507,12 @@ export function mutateUpdateProp(tab, path, propName, value) {
465
507
  * @param {string} caseName
466
508
  * @param {JxMutableNode} [caseDef]
467
509
  */
468
- export function mutateAddSwitchCase(tab, path, caseName, caseDef) {
510
+ export function mutateAddSwitchCase(
511
+ tab: Tab,
512
+ path: JxPath,
513
+ caseName: string,
514
+ caseDef?: JxMutableNode,
515
+ ) {
469
516
  const node = getNodeAtPath(tab.doc.document, path);
470
517
  if (!node.cases) node.cases = {};
471
518
  node.cases[caseName] = caseDef || { tagName: "div", textContent: caseName };
@@ -476,7 +523,7 @@ export function mutateAddSwitchCase(tab, path, caseName, caseDef) {
476
523
  * @param {JxPath} path
477
524
  * @param {string} caseName
478
525
  */
479
- export function mutateRemoveSwitchCase(tab, path, caseName) {
526
+ export function mutateRemoveSwitchCase(tab: Tab, path: JxPath, caseName: string) {
480
527
  const node = getNodeAtPath(tab.doc.document, path);
481
528
  if (node.cases) delete node.cases[caseName];
482
529
  }
@@ -487,7 +534,7 @@ export function mutateRemoveSwitchCase(tab, path, caseName) {
487
534
  * @param {string} oldName
488
535
  * @param {string} newName
489
536
  */
490
- export function mutateRenameSwitchCase(tab, path, oldName, newName) {
537
+ export function mutateRenameSwitchCase(tab: Tab, path: JxPath, oldName: string, newName: string) {
491
538
  const node = getNodeAtPath(tab.doc.document, path);
492
539
  if (!node.cases || !node.cases[oldName]) return;
493
540
  node.cases[newName] = node.cases[oldName];
@@ -503,7 +550,7 @@ export function mutateRenameSwitchCase(tab, path, oldName, newName) {
503
550
  * @param {string} field
504
551
  * @param {JsonValue} value
505
552
  */
506
- export function mutateUpdateFrontmatter(tab, field, value) {
553
+ export function mutateUpdateFrontmatter(tab: Tab, field: string, value: JsonValue) {
507
554
  if (value === undefined || value === null || value === "") {
508
555
  delete tab.doc.content.frontmatter[field];
509
556
  } else {
package/src/types.ts ADDED
@@ -0,0 +1,185 @@
1
+ /// <reference lib="dom" />
2
+ import type { JxMutableNode, JxPath, ProjectConfig } from "@jxsuite/schema/types";
3
+
4
+ // ─── Git & Platform Types ───────────────────────────────────────────────────
5
+
6
+ export interface GitFileStatus {
7
+ status: string;
8
+ path: string;
9
+ staged?: boolean;
10
+ }
11
+
12
+ export interface GitStatusResult {
13
+ branch: string;
14
+ files: GitFileStatus[];
15
+ ahead: number;
16
+ behind: number;
17
+ isRepo: boolean;
18
+ remotes: string[];
19
+ }
20
+
21
+ export interface GitBranchesResult {
22
+ current: string;
23
+ branches: string[];
24
+ }
25
+
26
+ export interface GitLogEntry {
27
+ hash: string;
28
+ message: string;
29
+ author: string;
30
+ date: string;
31
+ }
32
+
33
+ export interface ComponentMeta {
34
+ tagName: string;
35
+ $id?: string | null;
36
+ path: string;
37
+ props?: { name: string; type?: string; default?: unknown }[];
38
+ hasElements?: boolean;
39
+ }
40
+
41
+ export interface PackageInfo {
42
+ name: string;
43
+ version: string;
44
+ }
45
+
46
+ export interface CodeServiceResult {
47
+ code?: string;
48
+ diagnostics?: unknown[];
49
+ [key: string]: unknown;
50
+ }
51
+
52
+ export interface DirEntry {
53
+ name: string;
54
+ path: string;
55
+ type: "file" | "directory";
56
+ size?: number;
57
+ modified?: string;
58
+ }
59
+
60
+ export interface StudioPlatform {
61
+ id: string;
62
+ projectRoot: string;
63
+ activate(root?: string): Promise<void>;
64
+ openProject(): Promise<{
65
+ config: ProjectConfig;
66
+ handle: { root: string; name: string; projectConfig: ProjectConfig };
67
+ } | null>;
68
+ probeRootProject(): Promise<{
69
+ meta: { root: string; name: string };
70
+ info: { isSiteProject: boolean; projectConfig?: ProjectConfig | null; directories?: string[] };
71
+ } | null>;
72
+ listDirectory(dir: string): Promise<DirEntry[]>;
73
+ readFile(path: string): Promise<string>;
74
+ writeFile(path: string, content: string): Promise<void>;
75
+ uploadFile(path: string, data: string | File | Blob | ArrayBuffer): Promise<unknown>;
76
+ deleteFile(path: string): Promise<void>;
77
+ renameFile(from: string, to: string): Promise<void>;
78
+ createDirectory(path: string): Promise<void>;
79
+ discoverComponents(dir?: string): Promise<ComponentMeta[]>;
80
+ addPackage(name: string): Promise<unknown>;
81
+ removePackage(name: string): Promise<unknown>;
82
+ listPackages(): Promise<PackageInfo[]>;
83
+ codeService(action: string, payload: unknown): Promise<CodeServiceResult | null>;
84
+ resolveSiteContext(
85
+ filePath: string,
86
+ ): Promise<{ sitePath: string | null; projectConfig?: ProjectConfig; fileRelPath?: string }>;
87
+ locateFile(name: string): Promise<string | null>;
88
+ searchFiles(query: string): Promise<DirEntry[]>;
89
+ fetchPluginSchema(src: string, prototype?: string, base?: string): Promise<unknown>;
90
+ gitStatus(): Promise<GitStatusResult>;
91
+ gitBranches(): Promise<GitBranchesResult>;
92
+ gitLog(limit?: number): Promise<GitLogEntry[]>;
93
+ gitStage(files: string[]): Promise<void>;
94
+ gitUnstage(files: string[]): Promise<void>;
95
+ gitCommit(message: string): Promise<void>;
96
+ gitPush(opts?: { setUpstream?: boolean }): Promise<void>;
97
+ gitPull(): Promise<void>;
98
+ gitFetch(): Promise<void>;
99
+ gitCheckout(branch: string): Promise<void>;
100
+ gitCreateBranch(name: string): Promise<void>;
101
+ gitDiff(path?: string): Promise<string>;
102
+ gitShow(opts: { path: string; ref?: string }): Promise<string>;
103
+ gitDiscard(files: string[]): Promise<void>;
104
+ gitClone?(url: string): Promise<{ ok: boolean; root: string }>;
105
+ gitInit(): Promise<void>;
106
+ gitAddRemote(name: string, url: string): Promise<void>;
107
+ createProject(opts: {
108
+ name: string;
109
+ description?: string;
110
+ url?: string;
111
+ adapter?: string;
112
+ directory: string;
113
+ }): Promise<{ root: string; config: ProjectConfig }>;
114
+ aiAuthStatus(): Promise<{ authenticated: boolean; error?: string }>;
115
+ aiCreateSession(opts: { message: string; systemPrompt?: string }): Promise<{ id: string }>;
116
+ aiSendMessage(id: string, message: string): Promise<void>;
117
+ aiStreamUrl(id: string): string | Promise<string>;
118
+ aiStopSession(id: string): Promise<void>;
119
+ aiDeleteSession(id: string): Promise<void>;
120
+ }
121
+
122
+ // ─── Studio Types ───────────────────────────────────────────────────────────
123
+
124
+ export interface CanvasPanel {
125
+ mediaName: string;
126
+ element: HTMLElement;
127
+ canvas: HTMLElement;
128
+ overlay: HTMLElement;
129
+ overlayClk: HTMLElement;
130
+ viewport: HTMLElement;
131
+ scrollContainer: HTMLElement;
132
+ dropLine: HTMLElement;
133
+ _width: number | null;
134
+ }
135
+
136
+ export interface DocumentStackEntry {
137
+ document: JxMutableNode;
138
+ documentPath: string | null;
139
+ selection: JxPath | null;
140
+ dirty?: boolean;
141
+ mode?: string;
142
+ sourceFormat?: string | null;
143
+ }
144
+
145
+ export interface FunctionEditDef {
146
+ type: string;
147
+ defName?: string;
148
+ path?: JxPath;
149
+ eventKey?: string;
150
+ key?: string;
151
+ body?: string;
152
+ parameters?: string[];
153
+ }
154
+
155
+ export interface GitDiffState {
156
+ filePath: string;
157
+ originalContent: string;
158
+ currentContent: string;
159
+ isMarkdown: boolean;
160
+ fileStatus: string;
161
+ originalDoc?: unknown;
162
+ currentDoc?: unknown;
163
+ original?: unknown;
164
+ }
165
+
166
+ export interface InlineEditDef {
167
+ path: JxPath;
168
+ mediaName?: string;
169
+ }
170
+
171
+ export interface ProjectState {
172
+ root?: string;
173
+ name: string;
174
+ projectRoot: string;
175
+ isSiteProject: boolean;
176
+ projectConfig: ProjectConfig | null;
177
+ dirs: Map<string, DirEntry[]>;
178
+ expanded: Set<string>;
179
+ selectedPath: string | null;
180
+ searchQuery: string;
181
+ projectDirs?: string[];
182
+ [key: string]: unknown;
183
+ }
184
+
185
+ export type JsonValue = string | number | boolean | object | null | undefined;
@@ -1,3 +1,4 @@
1
+ /// <reference lib="dom" />
1
2
  /**
2
3
  * Button-group.js — Action group + overflow picker widget.
3
4
  *
@@ -7,8 +8,8 @@
7
8
 
8
9
  import { html, nothing } from "lit-html";
9
10
  import { classMap } from "lit-html/directives/class-map.js";
10
- import { abbreviateValue, kebabToLabel } from "../utils/studio-utils.js";
11
- import icons from "./icons.js";
11
+ import { abbreviateValue, kebabToLabel } from "../utils/studio-utils";
12
+ import icons from "./icons";
12
13
 
13
14
  /**
14
15
  * Render a button group widget with optional overflow menu.
@@ -20,30 +21,29 @@ import icons from "./icons.js";
20
21
  * @returns {import("lit-html").TemplateResult}
21
22
  */
22
23
  export function renderButtonGroup(
23
- /** @type {Record<string, unknown>} */ entry,
24
- /** @type {string} */ prop,
25
- /** @type {string | number | undefined} */ value,
26
- /** @type {(val: string) => void} */ onChange,
24
+ /** @type {Record<string, unknown>} */ entry: Record<string, unknown>,
25
+ /** @type {string} */ prop: string,
26
+ /** @type {string | number | undefined} */ value: string | number | undefined,
27
+ /** @type {(val: string) => void} */ onChange: (val: string) => void,
27
28
  ) {
28
- const values = /** @type {string[]} */ (entry.$buttonValues || entry.enum || []);
29
- /** @type {Record<string, string>} */
30
- const iconMap = /** @type {Record<string, string>} */ (entry.$icons || {});
31
- const buttonValues = /** @type {string[] | undefined} */ (entry.$buttonValues);
32
- const enumValues = /** @type {string[] | undefined} */ (entry.enum);
29
+ const values = (entry.$buttonValues || entry.enum || []) as string[];
30
+ const iconMap: Record<string, string> = (entry.$icons || {}) as Record<string, string>;
31
+ const buttonValues = entry.$buttonValues as string[] | undefined;
32
+ const enumValues = entry.enum as string[] | undefined;
33
33
  const extra =
34
34
  buttonValues && enumValues && enumValues.length > buttonValues.length
35
- ? enumValues.filter((/** @type {string} */ v) => !buttonValues.includes(v))
35
+ ? enumValues.filter((v: string) => !buttonValues.includes(v))
36
36
  : [];
37
37
 
38
38
  const menuId = `style-btngrp-${prop}`;
39
39
  const hasExtra = extra.length > 0;
40
- const extraSelected = hasExtra && extra.includes(/** @type {string} */ (value));
40
+ const extraSelected = hasExtra && extra.includes(String(value));
41
41
 
42
42
  return html`
43
43
  <div class=${classMap({ "button-group-combo": true, "has-overflow": hasExtra })}>
44
44
  <sp-action-group size="s" compact>
45
45
  ${values.map(
46
- (/** @type {string} */ v) => html`
46
+ (v: string) => html`
47
47
  <sp-action-button
48
48
  size="s"
49
49
  value=${v}
@@ -51,8 +51,8 @@ export function renderButtonGroup(
51
51
  ?selected=${v === value}
52
52
  @click=${() => onChange(v === value ? "" : v)}
53
53
  >
54
- ${iconMap[v] && /** @type {Record<string, unknown>} */ (icons)[iconMap[v]]
55
- ? /** @type {Record<string, unknown>} */ (icons)[iconMap[v]]
54
+ ${iconMap[v] && (icons as Record<string, unknown>)[iconMap[v]]
55
+ ? (icons as Record<string, unknown>)[iconMap[v]]
56
56
  : abbreviateValue(v)}
57
57
  </sp-action-button>
58
58
  `,
@@ -68,16 +68,16 @@ export function renderButtonGroup(
68
68
  <sp-overlay trigger="${menuId}@click" placement="bottom-end" type="auto">
69
69
  <sp-popover>
70
70
  <sp-menu
71
- @change=${(/** @type {Event} */ e) => {
72
- if (/** @type {HTMLInputElement} */ (e.target).value)
73
- onChange(/** @type {HTMLInputElement} */ (e.target).value);
71
+ @change=${(e: Event) => {
72
+ if ((e.target as HTMLInputElement).value)
73
+ onChange((e.target as HTMLInputElement).value);
74
74
  }}
75
75
  >
76
76
  <sp-menu-item value="__none__">—</sp-menu-item>
77
- ${extra.map((/** @type {string} */ v) => {
77
+ ${extra.map((v: string) => {
78
78
  const label = v.includes("-")
79
79
  ? kebabToLabel(v)
80
- : v.replace(/^./, (/** @type {string} */ c) => c.toUpperCase());
80
+ : v.replace(/^./, (c: string) => c.toUpperCase());
81
81
  return html`<sp-menu-item value=${v} ?selected=${v === value}
82
82
  >${label}</sp-menu-item
83
83
  >`;