@jxsuite/studio 0.28.2 → 0.28.4

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 (104) hide show
  1. package/dist/studio.js +86144 -83971
  2. package/dist/studio.js.map +229 -219
  3. package/package.json +5 -5
  4. package/src/browse/browse-modal.ts +9 -3
  5. package/src/browse/browse.ts +139 -82
  6. package/src/canvas/canvas-diff.ts +35 -18
  7. package/src/canvas/canvas-helpers.ts +55 -21
  8. package/src/canvas/canvas-live-render.ts +140 -89
  9. package/src/canvas/canvas-render.ts +127 -82
  10. package/src/canvas/canvas-utils.ts +68 -42
  11. package/src/canvas/nested-site-style.ts +15 -7
  12. package/src/editor/component-inline-edit.ts +90 -51
  13. package/src/editor/content-inline-edit.ts +75 -72
  14. package/src/editor/context-menu.ts +125 -71
  15. package/src/editor/convert-targets.ts +17 -13
  16. package/src/editor/convert-to-component.ts +51 -28
  17. package/src/editor/convert-to-repeater.ts +44 -19
  18. package/src/editor/inline-edit.ts +107 -52
  19. package/src/editor/inline-format.ts +128 -60
  20. package/src/editor/insertion-helper.ts +26 -14
  21. package/src/editor/shortcuts.ts +89 -43
  22. package/src/editor/slash-menu.ts +41 -26
  23. package/src/files/components.ts +9 -7
  24. package/src/files/file-ops.ts +53 -33
  25. package/src/files/files.ts +246 -143
  26. package/src/format/constraints.ts +25 -15
  27. package/src/format/format-host.ts +33 -10
  28. package/src/github/github-auth.ts +24 -14
  29. package/src/github/github-publish.ts +20 -14
  30. package/src/new-project/new-project-modal.ts +27 -18
  31. package/src/panels/activity-bar.ts +27 -26
  32. package/src/panels/ai-panel.ts +100 -44
  33. package/src/panels/block-action-bar.ts +84 -52
  34. package/src/panels/canvas-dnd.ts +47 -28
  35. package/src/panels/data-explorer.ts +24 -11
  36. package/src/panels/dnd.ts +148 -100
  37. package/src/panels/editors.ts +54 -37
  38. package/src/panels/elements-panel.ts +23 -13
  39. package/src/panels/events-panel.ts +48 -38
  40. package/src/panels/git-panel.ts +95 -60
  41. package/src/panels/head-panel.ts +138 -84
  42. package/src/panels/imports-panel.ts +66 -29
  43. package/src/panels/layers-panel.ts +80 -36
  44. package/src/panels/left-panel.ts +86 -53
  45. package/src/panels/overlays.ts +36 -19
  46. package/src/panels/panel-events.ts +48 -27
  47. package/src/panels/panel-scheduler.ts +41 -23
  48. package/src/panels/preview-render.ts +26 -26
  49. package/src/panels/properties-panel.ts +155 -118
  50. package/src/panels/pseudo-preview.ts +31 -15
  51. package/src/panels/quick-search.ts +28 -13
  52. package/src/panels/right-panel.ts +35 -24
  53. package/src/panels/shared.ts +46 -29
  54. package/src/panels/signals-panel.ts +303 -186
  55. package/src/panels/statusbar.ts +27 -12
  56. package/src/panels/style-inputs.ts +30 -24
  57. package/src/panels/style-panel.ts +130 -90
  58. package/src/panels/style-utils.ts +63 -23
  59. package/src/panels/stylebook-layers-panel.ts +27 -30
  60. package/src/panels/stylebook-panel.ts +140 -72
  61. package/src/panels/tab-strip.ts +21 -10
  62. package/src/panels/toolbar.ts +52 -40
  63. package/src/panels/welcome-screen.ts +1 -1
  64. package/src/platform.ts +2 -1
  65. package/src/platforms/devserver.ts +221 -105
  66. package/src/recent-projects.ts +15 -7
  67. package/src/resize-edges.ts +7 -5
  68. package/src/services/cem-export.ts +73 -57
  69. package/src/services/code-services.ts +52 -25
  70. package/src/services/monaco-setup.ts +5 -5
  71. package/src/settings/content-types-editor.ts +133 -62
  72. package/src/settings/css-vars-editor.ts +31 -14
  73. package/src/settings/defs-editor.ts +125 -63
  74. package/src/settings/general-settings.ts +11 -6
  75. package/src/settings/head-editor.ts +30 -15
  76. package/src/settings/schema-field-ui.ts +75 -35
  77. package/src/settings/settings-modal.ts +35 -17
  78. package/src/site-context.ts +80 -47
  79. package/src/state.ts +98 -65
  80. package/src/store.ts +42 -23
  81. package/src/studio.ts +165 -144
  82. package/src/tabs/tab.ts +51 -45
  83. package/src/tabs/transact.ts +230 -109
  84. package/src/types.ts +60 -51
  85. package/src/ui/button-group.ts +3 -2
  86. package/src/ui/color-selector.ts +33 -15
  87. package/src/ui/expression-editor.ts +139 -86
  88. package/src/ui/field-input.ts +35 -18
  89. package/src/ui/field-row.ts +1 -1
  90. package/src/ui/icons.ts +5 -6
  91. package/src/ui/layers.ts +33 -26
  92. package/src/ui/media-picker.ts +13 -7
  93. package/src/ui/panel-resize.ts +39 -20
  94. package/src/ui/spectrum.ts +4 -2
  95. package/src/ui/unit-selector.ts +20 -11
  96. package/src/ui/value-selector.ts +13 -7
  97. package/src/ui/widgets.ts +34 -18
  98. package/src/utils/canvas-media.ts +66 -33
  99. package/src/utils/edit-display.ts +43 -44
  100. package/src/utils/google-fonts.ts +10 -6
  101. package/src/utils/inherited-style.ts +22 -8
  102. package/src/utils/studio-utils.ts +88 -45
  103. package/src/view.ts +9 -5
  104. package/src/workspace/workspace.ts +49 -29
