@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
package/src/types.ts CHANGED
@@ -1,5 +1,10 @@
1
1
  /// <reference lib="dom" />
2
- import type { JxMutableNode, JxPath, ProjectConfig } from "@jxsuite/schema/types";
2
+ import type {
3
+ JsonValue as SchemaJsonValue,
4
+ JxMutableNode,
5
+ JxPath,
6
+ ProjectConfig,
7
+ } from "@jxsuite/schema/types";
3
8
 
4
9
  // ─── Git & Platform Types ───────────────────────────────────────────────────
5
10
 
@@ -34,7 +39,7 @@ export interface ComponentMeta {
34
39
  tagName: string;
35
40
  $id?: string | null;
36
41
  path: string;
37
- props?: { name: string; type?: string; default?: unknown }[];
42
+ props?: { name: string; type?: string; default?: JsonValue; [k: string]: unknown }[];
38
43
  hasElements?: boolean;
39
44
  }
40
45
 
@@ -60,12 +65,12 @@ export interface DirEntry {
60
65
  export interface StudioPlatform {
61
66
  id: string;
62
67
  projectRoot: string;
63
- activate(root?: string): Promise<void>;
64
- openProject(): Promise<{
68
+ activate: (root?: string) => Promise<void>;
69
+ openProject: () => Promise<{
65
70
  config: ProjectConfig;
66
71
  handle: { root: string; name: string; projectConfig: ProjectConfig };
67
72
  } | null>;
68
- probeRootProject(): Promise<{
73
+ probeRootProject: () => Promise<{
69
74
  meta: { root: string; name: string };
70
75
  info: {
71
76
  isSiteProject: boolean;
@@ -73,60 +78,60 @@ export interface StudioPlatform {
73
78
  directories?: string[];
74
79
  };
75
80
  } | null>;
76
- listDirectory(dir: string): Promise<DirEntry[]>;
77
- readFile(path: string): Promise<string>;
78
- writeFile(path: string, content: string): Promise<void>;
79
- uploadFile(path: string, data: string | File | Blob | ArrayBuffer): Promise<unknown>;
80
- deleteFile(path: string): Promise<void>;
81
- renameFile(from: string, to: string): Promise<void>;
82
- createDirectory(path: string): Promise<void>;
83
- discoverComponents(dir?: string): Promise<ComponentMeta[]>;
84
- addPackage(name: string): Promise<unknown>;
85
- removePackage(name: string): Promise<unknown>;
86
- listPackages(): Promise<PackageInfo[]>;
87
- codeService(action: string, payload: unknown): Promise<CodeServiceResult | null>;
88
- resolveSiteContext(filePath: string): Promise<{
81
+ listDirectory: (dir: string) => Promise<DirEntry[]>;
82
+ readFile: (path: string) => Promise<string>;
83
+ writeFile: (path: string, content: string) => Promise<void>;
84
+ uploadFile: (path: string, data: string | File | Blob | ArrayBuffer) => Promise<unknown>;
85
+ deleteFile: (path: string) => Promise<void>;
86
+ renameFile: (from: string, to: string) => Promise<void>;
87
+ createDirectory: (path: string) => Promise<void>;
88
+ discoverComponents: (dir?: string) => Promise<ComponentMeta[]>;
89
+ addPackage: (name: string) => Promise<unknown>;
90
+ removePackage: (name: string) => Promise<unknown>;
91
+ listPackages: () => Promise<PackageInfo[]>;
92
+ codeService: (action: string, payload: unknown) => Promise<CodeServiceResult | null>;
93
+ resolveSiteContext: (filePath: string) => Promise<{
89
94
  sitePath: string | null;
90
95
  projectConfig?: ProjectConfig;
91
96
  fileRelPath?: string;
92
97
  }>;
93
- locateFile(name: string): Promise<string | null>;
94
- searchFiles(query: string, extensions?: string[]): Promise<DirEntry[]>;
98
+ locateFile: (name: string) => Promise<string | null>;
99
+ searchFiles: (query: string, extensions?: string[]) => Promise<DirEntry[]>;
95
100
  /** List the project's registered format classes (auto-discovered from imports). */
96
- listFormats?(): Promise<unknown[]>;
101
+ listFormats?: () => Promise<unknown[]>;
97
102
  /** Invoke a format capability (parse/serialize) — { format, action, source?, doc?, options? }. */
98
- formatAction?(payload: Record<string, unknown>): Promise<unknown>;
99
- fetchPluginSchema(src: string, prototype?: string, base?: string): Promise<unknown>;
100
- gitStatus(): Promise<GitStatusResult>;
101
- gitBranches(): Promise<GitBranchesResult>;
102
- gitLog(limit?: number): Promise<GitLogEntry[]>;
103
- gitStage(files: string[]): Promise<void>;
104
- gitUnstage(files: string[]): Promise<void>;
105
- gitCommit(message: string): Promise<void>;
106
- gitPush(opts?: { setUpstream?: boolean }): Promise<void>;
107
- gitPull(): Promise<void>;
108
- gitFetch(): Promise<void>;
109
- gitCheckout(branch: string): Promise<void>;
110
- gitCreateBranch(name: string): Promise<void>;
111
- gitDiff(path?: string): Promise<string>;
112
- gitShow(opts: { path: string; ref?: string }): Promise<string>;
113
- gitDiscard(files: string[]): Promise<void>;
114
- gitClone?(url: string): Promise<{ ok: boolean; root: string }>;
115
- gitInit(): Promise<void>;
116
- gitAddRemote(name: string, url: string): Promise<void>;
117
- createProject(opts: {
103
+ formatAction?: (payload: Record<string, unknown>) => Promise<unknown>;
104
+ fetchPluginSchema: (src: string, prototype?: string, base?: string) => Promise<unknown>;
105
+ gitStatus: () => Promise<GitStatusResult>;
106
+ gitBranches: () => Promise<GitBranchesResult>;
107
+ gitLog: (limit?: number) => Promise<GitLogEntry[]>;
108
+ gitStage: (files: string[]) => Promise<void>;
109
+ gitUnstage: (files: string[]) => Promise<void>;
110
+ gitCommit: (message: string) => Promise<void>;
111
+ gitPush: (opts?: { setUpstream?: boolean }) => Promise<void>;
112
+ gitPull: () => Promise<void>;
113
+ gitFetch: () => Promise<void>;
114
+ gitCheckout: (branch: string) => Promise<void>;
115
+ gitCreateBranch: (name: string) => Promise<void>;
116
+ gitDiff: (path?: string) => Promise<string>;
117
+ gitShow: (opts: { path: string; ref?: string }) => Promise<string>;
118
+ gitDiscard: (files: string[]) => Promise<void>;
119
+ gitClone?: (url: string) => Promise<{ ok: boolean; root: string }>;
120
+ gitInit: () => Promise<void>;
121
+ gitAddRemote: (name: string, url: string) => Promise<void>;
122
+ createProject: (opts: {
118
123
  name: string;
119
124
  description?: string;
120
125
  url?: string;
121
126
  adapter?: string;
122
127
  directory: string;
123
- }): Promise<{ root: string; config: ProjectConfig }>;
124
- aiAuthStatus(): Promise<{ authenticated: boolean; error?: string }>;
125
- aiCreateSession(opts: { message: string; systemPrompt?: string }): Promise<{ id: string }>;
126
- aiSendMessage(id: string, message: string): Promise<void>;
127
- aiStreamUrl(id: string): string | Promise<string>;
128
- aiStopSession(id: string): Promise<void>;
129
- aiDeleteSession(id: string): Promise<void>;
128
+ }) => Promise<{ root: string; config: ProjectConfig }>;
129
+ aiAuthStatus: () => Promise<{ authenticated: boolean; error?: string }>;
130
+ aiCreateSession: (opts: { message: string; systemPrompt?: string }) => Promise<{ id: string }>;
131
+ aiSendMessage: (id: string, message: string) => Promise<void>;
132
+ aiStreamUrl: (id: string) => string | Promise<string>;
133
+ aiStopSession: (id: string) => Promise<void>;
134
+ aiDeleteSession: (id: string) => Promise<void>;
130
135
  }
131
136
 
132
137
  // ─── Studio Types ───────────────────────────────────────────────────────────
@@ -167,8 +172,8 @@ export interface GitDiffState {
167
172
  originalContent: string;
168
173
  currentContent: string;
169
174
  fileStatus: string;
170
- originalDoc?: unknown;
171
- currentDoc?: unknown;
175
+ originalDoc?: JxMutableNode;
176
+ currentDoc?: JxMutableNode;
172
177
  original?: unknown;
173
178
  }
174
179
 
@@ -191,4 +196,8 @@ export interface ProjectState {
191
196
  [key: string]: unknown;
192
197
  }
193
198
 
194
- export type JsonValue = string | number | boolean | object | null | undefined;
199
+ /**
200
+ * A JSON document value, or `undefined` to signal property removal in the mutators. Re-uses the
201
+ * schema's precise recursive JSON model.
202
+ */
203
+ export type JsonValue = SchemaJsonValue | undefined;
@@ -9,7 +9,7 @@
9
9
  import { html, nothing } from "lit-html";
10
10
  import { classMap } from "lit-html/directives/class-map.js";
11
11
  import { abbreviateValue, kebabToLabel } from "../utils/studio-utils";
12
- import icons from "./icons";
12
+ import { icons } from "./icons";
13
13
 
14
14
  /**
15
15
  * Render a button group widget with optional overflow menu.
@@ -74,8 +74,9 @@ export function renderButtonGroup(
74
74
  <sp-popover>
75
75
  <sp-menu
76
76
  @change=${(e: Event) => {
77
- if ((e.target as HTMLInputElement).value)
77
+ if ((e.target as HTMLInputElement).value) {
78
78
  onChange((e.target as HTMLInputElement).value);
79
+ }
79
80
  }}
80
81
  >
81
82
  <sp-menu-item value="__none__">—</sp-menu-item>
@@ -29,7 +29,9 @@ interface ColorVar {
29
29
  /** Extract --color-* CSS custom properties from the effective (site + document) style. */
30
30
  function getColorVars() {
31
31
  const style = getEffectiveStyle(activeTab.value?.doc.document?.style);
32
- if (!style) return [];
32
+ if (!style) {
33
+ return [];
34
+ }
33
35
  const vars = [];
34
36
  for (const [k, v] of Object.entries(style)) {
35
37
  if (k.startsWith("--color") && (typeof v === "string" || typeof v === "number")) {
@@ -49,27 +51,37 @@ function varToLabel(name: string) {
49
51
 
50
52
  /** Resolve a color value for display — if it's a var() reference, look up the actual color. */
51
53
  function resolveColorForDisplay(val: string | number | undefined) {
52
- if (!val) return "transparent";
54
+ if (!val) {
55
+ return "transparent";
56
+ }
53
57
  const s = String(val);
54
58
  const m = s.match(/^var\((--[^)]+)\)$/);
55
59
  if (m) {
56
60
  const style = getEffectiveStyle(activeTab.value?.doc.document?.style);
57
61
  const resolved = style?.[m[1]];
58
- if (typeof resolved === "string") return resolved;
62
+ if (typeof resolved === "string") {
63
+ return resolved;
64
+ }
59
65
  return "transparent";
60
66
  }
61
67
  return s;
62
68
  }
63
69
 
64
70
  function safeColor(val: string | number | undefined) {
65
- if (!val) return "transparent";
71
+ if (!val) {
72
+ return "transparent";
73
+ }
66
74
  return resolveColorForDisplay(val);
67
75
  }
68
76
 
69
77
  /** Normalize a color string to include # prefix for hex values. */
70
78
  function normalizeHex(c: string) {
71
- if (!c) return c;
72
- if (c.startsWith("var(") || c.startsWith("rgb") || c.startsWith("hsl")) return c;
79
+ if (!c) {
80
+ return c;
81
+ }
82
+ if (c.startsWith("var(") || c.startsWith("rgb") || c.startsWith("hsl")) {
83
+ return c;
84
+ }
73
85
  return c.replace(/^#?/, "#");
74
86
  }
75
87
 
@@ -83,9 +95,13 @@ function matchesColorVar(
83
95
  value: string | number | undefined,
84
96
  colorVars: { name: string; value: string }[],
85
97
  ) {
86
- if (!value || typeof value !== "string") return null;
98
+ if (!value || typeof value !== "string") {
99
+ return null;
100
+ }
87
101
  const m = value.match(/^var\((--[^)]+)\)$/);
88
- if (!m) return null;
102
+ if (!m) {
103
+ return null;
104
+ }
89
105
  return colorVars.find((cv) => cv.name === m[1]) || null;
90
106
  }
91
107
 
@@ -94,8 +110,8 @@ function matchesColorVar(
94
110
  export class JxColorPopover extends LitElement {
95
111
  static properties = {
96
112
  color: { type: String },
97
- displayColor: { type: String, attribute: false },
98
113
  colorVars: { attribute: false },
114
+ displayColor: { attribute: false, type: String },
99
115
  };
100
116
 
101
117
  declare color: string;
@@ -132,29 +148,31 @@ export class JxColorPopover extends LitElement {
132
148
  const color = normalizeHex(String((e.target as HTMLElement & { color: string }).color));
133
149
  this.displayColor = color;
134
150
  this.color = color;
135
- this.dispatchEvent(new CustomEvent("color-change", { detail: color, bubbles: true }));
151
+ this.dispatchEvent(new CustomEvent("color-change", { bubbles: true, detail: color }));
136
152
  }
137
153
 
138
154
  _handleSlider(e: Event) {
139
155
  const color = normalizeHex(String((e.target as HTMLElement & { color: string }).color));
140
156
  this.displayColor = color;
141
157
  this.color = color;
142
- this.dispatchEvent(new CustomEvent("color-change", { detail: color, bubbles: true }));
158
+ this.dispatchEvent(new CustomEvent("color-change", { bubbles: true, detail: color }));
143
159
  }
144
160
 
145
161
  _handleText(e: Event) {
146
162
  const val = (e.target as HTMLInputElement).value.trim();
147
- if (!val) return;
163
+ if (!val) {
164
+ return;
165
+ }
148
166
  this.displayColor = val;
149
167
  this.color = val;
150
- this.dispatchEvent(new CustomEvent("color-change", { detail: val, bubbles: true }));
168
+ this.dispatchEvent(new CustomEvent("color-change", { bubbles: true, detail: val }));
151
169
  }
152
170
 
153
171
  _handleSwatch(e: Event, varName: string) {
154
172
  e.stopPropagation();
155
173
  const varRef = `var(${varName})`;
156
174
  this.color = varRef;
157
- this.dispatchEvent(new CustomEvent("color-change", { detail: varRef, bubbles: true }));
175
+ this.dispatchEvent(new CustomEvent("color-change", { bubbles: true, detail: varRef }));
158
176
  }
159
177
 
160
178
  render() {
@@ -306,5 +324,5 @@ export function renderColorSelector(
306
324
 
307
325
  /** Whether any color popover is currently open. */
308
326
  export function isColorPopoverOpen() {
309
- return !!document.querySelector(".style-input-color sp-overlay[open]");
327
+ return Boolean(document.querySelector(".style-input-color sp-overlay[open]"));
310
328
  }