@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
@@ -26,8 +26,12 @@ export function createDevServerPlatform() {
26
26
  */
27
27
  function serverPath(rel: string) {
28
28
  const r = rel.replaceAll("\\", "/");
29
- if (!_projectRoot) return r;
30
- if (r === ".") return _projectRoot;
29
+ if (!_projectRoot) {
30
+ return r;
31
+ }
32
+ if (r === ".") {
33
+ return _projectRoot;
34
+ }
31
35
  return `${_projectRoot}/${r}`;
32
36
  }
33
37
 
@@ -38,8 +42,10 @@ export function createDevServerPlatform() {
38
42
  */
39
43
  function stripRoot(path: string) {
40
44
  const p = path.replaceAll("\\", "/");
41
- if (!_projectRoot) return p;
42
- return p.startsWith(_projectRoot + "/") ? p.slice(_projectRoot.length + 1) : p;
45
+ if (!_projectRoot) {
46
+ return p;
47
+ }
48
+ return p.startsWith(`${_projectRoot}/`) ? p.slice(_projectRoot.length + 1) : p;
43
49
  }
44
50
 
45
51
  return {
@@ -51,7 +57,9 @@ export function createDevServerPlatform() {
51
57
  },
52
58
  set projectRoot(v) {
53
59
  _projectRoot = v || "";
54
- if (_projectRoot) this.activate(_projectRoot);
60
+ if (_projectRoot) {
61
+ this.activate(_projectRoot);
62
+ }
55
63
  },
56
64
 
57
65
  /**
@@ -63,9 +71,9 @@ export function createDevServerPlatform() {
63
71
  async activate(root?: string) {
64
72
  const r = root ?? _projectRoot;
65
73
  await fetch("/__studio/activate", {
66
- method: "POST",
67
- headers: { "Content-Type": "application/json" },
68
74
  body: JSON.stringify({ root: r }),
75
+ headers: { "Content-Type": "application/json" },
76
+ method: "POST",
69
77
  });
70
78
  },
71
79
 
@@ -81,13 +89,15 @@ export function createDevServerPlatform() {
81
89
  try {
82
90
  dirHandle = await (
83
91
  window as unknown as {
84
- showDirectoryPicker(opts: { mode: string }): Promise<FileSystemDirectoryHandle>;
92
+ showDirectoryPicker: (opts: { mode: string }) => Promise<FileSystemDirectoryHandle>;
85
93
  }
86
94
  ).showDirectoryPicker({ mode: "readwrite" });
87
- } catch (e) {
95
+ } catch (error) {
88
96
  // User cancelled the picker
89
- if ((e as Error).name === "AbortError") return null;
90
- throw e;
97
+ if (error instanceof Error && error.name === "AbortError") {
98
+ return null;
99
+ }
100
+ throw error;
91
101
  }
92
102
 
93
103
  // Read project.json from the chosen directory
@@ -103,7 +113,9 @@ export function createDevServerPlatform() {
103
113
 
104
114
  // Resolve server-relative path by matching against known sites
105
115
  const sitesRes = await fetch("/__studio/sites");
106
- if (!sitesRes.ok) throw new Error("Failed to fetch site list from server");
116
+ if (!sitesRes.ok) {
117
+ throw new Error("Failed to fetch site list from server");
118
+ }
107
119
  const sites = await sitesRes.json();
108
120
  const match = sites.find(
109
121
  /** @param {{ config: unknown; path: string }} s */ (s: {
@@ -117,9 +129,13 @@ export function createDevServerPlatform() {
117
129
  const findRes = await fetch(
118
130
  `/__studio/find-project?name=${encodeURIComponent(dirHandle.name)}`,
119
131
  );
120
- if (!findRes.ok) throw new Error("Could not locate project on disk");
132
+ if (!findRes.ok) {
133
+ throw new Error("Could not locate project on disk");
134
+ }
121
135
  const found = await findRes.json();
122
- if (!found.path) throw new Error(`Could not find project directory "${dirHandle.name}"`);
136
+ if (!found.path) {
137
+ throw new Error(`Could not find project directory "${dirHandle.name}"`);
138
+ }
123
139
  _projectRoot = found.path;
124
140
  } else {
125
141
  _projectRoot = match.path;
@@ -131,9 +147,9 @@ export function createDevServerPlatform() {
131
147
  return {
132
148
  config,
133
149
  handle: {
134
- root: _projectRoot,
135
150
  name: config.name || _projectRoot.split("/").pop(),
136
151
  projectConfig: config,
152
+ root: _projectRoot,
137
153
  },
138
154
  };
139
155
  },
@@ -148,9 +164,9 @@ export function createDevServerPlatform() {
148
164
  fetch("/__studio/project"),
149
165
  fetch("/__studio/project-info?dir=."),
150
166
  ]);
151
- const meta = projectRes.ok ? await projectRes.json() : { root: ".", name: "project" };
167
+ const meta = projectRes.ok ? await projectRes.json() : { name: "project", root: "." };
152
168
  const info = infoRes.ok ? await infoRes.json() : { isSiteProject: false };
153
- return { meta, info };
169
+ return { info, meta };
154
170
  } catch {
155
171
  return null;
156
172
  }
@@ -175,9 +191,9 @@ export function createDevServerPlatform() {
175
191
  directory: string;
176
192
  }) {
177
193
  const res = await fetch("/__studio/create-project", {
178
- method: "POST",
179
- headers: { "Content-Type": "application/json" },
180
194
  body: JSON.stringify(opts),
195
+ headers: { "Content-Type": "application/json" },
196
+ method: "POST",
181
197
  });
182
198
  if (!res.ok) {
183
199
  const data = await res.json();
@@ -191,16 +207,22 @@ export function createDevServerPlatform() {
191
207
  /** @param {string} dir */
192
208
  async listDirectory(dir: string) {
193
209
  const res = await fetch(`/__studio/files?dir=${encodeURIComponent(serverPath(dir))}`);
194
- if (!res.ok) throw new Error(`Failed to list directory: ${dir}`);
210
+ if (!res.ok) {
211
+ throw new Error(`Failed to list directory: ${dir}`);
212
+ }
195
213
  const entries = await res.json();
196
- for (const e of entries) e.path = stripRoot(e.path);
214
+ for (const e of entries) {
215
+ e.path = stripRoot(e.path);
216
+ }
197
217
  return entries;
198
218
  },
199
219
 
200
220
  /** @param {string} path */
201
221
  async readFile(path: string) {
202
222
  const res = await fetch(`/__studio/file?path=${encodeURIComponent(serverPath(path))}`);
203
- if (!res.ok) throw new Error(`Failed to read file: ${path}`);
223
+ if (!res.ok) {
224
+ throw new Error(`Failed to read file: ${path}`);
225
+ }
204
226
  const data = await res.json();
205
227
  return data.content;
206
228
  },
@@ -211,10 +233,12 @@ export function createDevServerPlatform() {
211
233
  */
212
234
  async writeFile(path: string, content: string) {
213
235
  const res = await fetch(`/__studio/file?path=${encodeURIComponent(serverPath(path))}`, {
214
- method: "PUT",
215
236
  body: content,
237
+ method: "PUT",
216
238
  });
217
- if (!res.ok) throw new Error(`Failed to write file: ${path}`);
239
+ if (!res.ok) {
240
+ throw new Error(`Failed to write file: ${path}`);
241
+ }
218
242
  },
219
243
 
220
244
  /**
@@ -223,12 +247,14 @@ export function createDevServerPlatform() {
223
247
  * @param {string} path — project-relative destination path
224
248
  * @param {File | Blob | ArrayBuffer} data — file content
225
249
  */
226
- async uploadFile(path: string, data: File | Blob | ArrayBuffer) {
250
+ async uploadFile(path: string, data: string | File | Blob | ArrayBuffer) {
227
251
  const res = await fetch(
228
252
  `/__studio/file/upload?path=${encodeURIComponent(serverPath(path))}`,
229
- { method: "POST", body: data },
253
+ { body: data, method: "POST" },
230
254
  );
231
- if (!res.ok) throw new Error(`Upload failed: ${path}`);
255
+ if (!res.ok) {
256
+ throw new Error(`Upload failed: ${path}`);
257
+ }
232
258
  return await res.json();
233
259
  },
234
260
 
@@ -237,7 +263,9 @@ export function createDevServerPlatform() {
237
263
  const res = await fetch(`/__studio/file?path=${encodeURIComponent(serverPath(path))}`, {
238
264
  method: "DELETE",
239
265
  });
240
- if (!res.ok && res.status !== 404) throw new Error(`Failed to delete file: ${path}`);
266
+ if (!res.ok && res.status !== 404) {
267
+ throw new Error(`Failed to delete file: ${path}`);
268
+ }
241
269
  },
242
270
 
243
271
  /**
@@ -246,11 +274,13 @@ export function createDevServerPlatform() {
246
274
  */
247
275
  async renameFile(from: string, to: string) {
248
276
  const res = await fetch("/__studio/file/rename", {
249
- method: "POST",
250
- headers: { "Content-Type": "application/json" },
251
277
  body: JSON.stringify({ from: serverPath(from), to: serverPath(to) }),
278
+ headers: { "Content-Type": "application/json" },
279
+ method: "POST",
252
280
  });
253
- if (!res.ok) throw new Error(`Failed to rename: ${from} → ${to}`);
281
+ if (!res.ok) {
282
+ throw new Error(`Failed to rename: ${from} → ${to}`);
283
+ }
254
284
  },
255
285
 
256
286
  /** @param {string} _path */
@@ -258,18 +288,22 @@ export function createDevServerPlatform() {
258
288
  // The server creates directories implicitly when writing files.
259
289
  // Write a placeholder and delete it, or rely on mkdir behavior.
260
290
  // For now, use the writeFile + delete approach if directory creation
261
- // is explicitly needed. The server's writeFile already calls mkdir().
291
+ // Is explicitly needed. The server's writeFile already calls mkdir().
262
292
  },
263
293
 
264
294
  // ─── Component discovery ──────────────────────────────────────────────
265
295
 
266
296
  /** @param {string} dir */
267
- async discoverComponents(dir: string) {
297
+ async discoverComponents(dir?: string) {
268
298
  const scanDir = dir || _projectRoot;
269
- if (!scanDir) return [];
299
+ if (!scanDir) {
300
+ return [];
301
+ }
270
302
  const url = `/__studio/components?dir=${encodeURIComponent(scanDir)}`;
271
303
  const res = await fetch(url);
272
- if (!res.ok) return [];
304
+ if (!res.ok) {
305
+ return [];
306
+ }
273
307
  return await res.json();
274
308
  },
275
309
 
@@ -278,28 +312,34 @@ export function createDevServerPlatform() {
278
312
  /** @param {string} name */
279
313
  async addPackage(name: string) {
280
314
  const res = await fetch("/__studio/packages/add", {
281
- method: "POST",
282
- headers: { "Content-Type": "application/json" },
283
315
  body: JSON.stringify({ name }),
316
+ headers: { "Content-Type": "application/json" },
317
+ method: "POST",
284
318
  });
285
- if (!res.ok) throw new Error(await res.text());
319
+ if (!res.ok) {
320
+ throw new Error(await res.text());
321
+ }
286
322
  return await res.json();
287
323
  },
288
324
 
289
325
  /** @param {string} name */
290
326
  async removePackage(name: string) {
291
327
  const res = await fetch("/__studio/packages/remove", {
292
- method: "POST",
293
- headers: { "Content-Type": "application/json" },
294
328
  body: JSON.stringify({ name }),
329
+ headers: { "Content-Type": "application/json" },
330
+ method: "POST",
295
331
  });
296
- if (!res.ok) throw new Error(await res.text());
332
+ if (!res.ok) {
333
+ throw new Error(await res.text());
334
+ }
297
335
  return await res.json();
298
336
  },
299
337
 
300
338
  async listPackages() {
301
339
  const res = await fetch("/__studio/packages");
302
- if (!res.ok) return [];
340
+ if (!res.ok) {
341
+ return [];
342
+ }
303
343
  return await res.json();
304
344
  },
305
345
 
@@ -312,11 +352,13 @@ export function createDevServerPlatform() {
312
352
  async codeService(action: string, payload: unknown) {
313
353
  try {
314
354
  const res = await fetch(`/__studio/code/${action}`, {
315
- method: "POST",
316
- headers: { "Content-Type": "application/json" },
317
355
  body: JSON.stringify(payload),
356
+ headers: { "Content-Type": "application/json" },
357
+ method: "POST",
318
358
  });
319
- if (!res.ok) return null;
359
+ if (!res.ok) {
360
+ return null;
361
+ }
320
362
  return await res.json();
321
363
  } catch {
322
364
  return null;
@@ -333,7 +375,9 @@ export function createDevServerPlatform() {
333
375
  */
334
376
  async resolveSiteContext(filePath: string) {
335
377
  const res = await fetch(`/__studio/resolve-site?path=${encodeURIComponent(filePath)}`);
336
- if (!res.ok) return { sitePath: null };
378
+ if (!res.ok) {
379
+ return { sitePath: null };
380
+ }
337
381
  return await res.json();
338
382
  },
339
383
 
@@ -343,11 +387,14 @@ export function createDevServerPlatform() {
343
387
  async locateFile(name: string) {
344
388
  try {
345
389
  const res = await fetch("/__studio/locate", {
346
- method: "POST",
347
- headers: { "Content-Type": "application/json" },
348
390
  body: JSON.stringify({ name }),
391
+ headers: { "Content-Type": "application/json" },
392
+ method: "POST",
349
393
  });
350
- if (res.ok) return (await res.json()).path || null;
394
+ if (res.ok) {
395
+ const body = await res.json();
396
+ return body.path || null;
397
+ }
351
398
  } catch {}
352
399
  return null;
353
400
  },
@@ -362,9 +409,13 @@ export function createDevServerPlatform() {
362
409
  const res = await fetch(
363
410
  `/__studio/files?dir=${encodeURIComponent(serverPath("."))}&glob=${encodeURIComponent(glob)}`,
364
411
  );
365
- if (!res.ok) return [];
412
+ if (!res.ok) {
413
+ return [];
414
+ }
366
415
  const entries = await res.json();
367
- for (const e of entries) e.path = stripRoot(e.path);
416
+ for (const e of entries) {
417
+ e.path = stripRoot(e.path);
418
+ }
368
419
  return entries;
369
420
  },
370
421
 
@@ -373,8 +424,11 @@ export function createDevServerPlatform() {
373
424
  /** List the project's registered format classes (auto-discovered from imports). */
374
425
  async listFormats() {
375
426
  const res = await fetch(`/__studio/formats?dir=${encodeURIComponent(serverPath("."))}`);
376
- if (!res.ok) return [];
377
- return (await res.json()).formats ?? [];
427
+ if (!res.ok) {
428
+ return [];
429
+ }
430
+ const body = await res.json();
431
+ return body.formats ?? [];
378
432
  },
379
433
 
380
434
  /**
@@ -384,12 +438,14 @@ export function createDevServerPlatform() {
384
438
  */
385
439
  async formatAction(payload: Record<string, unknown>) {
386
440
  const res = await fetch("/__studio/format", {
387
- method: "POST",
388
- headers: { "Content-Type": "application/json" },
389
441
  body: JSON.stringify({ ...payload, dir: serverPath(".") }),
442
+ headers: { "Content-Type": "application/json" },
443
+ method: "POST",
390
444
  });
391
445
  const data = await res.json();
392
- if (!res.ok) throw new Error(data.error || "Format action failed");
446
+ if (!res.ok) {
447
+ throw new Error(data.error || "Format action failed");
448
+ }
393
449
  return data.result;
394
450
  },
395
451
 
@@ -400,12 +456,18 @@ export function createDevServerPlatform() {
400
456
  * @param {string} prototype
401
457
  * @param {string} base
402
458
  */
403
- async fetchPluginSchema(src: string, prototype: string, base: string) {
459
+ async fetchPluginSchema(src: string, prototype?: string, base?: string) {
404
460
  const params = new URLSearchParams({ src });
405
- if (prototype) params.set("prototype", prototype);
406
- if (base) params.set("base", base);
461
+ if (prototype) {
462
+ params.set("prototype", prototype);
463
+ }
464
+ if (base) {
465
+ params.set("base", base);
466
+ }
407
467
  const res = await fetch(`/__studio/plugin-schema?${params}`);
408
- if (!res.ok) return null;
468
+ if (!res.ok) {
469
+ return null;
470
+ }
409
471
  const { schema } = await res.json();
410
472
  return schema;
411
473
  },
@@ -414,13 +476,17 @@ export function createDevServerPlatform() {
414
476
 
415
477
  async gitStatus() {
416
478
  const res = await fetch("/__studio/git/status");
417
- if (!res.ok) throw new Error(await res.text());
479
+ if (!res.ok) {
480
+ throw new Error(await res.text());
481
+ }
418
482
  return await res.json();
419
483
  },
420
484
 
421
485
  async gitBranches() {
422
486
  const res = await fetch("/__studio/git/branches");
423
- if (!res.ok) throw new Error(await res.text());
487
+ if (!res.ok) {
488
+ throw new Error(await res.text());
489
+ }
424
490
  return await res.json();
425
491
  },
426
492
 
@@ -428,101 +494,133 @@ export function createDevServerPlatform() {
428
494
  async gitLog(limit?: number) {
429
495
  const q = limit ? `?limit=${limit}` : "";
430
496
  const res = await fetch(`/__studio/git/log${q}`);
431
- if (!res.ok) throw new Error(await res.text());
497
+ if (!res.ok) {
498
+ throw new Error(await res.text());
499
+ }
432
500
  return await res.json();
433
501
  },
434
502
 
435
503
  /** @param {string[]} files */
436
504
  async gitStage(files: string[]) {
437
505
  const res = await fetch("/__studio/git/stage", {
438
- method: "POST",
439
- headers: { "Content-Type": "application/json" },
440
506
  body: JSON.stringify({ files }),
507
+ headers: { "Content-Type": "application/json" },
508
+ method: "POST",
441
509
  });
442
- if (!res.ok) throw new Error((await res.json()).error);
510
+ if (!res.ok) {
511
+ const body = await res.json();
512
+ throw new Error(body.error);
513
+ }
443
514
  return await res.json();
444
515
  },
445
516
 
446
517
  /** @param {string[]} files */
447
518
  async gitUnstage(files: string[]) {
448
519
  const res = await fetch("/__studio/git/unstage", {
449
- method: "POST",
450
- headers: { "Content-Type": "application/json" },
451
520
  body: JSON.stringify({ files }),
521
+ headers: { "Content-Type": "application/json" },
522
+ method: "POST",
452
523
  });
453
- if (!res.ok) throw new Error((await res.json()).error);
524
+ if (!res.ok) {
525
+ const body = await res.json();
526
+ throw new Error(body.error);
527
+ }
454
528
  return await res.json();
455
529
  },
456
530
 
457
531
  /** @param {string} message */
458
532
  async gitCommit(message: string) {
459
533
  const res = await fetch("/__studio/git/commit", {
460
- method: "POST",
461
- headers: { "Content-Type": "application/json" },
462
534
  body: JSON.stringify({ message }),
535
+ headers: { "Content-Type": "application/json" },
536
+ method: "POST",
463
537
  });
464
- if (!res.ok) throw new Error((await res.json()).error);
538
+ if (!res.ok) {
539
+ const body = await res.json();
540
+ throw new Error(body.error);
541
+ }
465
542
  return await res.json();
466
543
  },
467
544
 
468
545
  /** @param {{ setUpstream?: boolean }} [opts] */
469
546
  async gitPush(opts?: { setUpstream?: boolean }) {
470
547
  const res = await fetch("/__studio/git/push", {
471
- method: "POST",
472
- headers: { "Content-Type": "application/json" },
473
548
  body: JSON.stringify(opts || {}),
549
+ headers: { "Content-Type": "application/json" },
550
+ method: "POST",
474
551
  });
475
- if (!res.ok) throw new Error((await res.json()).error);
552
+ if (!res.ok) {
553
+ const body = await res.json();
554
+ throw new Error(body.error);
555
+ }
476
556
  return await res.json();
477
557
  },
478
558
 
479
559
  async gitPull() {
480
560
  const res = await fetch("/__studio/git/pull", { method: "POST" });
481
- if (!res.ok) throw new Error((await res.json()).error);
561
+ if (!res.ok) {
562
+ const body = await res.json();
563
+ throw new Error(body.error);
564
+ }
482
565
  return await res.json();
483
566
  },
484
567
 
485
568
  async gitFetch() {
486
569
  const res = await fetch("/__studio/git/fetch", { method: "POST" });
487
- if (!res.ok) throw new Error((await res.json()).error);
570
+ if (!res.ok) {
571
+ const body = await res.json();
572
+ throw new Error(body.error);
573
+ }
488
574
  return await res.json();
489
575
  },
490
576
 
491
577
  /** @param {string} branch */
492
578
  async gitCheckout(branch: string) {
493
579
  const res = await fetch("/__studio/git/checkout", {
494
- method: "POST",
495
- headers: { "Content-Type": "application/json" },
496
580
  body: JSON.stringify({ branch }),
581
+ headers: { "Content-Type": "application/json" },
582
+ method: "POST",
497
583
  });
498
- if (!res.ok) throw new Error((await res.json()).error);
584
+ if (!res.ok) {
585
+ const body = await res.json();
586
+ throw new Error(body.error);
587
+ }
499
588
  return await res.json();
500
589
  },
501
590
 
502
591
  /** @param {string} name */
503
592
  async gitCreateBranch(name: string) {
504
593
  const res = await fetch("/__studio/git/create-branch", {
505
- method: "POST",
506
- headers: { "Content-Type": "application/json" },
507
594
  body: JSON.stringify({ name }),
595
+ headers: { "Content-Type": "application/json" },
596
+ method: "POST",
508
597
  });
509
- if (!res.ok) throw new Error((await res.json()).error);
598
+ if (!res.ok) {
599
+ const body = await res.json();
600
+ throw new Error(body.error);
601
+ }
510
602
  return await res.json();
511
603
  },
512
604
 
513
- /** @param {string} path */
514
- async gitDiff(path: string) {
515
- const res = await fetch(`/__studio/git/diff?path=${encodeURIComponent(path)}`);
516
- if (!res.ok) throw new Error(await res.text());
605
+ /** @param {string} [path] */
606
+ async gitDiff(path?: string) {
607
+ const res = await fetch(`/__studio/git/diff?path=${encodeURIComponent(path ?? "")}`);
608
+ if (!res.ok) {
609
+ throw new Error(await res.text());
610
+ }
517
611
  return await res.json();
518
612
  },
519
613
 
520
614
  /** @param {{ path: string; ref?: string }} opts */
521
615
  async gitShow(opts: { path: string; ref?: string }) {
522
616
  const params = new URLSearchParams({ path: opts.path });
523
- if (opts.ref) params.set("ref", opts.ref);
617
+ if (opts.ref) {
618
+ params.set("ref", opts.ref);
619
+ }
524
620
  const res = await fetch(`/__studio/git/show?${params}`);
525
- if (!res.ok) throw new Error(await res.text());
621
+ if (!res.ok) {
622
+ throw new Error(await res.text());
623
+ }
526
624
  const data = await res.json();
527
625
  return data.content;
528
626
  },
@@ -530,28 +628,37 @@ export function createDevServerPlatform() {
530
628
  /** @param {string[]} files */
531
629
  async gitDiscard(files: string[]) {
532
630
  const res = await fetch("/__studio/git/discard", {
533
- method: "POST",
534
- headers: { "Content-Type": "application/json" },
535
631
  body: JSON.stringify({ files }),
632
+ headers: { "Content-Type": "application/json" },
633
+ method: "POST",
536
634
  });
537
- if (!res.ok) throw new Error((await res.json()).error);
635
+ if (!res.ok) {
636
+ const body = await res.json();
637
+ throw new Error(body.error);
638
+ }
538
639
  return await res.json();
539
640
  },
540
641
 
541
642
  /** @param {string} url */
542
643
  async gitClone(url: string) {
543
644
  const res = await fetch("/__studio/git/clone", {
544
- method: "POST",
545
- headers: { "Content-Type": "application/json" },
546
645
  body: JSON.stringify({ url }),
646
+ headers: { "Content-Type": "application/json" },
647
+ method: "POST",
547
648
  });
548
- if (!res.ok) throw new Error((await res.json()).error);
649
+ if (!res.ok) {
650
+ const body = await res.json();
651
+ throw new Error(body.error);
652
+ }
549
653
  return await res.json();
550
654
  },
551
655
 
552
656
  async gitInit() {
553
657
  const res = await fetch("/__studio/git/init", { method: "POST" });
554
- if (!res.ok) throw new Error((await res.json()).error);
658
+ if (!res.ok) {
659
+ const body = await res.json();
660
+ throw new Error(body.error);
661
+ }
555
662
  },
556
663
 
557
664
  /**
@@ -560,11 +667,14 @@ export function createDevServerPlatform() {
560
667
  */
561
668
  async gitAddRemote(name: string, url: string) {
562
669
  const res = await fetch("/__studio/git/add-remote", {
563
- method: "POST",
564
- headers: { "Content-Type": "application/json" },
565
670
  body: JSON.stringify({ name, url }),
671
+ headers: { "Content-Type": "application/json" },
672
+ method: "POST",
566
673
  });
567
- if (!res.ok) throw new Error((await res.json()).error);
674
+ if (!res.ok) {
675
+ const body = await res.json();
676
+ throw new Error(body.error);
677
+ }
568
678
  },
569
679
 
570
680
  // ─── AI Assistant ───────────────────────────────────
@@ -577,22 +687,28 @@ export function createDevServerPlatform() {
577
687
  /** @param {{ message: string; systemPrompt?: string }} opts */
578
688
  async aiCreateSession(opts: { message: string; systemPrompt?: string }) {
579
689
  const res = await fetch("/__studio/ai/session", {
580
- method: "POST",
581
- headers: { "Content-Type": "application/json" },
582
690
  body: JSON.stringify(opts),
691
+ headers: { "Content-Type": "application/json" },
692
+ method: "POST",
583
693
  });
584
- if (!res.ok) throw new Error((await res.json()).error);
694
+ if (!res.ok) {
695
+ const body = await res.json();
696
+ throw new Error(body.error);
697
+ }
585
698
  return await res.json();
586
699
  },
587
700
 
588
701
  /** @param {string} id @param {string} message */
589
702
  async aiSendMessage(id: string, message: string) {
590
703
  const res = await fetch(`/__studio/ai/session/${id}/message`, {
591
- method: "POST",
592
- headers: { "Content-Type": "application/json" },
593
704
  body: JSON.stringify({ message }),
705
+ headers: { "Content-Type": "application/json" },
706
+ method: "POST",
594
707
  });
595
- if (!res.ok) throw new Error((await res.json()).error);
708
+ if (!res.ok) {
709
+ const body = await res.json();
710
+ throw new Error(body.error);
711
+ }
596
712
  return await res.json();
597
713
  },
598
714