@@ -1,8 +1,12 @@
1
1
  /// <reference lib="dom" />
2
2
  import { html, nothing } from "lit-html";
3
3
  import { live } from "lit-html/directives/live.js";
4
+ import { isJsonObject, isRef } from "@jxsuite/schema/guards";
4
5
  import { renderFieldRow } from "./field-row";
5
6
 
7
+ import type { JxExpressionNode, JxExpressionOperand } from "@jxsuite/schema/types";
8
+ import type { TemplateResult } from "lit-html";
9
+
6
10
  // ─── Operator Categories ────────────────────────────────────────────────────
7
11
 
8
12
  const UNARY_OPS = new Set(["!", "-"]);
@@ -49,75 +53,83 @@ const OPERATOR_GROUPS = [
49
53
  * }}
50
54
  */
51
55
  function operatorInfo(op: string) {
52
- if (UNARY_OPS.has(op))
56
+ if (UNARY_OPS.has(op)) {
53
57
  return {
54
- needsValue: false,
55
58
  needsInitial: false,
56
- targetMustBeRef: false,
59
+ needsValue: false,
57
60
  spliceArray: false,
61
+ targetMustBeRef: false,
58
62
  valueIsNode: false,
59
63
  };
60
- if (BINARY_OPS.has(op))
64
+ }
65
+ if (BINARY_OPS.has(op)) {
61
66
  return {
62
- needsValue: true,
63
67
  needsInitial: false,
64
- targetMustBeRef: false,
68
+ needsValue: true,
65
69
  spliceArray: false,
70
+ targetMustBeRef: false,
66
71
  valueIsNode: false,
67
72
  };
68
- if (ASSIGN_OPS.has(op))
73
+ }
74
+ if (ASSIGN_OPS.has(op)) {
69
75
  return {
70
- needsValue: true,
71
76
  needsInitial: false,
72
- targetMustBeRef: true,
77
+ needsValue: true,
73
78
  spliceArray: false,
79
+ targetMustBeRef: true,
74
80
  valueIsNode: false,
75
81
  };
76
- if (NO_ARG_OPS.has(op))
82
+ }
83
+ if (NO_ARG_OPS.has(op)) {
77
84
  return {
78
- needsValue: false,
79
85
  needsInitial: false,
80
- targetMustBeRef: true,
86
+ needsValue: false,
81
87
  spliceArray: false,
88
+ targetMustBeRef: true,
82
89
  valueIsNode: false,
83
90
  };
84
- if (ONE_ARG_OPS.has(op))
91
+ }
92
+ if (ONE_ARG_OPS.has(op)) {
85
93
  return {
86
- needsValue: true,
87
94
  needsInitial: false,
88
- targetMustBeRef: true,
95
+ needsValue: true,
89
96
  spliceArray: false,
97
+ targetMustBeRef: true,
90
98
  valueIsNode: false,
91
99
  };
92
- if (op === "splice")
100
+ }
101
+ if (op === "splice") {
93
102
  return {
94
- needsValue: true,
95
103
  needsInitial: false,
96
- targetMustBeRef: true,
104
+ needsValue: true,
97
105
  spliceArray: true,
106
+ targetMustBeRef: true,
98
107
  valueIsNode: false,
99
108
  };
100
- if (op === "reduce")
109
+ }
110
+ if (op === "reduce") {
101
111
  return {
102
- needsValue: true,
103
112
  needsInitial: true,
104
- targetMustBeRef: true,
113
+ needsValue: true,
105
114
  spliceArray: false,
115
+ targetMustBeRef: true,
106
116
  valueIsNode: true,
107
117
  };
108
- if (op === "map" || op === "filter")
118
+ }
119
+ if (op === "map" || op === "filter") {
109
120
  return {
110
- needsValue: true,
111
121
  needsInitial: false,
112
- targetMustBeRef: true,
122
+ needsValue: true,
113
123
  spliceArray: false,
124
+ targetMustBeRef: true,
114
125
  valueIsNode: true,
115
126
  };
127
+ }
116
128
  return {
117
- needsValue: false,
118
129
  needsInitial: false,
119
- targetMustBeRef: false,
130
+ needsValue: false,
120
131
  spliceArray: false,
132
+ targetMustBeRef: false,
121
133
  valueIsNode: false,
122
134
  };
123
135
  }
@@ -125,71 +137,105 @@ function operatorInfo(op: string) {
125
137
  // ─── Operand Mode Detection ─────────────────────────────────────────────────
126
138
 
127
139
  /**
128
- * @param {any} operand
140
+ * @param {unknown} operand
129
141
  * @returns {"ref" | "expression" | "literal"}
130
142
  */
131
- function operandMode(operand: any) {
143
+ function operandMode(operand: unknown) {
132
144
  if (operand && typeof operand === "object") {
133
- if ("$ref" in operand) return "ref";
134
- if ("operator" in operand) return "expression";
145
+ if ("$ref" in operand) {
146
+ return "ref";
147
+ }
148
+ if ("operator" in operand) {
149
+ return "expression";
150
+ }
135
151
  }
136
152
  return "literal";
137
153
  }
138
154
 
139
155
  /**
140
156
  * @param {string} mode
141
- * @returns {any}
157
+ * @returns {JxExpressionOperand}
142
158
  */
143
- function defaultForMode(mode: string) {
144
- if (mode === "ref") return { $ref: "" };
145
- if (mode === "expression") return { operator: "!", target: null };
159
+ function defaultForMode(mode: string): JxExpressionOperand {
160
+ if (mode === "ref") {
161
+ return { $ref: "" };
162
+ }
163
+ if (mode === "expression") {
164
+ return { operator: "!", target: null };
165
+ }
146
166
  return null;
147
167
  }
148
168
 
149
169
  // ─── Literal Type Detection ─────────────────────────────────────────────────
150
170
 
151
171
  /**
152
- * @param {any} val
172
+ * @param {unknown} val
153
173
  * @returns {"string" | "number" | "boolean" | "null"}
154
174
  */
155
- function literalType(val: any) {
156
- if (val === null || val === undefined) return "null";
157
- if (typeof val === "boolean") return "boolean";
158
- if (typeof val === "number") return "number";
175
+ function literalType(val: unknown) {
176
+ if (val === null || val === undefined) {
177
+ return "null";
178
+ }
179
+ if (typeof val === "boolean") {
180
+ return "boolean";
181
+ }
182
+ if (typeof val === "number") {
183
+ return "number";
184
+ }
159
185
  return "string";
160
186
  }
161
187
 
162
188
  /**
163
189
  * @param {string} type
164
- * @returns {any}
190
+ * @returns {JxExpressionOperand}
165
191
  */
166
- function defaultForLiteralType(type: string) {
167
- if (type === "number") return 0;
168
- if (type === "boolean") return false;
169
- if (type === "null") return null;
192
+ function defaultForLiteralType(type: string): JxExpressionOperand {
193
+ if (type === "number") {
194
+ return 0;
195
+ }
196
+ if (type === "boolean") {
197
+ return false;
198
+ }
199
+ if (type === "null") {
200
+ return null;
201
+ }
170
202
  return "";
171
203
  }
172
204
 
173
205
  // ─── Hint (one-line summary for signal rows) ────────────────────────────────
174
206
 
175
207
  /**
176
- * @param {any} node
208
+ * @param {unknown} node
177
209
  * @returns {string}
178
210
  */
179
- export function expressionHint(node: any) {
180
- if (!node || !node.operator) return "$expression";
181
- const op = node.operator;
182
- const targetLabel = node.target?.$ref
183
- ? node.target.$ref.replace("#/state/", "")
184
- : node.target?.operator
185
- ? `(${node.target.operator}…)`
186
- : String(node.target ?? "?");
211
+ export function expressionHint(node: unknown) {
212
+ if (!isJsonObject(node) || typeof node.operator !== "string") {
213
+ return "$expression";
214
+ }
215
+ const expr = node as unknown as JxExpressionNode;
216
+ const op = expr.operator;
217
+ const { target } = expr;
218
+ const targetLabel = isRef(target)
219
+ ? target.$ref.replace("#/state/", "")
220
+ : isJsonObject(target) && typeof target.operator === "string"
221
+ ? `(${target.operator}…)`
222
+ : String(target ?? "?");
187
223
 
188
- if (ASSIGN_OPS.has(op) || ONE_ARG_OPS.has(op)) return `${op} ${targetLabel}`;
189
- if (NO_ARG_OPS.has(op)) return `${op}(${targetLabel})`;
190
- if (op === "splice") return `splice(${targetLabel})`;
191
- if (op === "reduce" || op === "map" || op === "filter") return `${op}(${targetLabel})`;
192
- if (UNARY_OPS.has(op)) return `${op}${targetLabel}`;
224
+ if (ASSIGN_OPS.has(op) || ONE_ARG_OPS.has(op)) {
225
+ return `${op} ${targetLabel}`;
226
+ }
227
+ if (NO_ARG_OPS.has(op)) {
228
+ return `${op}(${targetLabel})`;
229
+ }
230
+ if (op === "splice") {
231
+ return `splice(${targetLabel})`;
232
+ }
233
+ if (op === "reduce" || op === "map" || op === "filter") {
234
+ return `${op}(${targetLabel})`;
235
+ }
236
+ if (UNARY_OPS.has(op)) {
237
+ return `${op}${targetLabel}`;
238
+ }
193
239
  return `${targetLabel} ${op} …`;
194
240
  }
195
241
 
@@ -219,7 +265,9 @@ function renderRefPicker(
219
265
  .value=${live(isCustom ? "__custom__" : refVal || "")}
220
266
  @change=${(e: Event) => {
221
267
  const val = (e.target as HTMLInputElement).value;
222
- if (val === "__custom__") return;
268
+ if (val === "__custom__") {
269
+ return;
270
+ }
223
271
  onRefChange(val);
224
272
  }}
225
273
  >
@@ -241,11 +289,11 @@ function renderRefPicker(
241
289
  // ─── Literal Editor ─────────────────────────────────────────────────────────
242
290
 
243
291
  /**
244
- * @param {any} operand
245
- * @param {(newVal: any) => void} onChange
292
+ * @param {unknown} operand
293
+ * @param {(newVal: JxExpressionOperand) => void} onChange
246
294
  * @returns {import("lit-html").TemplateResult}
247
295
  */
248
- function renderLiteralEditor(operand: any, onChange: (newVal: any) => void) {
296
+ function renderLiteralEditor(operand: unknown, onChange: (newVal: JxExpressionOperand) => void) {
249
297
  const type = literalType(operand);
250
298
  return html`
251
299
  <div style="display:flex;gap:4px;align-items:center;flex:1">
@@ -280,7 +328,7 @@ function renderLiteralEditor(operand: any, onChange: (newVal: any) => void) {
280
328
  : type === "boolean"
281
329
  ? html`<sp-checkbox
282
330
  size="s"
283
- ?checked=${!!operand}
331
+ ?checked=${Boolean(operand)}
284
332
  @change=${(e: Event) => onChange((e.target as HTMLInputElement).checked)}
285
333
  >true</sp-checkbox
286
334
  >`
@@ -294,8 +342,8 @@ function renderLiteralEditor(operand: any, onChange: (newVal: any) => void) {
294
342
  // ─── Operand Editor ─────────────────────────────────────────────────────────
295
343
 
296
344
  /**
297
- * @param {any} operand
298
- * @param {(newOperand: any) => void} onChange
345
+ * @param {unknown} operand
346
+ * @param {(newOperand: unknown) => void} onChange
299
347
  * @param {{
300
348
  * stateDefs: string[];
301
349
  * allowEventRef: boolean;
@@ -313,7 +361,7 @@ function renderOperandEditor(
313
361
  depth: number;
314
362
  mustBeRef?: boolean;
315
363
  },
316
- ): import("lit-html").TemplateResult {
364
+ ): TemplateResult {
317
365
  if (opts.mustBeRef) {
318
366
  const refVal = ((operand as Record<string, unknown> | null)?.$ref as string) ?? "";
319
367
  return html`
@@ -356,8 +404,8 @@ function renderOperandEditor(
356
404
  // ─── Splice Args Editor ─────────────────────────────────────────────────────
357
405
 
358
406
  /**
359
- * @param {any[]} args
360
- * @param {(newArgs: any[]) => void} onChange
407
+ * @param {unknown[]} args
408
+ * @param {(newArgs: unknown[]) => void} onChange
361
409
  * @param {{ stateDefs: string[]; allowEventRef: boolean; depth: number }} opts
362
410
  * @returns {import("lit-html").TemplateResult}
363
411
  */
@@ -365,7 +413,7 @@ function renderSpliceArgsEditor(
365
413
  args: unknown[],
366
414
  onChange: (newArgs: unknown[]) => void,
367
415
  opts: { stateDefs: string[]; allowEventRef: boolean; depth: number },
368
- ): import("lit-html").TemplateResult {
416
+ ): TemplateResult {
369
417
  const safeArgs = Array.isArray(args) ? args : [];
370
418
  const labels = ["start", "del", "item"];
371
419
 
@@ -424,8 +472,8 @@ const _operatorMenuCache = OPERATOR_GROUPS.map(
424
472
  // ─── Main Expression Editor ─────────────────────────────────────────────────
425
473
 
426
474
  /**
427
- * @param {any} node
428
- * @param {(node: any) => void} onChange
475
+ * @param {unknown} node
476
+ * @param {(node: unknown) => void} onChange
429
477
  * @param {{
430
478
  * stateDefs: string[];
431
479
  * allowEventRef: boolean;
@@ -437,7 +485,7 @@ export function renderExpressionEditor(
437
485
  node: unknown,
438
486
  onChange: (node: unknown) => void,
439
487
  opts: { stateDefs: string[]; allowEventRef: boolean; depth?: number },
440
- ): import("lit-html").TemplateResult {
488
+ ): TemplateResult {
441
489
  const depth = opts.depth ?? 0;
442
490
  const safeNode: Record<string, unknown> =
443
491
  node && typeof node === "object"
@@ -454,9 +502,9 @@ export function renderExpressionEditor(
454
502
  return html`
455
503
  <div class="expression-editor" style=${nestStyle}>
456
504
  ${renderFieldRow({
457
- prop: "operator",
458
- label: "Operator",
459
505
  hasValue: false,
506
+ label: "Operator",
507
+ prop: "operator",
460
508
  widget: html`
461
509
  <sp-picker
462
510
  size="s"
@@ -464,7 +512,10 @@ export function renderExpressionEditor(
464
512
  @change=${(e: Event) => {
465
513
  const newOp = (e.target as HTMLInputElement).value;
466
514
  const newInfo = operatorInfo(newOp);
467
- const updated: any = { operator: newOp, target: safeNode.target };
515
+ const updated: Record<string, unknown> = {
516
+ operator: newOp,
517
+ target: safeNode.target,
518
+ };
468
519
  if (newInfo.targetMustBeRef && operandMode(safeNode.target) !== "ref") {
469
520
  updated.target = { $ref: "" };
470
521
  }
@@ -478,7 +529,9 @@ export function renderExpressionEditor(
478
529
  updated.value = safeNode.value ?? null;
479
530
  }
480
531
  }
481
- if (newInfo.needsInitial) updated.initial = safeNode.initial ?? 0;
532
+ if (newInfo.needsInitial) {
533
+ updated.initial = safeNode.initial ?? 0;
534
+ }
482
535
  onChange(updated);
483
536
  }}
484
537
  >
@@ -487,9 +540,9 @@ export function renderExpressionEditor(
487
540
  `,
488
541
  })}
489
542
  ${renderFieldRow({
490
- prop: "target",
491
- label: "Target",
492
543
  hasValue: false,
544
+ label: "Target",
545
+ prop: "target",
493
546
  widget: renderOperandEditor(safeNode.target, (t) => onChange({ ...safeNode, target: t }), {
494
547
  ...opts,
495
548
  depth,
@@ -498,9 +551,9 @@ export function renderExpressionEditor(
498
551
  })}
499
552
  ${info.needsValue && !info.valueIsNode && !info.spliceArray
500
553
  ? renderFieldRow({
501
- prop: "value",
502
- label: "Value",
503
554
  hasValue: false,
555
+ label: "Value",
556
+ prop: "value",
504
557
  widget: renderOperandEditor(
505
558
  safeNode.value,
506
559
  (v) => onChange({ ...safeNode, value: v }),
@@ -512,9 +565,9 @@ export function renderExpressionEditor(
512
565
  ? html`
513
566
  <div style="margin-top:4px">
514
567
  ${renderFieldRow({
515
- prop: "value",
516
- label: "Per-item",
517
568
  hasValue: false,
569
+ label: "Per-item",
570
+ prop: "value",
518
571
  widget: nothing,
519
572
  })}
520
573
  ${renderExpressionEditor(
@@ -531,9 +584,9 @@ export function renderExpressionEditor(
531
584
  ? html`
532
585
  <div style="margin-top:4px">
533
586
  ${renderFieldRow({
534
- prop: "value",
535
- label: "Args",
536
587
  hasValue: false,
588
+ label: "Args",
589
+ prop: "value",
537
590
  widget: nothing,
538
591
  })}
539
592
  ${renderSpliceArgsEditor(
@@ -549,9 +602,9 @@ export function renderExpressionEditor(
549
602
  : nothing}
550
603
  ${info.needsInitial
551
604
  ? renderFieldRow({
552
- prop: "initial",
553
- label: "Initial",
554
605
  hasValue: false,
606
+ label: "Initial",
607
+ prop: "initial",
555
608
  widget: renderOperandEditor(
556
609
  safeNode.initial,
557
610
  (v) => onChange({ ...safeNode, initial: v }),
@@ -49,14 +49,19 @@ export function hasDraft(key: string): boolean {
49
49
  /** Record the in-progress value for a field without committing it. */
50
50
  export function setDraft(key: string, value: string): void {
51
51
  const d = _drafts.get(key);
52
- if (d) d.value = value;
53
- else _drafts.set(key, { value });
52
+ if (d) {
53
+ d.value = value;
54
+ } else {
55
+ _drafts.set(key, { value });
56
+ }
54
57
  }
55
58
 
56
59
  /** Discard a field's draft (and cancel any pending debounced commit). */
57
60
  export function clearDraft(key: string): void {
58
61
  const d = _drafts.get(key);
59
- if (d?.timer) clearTimeout(d.timer);
62
+ if (d?.timer) {
63
+ clearTimeout(d.timer);
64
+ }
60
65
  _drafts.delete(key);
61
66
  }
62
67
 
@@ -67,11 +72,17 @@ export function clearDraft(key: string): void {
67
72
  */
68
73
  export function scheduleDraftCommit(key: string, ms: number, commit: (v: string) => void): void {
69
74
  const d = _drafts.get(key);
70
- if (!d) return;
71
- if (d.timer) clearTimeout(d.timer);
75
+ if (!d) {
76
+ return;
77
+ }
78
+ if (d.timer) {
79
+ clearTimeout(d.timer);
80
+ }
72
81
  d.timer = setTimeout(() => {
73
82
  const cur = _drafts.get(key);
74
- if (!cur) return;
83
+ if (!cur) {
84
+ return;
85
+ }
75
86
  delete cur.timer;
76
87
  commit(cur.value);
77
88
  }, ms);
@@ -83,9 +94,13 @@ export function scheduleDraftCommit(key: string, ms: number, commit: (v: string)
83
94
  */
84
95
  export function commitField(key: string, commit: (v: string) => void): void {
85
96
  const d = _drafts.get(key);
86
- if (!d) return;
87
- if (d.timer) clearTimeout(d.timer);
88
- const value = d.value;
97
+ if (!d) {
98
+ return;
99
+ }
100
+ if (d.timer) {
101
+ clearTimeout(d.timer);
102
+ }
103
+ const { value } = d;
89
104
  _drafts.delete(key);
90
105
  commit(value);
91
106
  }
@@ -119,15 +134,17 @@ function makeHandlers(
119
134
  commitMode: "live" | "blur" = "live",
120
135
  ) {
121
136
  return {
122
- onInput: (e: Event) => {
137
+ onCommit: (e: Event) => {
123
138
  const v = (e.target as HTMLInputElement).value;
124
139
  setDraft(key, v);
125
- if (commitMode === "live") scheduleDraftCommit(key, ms, commit);
140
+ commitField(key, commit);
126
141
  },
127
- onCommit: (e: Event) => {
142
+ onInput: (e: Event) => {
128
143
  const v = (e.target as HTMLInputElement).value;
129
144
  setDraft(key, v);
130
- commitField(key, commit);
145
+ if (commitMode === "live") {
146
+ scheduleDraftCommit(key, ms, commit);
147
+ }
131
148
  },
132
149
  onKeydown: (e: KeyboardEvent, multiline: boolean) => {
133
150
  if (e.key === "Enter" && !multiline) {
@@ -158,7 +175,7 @@ export function spTextField(
158
175
  <sp-textfield
159
176
  size=${opts.size ?? "s"}
160
177
  placeholder=${opts.placeholder ?? ""}
161
- ?disabled=${!!opts.disabled}
178
+ ?disabled=${Boolean(opts.disabled)}
162
179
  style=${opts.style ?? ""}
163
180
  .value=${live(getFieldValue(key, value))}
164
181
  @input=${onInput}
@@ -185,7 +202,7 @@ export function spTextArea(
185
202
  multiline
186
203
  size=${opts.size ?? "s"}
187
204
  placeholder=${opts.placeholder ?? ""}
188
- ?disabled=${!!opts.disabled}
205
+ ?disabled=${Boolean(opts.disabled)}
189
206
  style=${opts.style ?? ""}
190
207
  .value=${live(getFieldValue(key, value))}
191
208
  @input=${onInput}
@@ -218,7 +235,7 @@ export function rawTextArea(
218
235
  class="field-input"
219
236
  style=${style}
220
237
  placeholder=${opts.placeholder ?? ""}
221
- ?disabled=${!!opts.disabled}
238
+ ?disabled=${Boolean(opts.disabled)}
222
239
  .value=${live(getFieldValue(key, value))}
223
240
  @input=${onInput}
224
241
  @change=${onCommit}
@@ -241,13 +258,13 @@ export function spNumberField(
241
258
  <sp-number-field
242
259
  size=${opts.size ?? "s"}
243
260
  ?hide-stepper=${opts.hideStepper ?? true}
244
- ?disabled=${!!opts.disabled}
261
+ ?disabled=${Boolean(opts.disabled)}
245
262
  style=${opts.style ?? ""}
246
263
  .value=${live(value !== undefined ? Number(value) : undefined)}
247
264
  @change=${(e: Event) => {
248
265
  const raw = (e.target as HTMLInputElement).value;
249
266
  const n = Number(raw);
250
- commit(raw === "" || isNaN(n) ? undefined : n);
267
+ commit(raw === "" || Number.isNaN(n) ? undefined : n);
251
268
  }}
252
269
  ></sp-number-field>
253
270
  `;
@@ -42,7 +42,7 @@ export function renderFieldRow({
42
42
  }) {
43
43
  return html`
44
44
  <div
45
- class=${classMap({ "style-row": true, "style-row--warning": !!warning })}
45
+ class=${classMap({ "style-row": true, "style-row--warning": Boolean(warning) })}
46
46
  data-prop=${prop}
47
47
  style=${span === 2 ? "grid-column: 1 / -1" : ""}
48
48
  >
package/src/ui/icons.ts CHANGED
@@ -1,11 +1,12 @@
1
- // packages/studio/icons.js
1
+ // Packages/studio/icons.js
2
2
  // Icon templates for style sidebar button groups.
3
3
  // Uses Spectrum workflow icons where available; custom SVGs for flex-specific concepts.
4
4
 
5
5
  import { html } from "lit";
6
+ import type { TemplateResult } from "lit";
6
7
 
7
8
  // Helper for custom filled-rect icons (alignment/justify diagrams) where no Spectrum match exists
8
- const _R = (d: import("lit").TemplateResult) =>
9
+ const _R = (d: TemplateResult) =>
9
10
  html`<svg
10
11
  xmlns="http://www.w3.org/2000/svg"
11
12
  width="16"
@@ -18,7 +19,7 @@ const _R = (d: import("lit").TemplateResult) =>
18
19
  </svg>`;
19
20
 
20
21
  // Helper for custom stroke icons
21
- const _S = (d: import("lit").TemplateResult) =>
22
+ const _S = (d: TemplateResult) =>
22
23
  html`<svg
23
24
  xmlns="http://www.w3.org/2000/svg"
24
25
  width="16"
@@ -33,7 +34,7 @@ const _S = (d: import("lit").TemplateResult) =>
33
34
  ${d}
34
35
  </svg>`;
35
36
 
36
- const icons = {
37
+ export const icons = {
37
38
  // ─── Arrows — flexDirection ───
38
39
  "arrow-right": html`<sp-icon-arrow-right slot="icon"></sp-icon-arrow-right>`,
39
40
  "arrow-left": html`<sp-icon-arrow-left slot="icon"></sp-icon-arrow-left>`,
@@ -79,5 +80,3 @@ const icons = {
79
80
  "display-inline": html`<sp-icon-remove slot="icon"></sp-icon-remove>`,
80
81
  "display-none": html`<sp-icon-visibility-off slot="icon"></sp-icon-visibility-off>`,
81
82
  };
82
-
83
- export default icons;