@jxsuite/studio 3.0.0 → 4.0.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 (173) hide show
  1. package/README.md +5 -5
  2. package/dist/chunks/Overlay.dev-723bv0df.js +23 -0
  3. package/dist/chunks/{cf-settings-zj2cj1ar.js → cf-settings-45fgyx8y.js} +6 -5
  4. package/dist/chunks/{components-z6cnkyy1.js → components-zmx10t62.js} +6 -5
  5. package/dist/chunks/content-source-t88vb2dr.js +49 -0
  6. package/dist/chunks/{convert-to-repeater-6vvx96n2.js → convert-to-repeater-dhw1nrz9.js} +27 -19
  7. package/dist/chunks/entry-commands-6z6bhxg0.js +46 -0
  8. package/dist/chunks/{extension-sections-kwzvpemr.js → extension-sections-3ydgjbdy.js} +32 -24
  9. package/dist/chunks/file-ops-rskmhxyq.js +48 -0
  10. package/dist/chunks/files-0cratsr0.js +92 -0
  11. package/dist/chunks/{format-host-8fr3545e.js → format-host-tgcmfs4p.js} +3 -3
  12. package/dist/chunks/fs-events-hrrg328g.js +21 -0
  13. package/dist/chunks/{git-panel-g01d93vc.js → git-panel-0yywyk76.js} +21 -16
  14. package/dist/chunks/{jx-validate-jj9rnt4n.js → jx-validate-8q2g05x1.js} +6 -4
  15. package/dist/chunks/layers-panel-z3tqe5cd.js +67 -0
  16. package/dist/chunks/layers-q75tj2mf.js +45 -0
  17. package/dist/chunks/lit-html-j055mkh8.js +22 -0
  18. package/dist/chunks/monaco-buffer-dqw0xxpx.js +25 -0
  19. package/dist/chunks/{monaco-setup-a5wkmbgh.js → monaco-setup-rws7k4br.js} +4 -4
  20. package/dist/chunks/notify-yjm79n4s.js +30 -0
  21. package/dist/chunks/parse-24d0gay3.js +19 -0
  22. package/dist/chunks/platform-ba1j1gbh.js +16 -0
  23. package/dist/chunks/publish-panel-54jvhskq.js +28 -0
  24. package/dist/chunks/quick-search-cgfmjwq5.js +65 -0
  25. package/dist/chunks/references-3qx8fkyp.js +29 -0
  26. package/dist/chunks/right-panel-cqp8bc4v.js +46 -0
  27. package/dist/chunks/sp-overlay.dev-yztmtrs1.js +18 -0
  28. package/dist/chunks/sp-popover.dev-n2etmqdp.js +9 -0
  29. package/dist/chunks/sp-progress-circle.dev-hryhj0es.js +15 -0
  30. package/dist/chunks/sp-tooltip.dev-xcvpnqr0.js +15 -0
  31. package/dist/chunks/{sp-tray.dev-64v4pxw8.js → sp-tray.dev-tkbscm06.js} +5 -5
  32. package/dist/chunks/{studio-8ezdegcx.js → studio-1hbqnt19.js} +36 -32
  33. package/dist/chunks/{studio-r5xbtctf.js → studio-2zkz8evn.js} +446 -2451
  34. package/dist/chunks/{studio-pkp61mh8.js → studio-4pqqxbrt.js} +23 -15
  35. package/dist/chunks/{studio-kvfx9z2c.js → studio-4rzyrtjz.js} +70 -31
  36. package/dist/chunks/{studio-3dkcd4c5.js → studio-5q69gfag.js} +60 -56
  37. package/dist/chunks/{studio-rh1cnamv.js → studio-5rywrkxz.js} +563 -133
  38. package/dist/chunks/{studio-6kv3apa9.js → studio-68n67094.js} +25 -9
  39. package/dist/chunks/{studio-0rqqhrdy.js → studio-7nte2hj4.js} +2 -2
  40. package/dist/chunks/{studio-xntfdy1x.js → studio-8swrjbny.js} +31 -18
  41. package/dist/chunks/studio-92ab2jqe.js +282 -0
  42. package/dist/chunks/{studio-sas7mt3k.js → studio-9ab8gqsm.js} +2 -2
  43. package/dist/chunks/{studio-ry1vz5sv.js → studio-9c4k4vhv.js} +15 -13
  44. package/dist/chunks/studio-9n4mrmnb.js +184 -0
  45. package/dist/chunks/{studio-tr20m8g8.js → studio-9r6j5ev1.js} +37 -2
  46. package/dist/chunks/{studio-2x9zbxmx.js → studio-ahjfdy6e.js} +9 -7
  47. package/dist/chunks/{studio-s519zc99.js → studio-bgxt93ga.js} +3 -3
  48. package/dist/chunks/{studio-dc8s8heb.js → studio-cb12yz9w.js} +8528 -8780
  49. package/dist/chunks/studio-cft9r4r7.js +119 -0
  50. package/dist/chunks/{studio-hcxzyk3x.js → studio-eenfkt4e.js} +6 -6
  51. package/dist/chunks/studio-eesa1myv.js +498 -0
  52. package/dist/chunks/{studio-ay8wcr2b.js → studio-eqq1w638.js} +7 -9
  53. package/dist/chunks/{studio-7p35254k.js → studio-ex8zyp98.js} +30 -26
  54. package/dist/chunks/studio-gv8vvshc.js +118 -0
  55. package/dist/chunks/studio-hbtp0eh1.js +1315 -0
  56. package/dist/chunks/{studio-a0yp7syn.js → studio-htsf0w6m.js} +29 -114
  57. package/dist/chunks/{studio-j595e748.js → studio-kjjsjak4.js} +139 -63
  58. package/dist/chunks/{studio-5x5774ak.js → studio-kryvsw6j.js} +3 -3
  59. package/dist/chunks/{studio-kfpek536.js → studio-kyh9avvv.js} +2 -2
  60. package/dist/chunks/{studio-g5gdqnyd.js → studio-kyk2v92a.js} +14 -14
  61. package/dist/chunks/{studio-twsafr1e.js → studio-m3hwn7y1.js} +3 -3
  62. package/dist/chunks/{studio-tzx3xmvx.js → studio-mmv72a27.js} +8 -8
  63. package/dist/chunks/{studio-vhk5fmfv.js → studio-n8b9wqwy.js} +5 -5
  64. package/dist/chunks/{studio-cjm7fdjf.js → studio-p4ngc046.js} +8 -104
  65. package/dist/chunks/{studio-p5dja96s.js → studio-p8j86wag.js} +2 -2
  66. package/dist/chunks/{studio-6z5eyjwn.js → studio-pdfjntbk.js} +66 -129
  67. package/dist/chunks/studio-q43a5y9h.js +394 -0
  68. package/dist/chunks/studio-q7v10cg2.js +106 -0
  69. package/dist/chunks/{studio-2w11grav.js → studio-qq6jym04.js} +3 -3
  70. package/dist/chunks/studio-r1r7afxx.js +1066 -0
  71. package/dist/chunks/{studio-3fk1ac9z.js → studio-rdx8jcxy.js} +4 -4
  72. package/dist/chunks/{studio-4mqb9a0j.js → studio-rn9ta2mz.js} +4 -4
  73. package/dist/chunks/studio-sf7zjgv1.js +91 -0
  74. package/dist/chunks/studio-t7qb9db8.js +665 -0
  75. package/dist/chunks/studio-tmc5emrw.js +8 -0
  76. package/dist/chunks/{studio-wdt9y92a.js → studio-txwkm6p9.js} +2 -2
  77. package/dist/chunks/{studio-k4jvjcfx.js → studio-v38hmvx1.js} +7 -7
  78. package/dist/chunks/{studio-7ejemhej.js → studio-vadzgkrz.js} +6 -6
  79. package/dist/chunks/studio-wcy8trfx.js +263 -0
  80. package/dist/chunks/{studio-5efrtn1m.js → studio-wx5609xd.js} +21 -17
  81. package/dist/chunks/{studio-qpfq7xhm.js → studio-xrapxwwc.js} +22 -285
  82. package/dist/chunks/{studio-hj2se583.js → studio-yxrkyv8a.js} +41 -31
  83. package/dist/chunks/{studio-xm3dbxvn.js → studio-zczps70q.js} +2 -2
  84. package/dist/chunks/workspace-xpres40v.js +89 -0
  85. package/dist/iframe-entry.js +108 -24
  86. package/dist/studio.js +2362 -1695
  87. package/package.json +16 -15
  88. package/src/browse/library-pane.ts +57 -3
  89. package/src/canvas/canvas-render.ts +23 -1
  90. package/src/canvas/iframe-patch.ts +47 -21
  91. package/src/canvas/iframe-render.ts +22 -6
  92. package/src/commands/app-commands.ts +4 -0
  93. package/src/commands/command-args.ts +29 -0
  94. package/src/commands/context.ts +15 -1
  95. package/src/commands/defaults.ts +29 -1
  96. package/src/commands/levels.ts +16 -0
  97. package/src/commands/live-context.ts +3 -0
  98. package/src/content/collection-match.ts +267 -0
  99. package/src/content/entry-commands.ts +38 -29
  100. package/src/content/entry-fields.ts +6 -0
  101. package/src/editor/shortcuts.ts +5 -0
  102. package/src/files/file-ops.ts +10 -42
  103. package/src/files/files.ts +485 -59
  104. package/src/files/fs-events.ts +9 -0
  105. package/src/files/gitignore.ts +432 -0
  106. package/src/files/serialize-document.ts +60 -0
  107. package/src/format/convert-file.ts +511 -0
  108. package/src/format/format-choices.ts +199 -0
  109. package/src/grid/grid-view.ts +16 -2
  110. package/src/media/media-open.ts +58 -0
  111. package/src/media/media-pane.ts +310 -0
  112. package/src/new-project/import-tab.ts +332 -160
  113. package/src/new-project/new-project-modal.ts +27 -34
  114. package/src/packages/ensure-deps.ts +14 -7
  115. package/src/packages/jxsuite-update.ts +11 -11
  116. package/src/page-params.ts +10 -46
  117. package/src/panels/activity-bar.ts +45 -16
  118. package/src/panels/ai-chat/attached-context.ts +6 -1
  119. package/src/panels/ai-chat/chat-view.ts +255 -13
  120. package/src/panels/ai-chat/composer.ts +21 -97
  121. package/src/panels/ai-panel.ts +116 -2
  122. package/src/panels/navigator-panels.ts +7 -6
  123. package/src/panels/settings-menu.ts +799 -0
  124. package/src/panels/toolbar.ts +135 -15
  125. package/src/platforms/cloud.ts +20 -0
  126. package/src/platforms/devserver.ts +67 -33
  127. package/src/preview/preview-overlay.ts +226 -0
  128. package/src/services/ai-ask.ts +247 -0
  129. package/src/services/ai-import-tools.ts +494 -0
  130. package/src/services/ai-project-tools.ts +20 -26
  131. package/src/services/ai-system-prompt.ts +88 -2
  132. package/src/services/ai-turn-signal.ts +70 -0
  133. package/src/services/connection.ts +74 -0
  134. package/src/services/context-manager.ts +89 -0
  135. package/src/services/document-assistant.ts +45 -4
  136. package/src/services/import-client.ts +33 -6
  137. package/src/services/import-run.ts +158 -0
  138. package/src/services/import-seed.ts +120 -0
  139. package/src/services/jx-validate.ts +58 -6
  140. package/src/services/project-adoption.ts +71 -0
  141. package/src/services/references.ts +0 -0
  142. package/src/services/settings/definitions.ts +17 -0
  143. package/src/services/tool-executor.ts +34 -2
  144. package/src/settings/contexts-section.ts +45 -3
  145. package/src/settings/dependencies-editor.ts +43 -23
  146. package/src/settings/general-settings.ts +30 -12
  147. package/src/settings/preferences-dialog.ts +18 -41
  148. package/src/settings/preferences-sections.ts +57 -0
  149. package/src/settings/settings-document.ts +65 -9
  150. package/src/studio.ts +33 -6
  151. package/src/tabs/transact.ts +42 -19
  152. package/src/types.ts +103 -5
  153. package/src/ui/ai-model-picker.ts +162 -0
  154. package/src/ui/choice-field.ts +72 -0
  155. package/src/ui/layers.ts +83 -5
  156. package/src/utils/studio-utils.ts +22 -52
  157. package/styles/forced-colors.css +7 -0
  158. package/styles/overlays.css +256 -0
  159. package/styles/panels.css +13 -1
  160. package/styles/shell.css +111 -0
  161. package/dist/chunks/Overlay.dev-d2ajy6sj.js +0 -23
  162. package/dist/chunks/content-source-7fy9qrm0.js +0 -40
  163. package/dist/chunks/files-xcybanxe.js +0 -70
  164. package/dist/chunks/layers-panel-27essgkc.js +0 -60
  165. package/dist/chunks/publish-panel-09sy2bab.js +0 -22
  166. package/dist/chunks/quick-search-sefb61v8.js +0 -55
  167. package/dist/chunks/right-panel-9r32jd5j.js +0 -39
  168. package/dist/chunks/sp-overlay.dev-s18hymn9.js +0 -18
  169. package/dist/chunks/sp-popover.dev-dj9x7e7a.js +0 -9
  170. package/dist/chunks/sp-progress-circle.dev-0zxcyz3r.js +0 -15
  171. package/dist/chunks/sp-tooltip.dev-5c6d1kjz.js +0 -15
  172. package/dist/chunks/studio-c31yb4t6.js +0 -55
  173. package/src/canvas/site-style-css.ts +0 -117
@@ -1,69 +1,46 @@
1
1
  import {
2
- ref,
3
- repeat
4
- } from "./studio-kvfx9z2c.js";
2
+ commitTabBuffers
3
+ } from "./studio-wcy8trfx.js";
5
4
  import {
6
- html,
7
- nothing,
8
- render
9
- } from "./studio-5x5774ak.js";
5
+ computed,
6
+ effect,
7
+ effectScope,
8
+ notify,
9
+ onScopeDispose,
10
+ reactive,
11
+ toRaw
12
+ } from "./studio-xrapxwwc.js";
13
+ import {
14
+ applyDocOpToDoc,
15
+ childArray,
16
+ cloneValue
17
+ } from "./studio-5ehhackm.js";
10
18
  import {
11
19
  childIndex,
12
- ensureNestedStyle,
13
- getNestedStyle,
14
20
  getNodeAtPath,
15
21
  isAncestor,
16
- isEventBinding,
17
22
  normalizeArrayChildren,
18
23
  parentElementPath,
19
24
  pathsEqual,
20
- projectState,
21
- requireProjectState,
22
- setProjectState
23
- } from "./studio-cjm7fdjf.js";
25
+ projectState
26
+ } from "./studio-p4ngc046.js";
24
27
  import {
25
- SETTINGS,
26
- computed,
27
- dismiss,
28
- effect,
29
- effectScope,
30
- notify,
31
- onScopeDispose,
32
- pinClock,
33
- reactive,
34
- readStoredSetting,
35
- setSetting,
36
- shallowRef,
37
- toRaw,
38
- toasts
39
- } from "./studio-qpfq7xhm.js";
28
+ ensureNestedStyle,
29
+ getNestedStyle,
30
+ isEventBinding
31
+ } from "./studio-q7v10cg2.js";
40
32
  import {
41
33
  defaultContentFormat,
42
34
  formatByName,
43
- formatForPath,
44
- loadedMonaco
45
- } from "./studio-hcxzyk3x.js";
35
+ formatForPath
36
+ } from "./studio-eenfkt4e.js";
46
37
  import {
47
38
  getPlatform
48
- } from "./studio-sas7mt3k.js";
49
- import {
50
- applyDocOpToDoc,
51
- childArray,
52
- cloneValue
53
- } from "./studio-5ehhackm.js";
39
+ } from "./studio-9ab8gqsm.js";
54
40
  import {
55
41
  __require
56
42
  } from "./studio-a0tswy0w.js";
57
43
 
58
- // ../schema/src/ijson.ts
59
- var WHITESPACE = new Set([" ", "\t", `
60
- `, "\r"]);
61
-
62
- // ../schema/src/parse.ts
63
- function errorMessage(e) {
64
- return e instanceof Error ? e.message : String(e);
65
- }
66
-
67
44
  // ../schema/src/locale.ts
68
45
  var RTL_SCRIPTS = new Set([
69
46
  "Adlm",
@@ -230,346 +207,115 @@ function localeSlot(segments) {
230
207
  return -1;
231
208
  }
232
209
 
233
- // src/view.ts
234
- var view = {
235
- functionEditor: null,
236
- blockActionBarEl: null,
237
- selDragCleanup: null,
238
- dndCleanups: [],
239
- elementsCollapsed: new Set,
240
- elementsFilter: "",
241
- _currentDropTargetRow: null,
242
- layerDragSourceHeight: 0,
243
- _completionRegistered: false,
244
- _layersCollapsed: null
245
- };
246
-
247
- // src/canvas/surface-registry.ts
248
- var STAGE_CLASS = "pane-stage";
249
- var _nextGen = 0;
250
- function nextRenderGeneration() {
251
- _nextGen += 1;
252
- return _nextGen;
253
- }
254
- var _surfaces = new Map;
255
- function surfaceForPane(paneId) {
256
- let surface = _surfaces.get(paneId);
257
- if (!surface) {
258
- surface = freshSurface(paneId);
259
- _surfaces.set(paneId, surface);
260
- }
261
- return surface;
262
- }
263
- function freshSurface(paneId) {
264
- return {
265
- centerObserver: null,
266
- monacoEditor: null,
267
- needsCenter: true,
268
- panX: 0,
269
- panY: 0,
270
- panels: [],
271
- paneId,
272
- panzoomWrap: null,
273
- prevCanvasMode: null,
274
- prevStylebookCustomizedOnly: null,
275
- prevStylebookFilter: null,
276
- renderGeneration: 0,
277
- sourceCollabCleanup: null,
278
- wrap: null
279
- };
210
+ // src/content/collection-match.ts
211
+ var LOCALE_PLACEHOLDER = "{locale}";
212
+ function escapeLiteral(part) {
213
+ return part.replaceAll(/[.*+?^${}()|[\]\\]/g, String.raw`\$&`);
280
214
  }
281
- function allCanvasSurfaces() {
282
- return [..._surfaces.values()];
215
+ function normalize(path) {
216
+ return path.replaceAll("\\", "/").replace(/^\.\//, "").replace(/\/+$/, "");
283
217
  }
284
- function stageContaining(node) {
285
- if (!node) {
286
- return null;
287
- }
288
- for (const surface of _surfaces.values()) {
289
- if (surface.wrap?.contains(node)) {
290
- return surface;
291
- }
292
- }
293
- return null;
218
+ function configOf(config) {
219
+ return config ?? projectState?.projectConfig ?? null;
294
220
  }
295
- function releaseMountedPanels(surface) {
296
- for (const panel of surface.panels) {
297
- panel.renderScope?.stop();
298
- panel.renderScope = null;
299
- }
300
- surface.panels.length = 0;
301
- surface.prevCanvasMode = null;
221
+ function contentSection(config) {
222
+ return config?.content ?? {};
302
223
  }
303
- function registerCanvasSurface(paneId, wrap) {
304
- const surface = surfaceForPane(paneId);
305
- surface.wrap = wrap;
306
- releaseMountedPanels(surface);
307
- return surface;
224
+ function declaredLocales(config) {
225
+ const locales = resolveI18n(config ?? {}).i18n?.locales ?? [];
226
+ return locales.length > 0 ? [...locales] : null;
308
227
  }
309
- function createPaneSurface(paneId) {
310
- const existing = _surfaces.get(paneId);
311
- if (existing) {
312
- disposePaneSurface(paneId);
228
+ function isFileBacked(source) {
229
+ if (source.endsWith("/")) {
230
+ return false;
313
231
  }
314
- const surface = freshSurface(paneId);
315
- _surfaces.set(paneId, surface);
316
- return surface;
232
+ const base = source.slice(source.lastIndexOf("/") + 1);
233
+ return /\.[a-z\d]+$/i.test(base);
317
234
  }
318
- var _teardown = null;
319
- function setSurfaceTeardown(fn) {
320
- _teardown = fn;
321
- }
322
- function disposePaneSurface(paneId) {
323
- const surface = _surfaces.get(paneId);
324
- if (!surface) {
325
- return;
235
+ function resolveExt(def) {
236
+ if (def.format === "json") {
237
+ return { ext: ".json", unresolvedFormat: null };
326
238
  }
327
- _teardown?.(surface);
328
- releaseMountedPanels(surface);
329
- unregisterCanvasSurface(paneId);
330
- }
331
- function unregisterCanvasSurface(paneId) {
332
- _surfaces.delete(paneId);
333
- }
334
-
335
- // src/services/monaco-buffer.ts
336
- var BUFFER_COMMIT = "commit";
337
- function bufferWrites(buffer) {
338
- const timers = new Map;
339
- const runs = new Map;
340
- const inFlight = new Map;
341
- let isAhead = false;
342
- let isTyped = false;
343
- let isShared = false;
344
- function track(key, result) {
345
- if (typeof result?.then !== "function") {
346
- return result;
347
- }
348
- const promise = result.finally(() => {
349
- if (inFlight.get(key) === promise) {
350
- inFlight.delete(key);
351
- }
352
- });
353
- inFlight.set(key, promise);
354
- return promise;
355
- }
356
- const writes = {
357
- arm(key, ms, run) {
358
- clearTimeout(timers.get(key));
359
- runs.set(key, run);
360
- timers.set(key, setTimeout(() => {
361
- timers.delete(key);
362
- runs.delete(key);
363
- track(key, run());
364
- }, ms));
365
- },
366
- cancel() {
367
- for (const timer of timers.values()) {
368
- clearTimeout(timer);
369
- }
370
- timers.clear();
371
- runs.clear();
372
- },
373
- flush(key) {
374
- const run = runs.get(key);
375
- if (!run) {
376
- return inFlight.get(key);
377
- }
378
- clearTimeout(timers.get(key));
379
- timers.delete(key);
380
- runs.delete(key);
381
- return track(key, run());
382
- },
383
- ahead: () => isAhead,
384
- markAhead() {
385
- isAhead = true;
386
- },
387
- typed: () => isTyped,
388
- markTyped() {
389
- isAhead = true;
390
- isTyped = true;
391
- },
392
- markSettled() {
393
- isAhead = false;
394
- isTyped = false;
395
- },
396
- shared: () => isShared,
397
- markShared() {
398
- isShared = true;
399
- }
400
- };
401
- buffer._writes = writes;
402
- return writes;
403
- }
404
- var SURFACE = {
405
- logic: { holds: "The handler you were typing", reached: "written into", source: "Logic" },
406
- source: { holds: "The source you were typing", reached: "parsed into", source: "Editor" }
407
- };
408
- function reportDiscarded(buffer, surface, failure) {
409
- const writes = buffer._writes;
410
- if (writes?.typed() !== true) {
411
- return true;
239
+ if (def.format === undefined || def.format === "") {
240
+ return { ext: defaultContentFormat()?.extensions[0] ?? ".json", unresolvedFormat: null };
412
241
  }
413
- const tab = buffer._editingTab ?? null;
414
- const where = tab?.documentPath ?? "Untitled";
415
- const { holds, reached, source } = SURFACE[surface];
416
- notify.warn(`${holds} was discarded — it was never ${reached} "${where}".`, {
417
- ...failure ? { detail: describeCommitFailure(failure.error) } : {},
418
- key: `buffer-discarded:${surface}:${tab?.id ?? "none"}`,
419
- source,
420
- ...tab?.documentPath ? { path: tab.documentPath } : {}
421
- });
422
- return false;
242
+ const format = formatByName(def.format);
243
+ return format?.extensions[0] === undefined ? { ext: null, unresolvedFormat: def.format } : { ext: format.extensions[0], unresolvedFormat: null };
423
244
  }
424
- function describeCommitFailure(error) {
425
- return error instanceof Error ? error.stack ?? error.message : String(error);
245
+ function sourcePattern(source, locales) {
246
+ const segment = locales === null ? "[^/]+" : `(?:${locales.map((locale) => escapeLiteral(locale)).join("|")})`;
247
+ const body = normalize(source).split(LOCALE_PLACEHOLDER).map((part) => escapeLiteral(part)).join(segment);
248
+ return new RegExp(`^${body}(?:/|$)`, "i");
426
249
  }
427
- function commitBufferWrites(buffer, surface) {
428
- const writes = buffer?._writes;
429
- if (!buffer || !writes) {
430
- return true;
431
- }
432
- let pending;
433
- try {
434
- pending = writes.flush(BUFFER_COMMIT);
435
- } catch (error) {
436
- writes.cancel();
437
- return reportDiscarded(buffer, surface, { error });
438
- }
439
- writes.cancel();
440
- if (typeof pending?.then === "function") {
441
- pending.then(() => reportDiscarded(buffer, surface, null), (error) => reportDiscarded(buffer, surface, { error }));
442
- return true;
250
+ function matchedRoot(source, target, locales) {
251
+ const hit = sourcePattern(source, locales).exec(target);
252
+ if (!hit) {
253
+ return null;
443
254
  }
444
- return reportDiscarded(buffer, surface, null);
445
- }
446
- function cancelBufferWrites(buffer) {
447
- buffer?._writes?.cancel();
255
+ return hit[0].replace(/\/$/, "");
448
256
  }
449
- function bufferIsLive(buffer) {
450
- return Boolean(buffer?.getModel());
257
+ function sourceWeight(source) {
258
+ return normalize(source).replaceAll(LOCALE_PLACEHOLDER, "").length;
451
259
  }
452
- function bufferMovedOn(buffer, expected) {
453
- if (!bufferIsLive(buffer) || buffer.hasTextFocus() || buffer._writes?.ahead() || buffer._writes?.shared()) {
454
- return true;
260
+ function collectionForDirectory(dir, config) {
261
+ if (dir === null || dir === undefined || dir === "") {
262
+ return null;
455
263
  }
456
- return expected !== undefined && buffer.getValue() !== expected;
457
- }
458
- function buffersForTab(tab) {
459
- const raw = toRaw(tab);
460
- const mounted = [
461
- ...allCanvasSurfaces().map((surface) => surface.monacoEditor),
462
- view.functionEditor
463
- ];
464
- return mounted.filter((buffer) => buffer != null && buffer._editingTab != null && toRaw(buffer._editingTab) === raw && bufferIsLive(buffer));
465
- }
466
- async function commitTabBuffers(tab) {
467
- for (const buffer of buffersForTab(tab)) {
468
- if (!bufferIsLive(buffer)) {
264
+ const resolved = configOf(config);
265
+ const locales = declaredLocales(resolved);
266
+ const target = normalize(dir);
267
+ let best = null;
268
+ let bestWeight = -1;
269
+ for (const [name, def] of Object.entries(contentSection(resolved))) {
270
+ const { source } = def;
271
+ if (source === undefined || source === "" || isFileBacked(source)) {
469
272
  continue;
470
273
  }
471
- try {
472
- await buffer._writes?.flush(BUFFER_COMMIT);
473
- } catch (error) {
474
- notify.error(`Could not save the editor's contents into "${tab.documentPath ?? "Untitled"}".`, {
475
- detail: error instanceof Error ? error.stack ?? error.message : String(error),
476
- key: `buffer-commit:${tab.id}`,
477
- source: "Editor",
478
- ...tab.documentPath ? { path: tab.documentPath } : {}
479
- });
480
- }
481
- }
482
- }
483
- function tabBufferUnsaved(tab) {
484
- return buffersForTab(tab).some((buffer) => buffer._writes?.typed() === true);
485
- }
486
-
487
- // src/tabs/selection.ts
488
- function primarySelection(selection) {
489
- return selection && selection.length > 0 ? selection.at(-1) : null;
490
- }
491
- function selectionAnchor(selection) {
492
- return selection && selection.length > 0 ? selection[0] : null;
493
- }
494
- function isSelected(selection, path) {
495
- if (!path || !selection) {
496
- return false;
497
- }
498
- return selection.some((p) => pathsEqual(p, path));
499
- }
500
- function cloneSelection(paths) {
501
- return clonePaths(paths);
502
- }
503
- function clonePaths(paths) {
504
- const out = [];
505
- for (const path of paths) {
506
- out.push([...path]);
507
- }
508
- return out;
509
- }
510
- function toggleSelected(selection, path) {
511
- if (isSelected(selection, path)) {
512
- return clonePaths(selection.filter((p) => !pathsEqual(p, path)));
513
- }
514
- return [...clonePaths(selection), [...path]];
515
- }
516
- function rangeSelection(rows, anchor, target) {
517
- const to = rows.findIndex((p) => pathsEqual(p, target));
518
- const from = anchor ? rows.findIndex((p) => pathsEqual(p, anchor)) : -1;
519
- if (to === -1 || from === -1) {
520
- return [[...target]];
521
- }
522
- const slice = from <= to ? rows.slice(from, to + 1) : rows.slice(to, from + 1).toReversed();
523
- return clonePaths(slice);
524
- }
525
- function pruneSelection(selection, removed) {
526
- return clonePaths(selection.filter((p) => !isAncestor(removed, p)));
527
- }
528
- function comparePaths(a, b) {
529
- const len = Math.min(a.length, b.length);
530
- for (let i = 0;i < len; i++) {
531
- const x = a[i];
532
- const y = b[i];
533
- if (x === y) {
274
+ const weight = sourceWeight(source);
275
+ if (weight <= bestWeight) {
534
276
  continue;
535
277
  }
536
- if (typeof x === "number" && typeof y === "number") {
537
- return x - y;
538
- }
539
- return String(x) < String(y) ? -1 : 1;
540
- }
541
- return a.length - b.length;
542
- }
543
- function topLevelSelection(selection) {
544
- return clonePaths(selection.filter((p) => !selection.some((q) => !pathsEqual(q, p) && isAncestor(q, p))));
545
- }
546
- function isSpliceablePath(path) {
547
- return path.at(-2) === "children" && typeof path.at(-1) === "number";
548
- }
549
- function structuralBatch(selection) {
550
- return topLevelSelection(selection).filter((path) => isSpliceablePath(path)).toSorted((a, b) => comparePaths(b, a));
551
- }
552
- function uniquePaths(paths) {
553
- const out = [];
554
- for (const path of paths) {
555
- if (!out.some((p) => pathsEqual(p, path))) {
556
- out.push([...path]);
278
+ const root = matchedRoot(source, target, locales);
279
+ if (root === null) {
280
+ continue;
557
281
  }
282
+ best = {
283
+ def,
284
+ dir: target,
285
+ fileBacked: false,
286
+ isSourceRoot: target.length === root.length,
287
+ name,
288
+ ...resolveExt(def)
289
+ };
290
+ bestWeight = weight;
558
291
  }
559
- return out;
292
+ return best;
560
293
  }
561
- function unifyValues(values) {
562
- if (values.length === 0) {
563
- return { mixed: false, value: undefined };
294
+ function collectionForFile(path, config) {
295
+ if (path === null || path === undefined || path === "") {
296
+ return null;
564
297
  }
565
- const first = values[0];
566
- const key = JSON.stringify(first ?? null);
567
- for (let i = 1;i < values.length; i++) {
568
- if (JSON.stringify(values[i] ?? null) !== key) {
569
- return { mixed: true, value: undefined };
298
+ const resolved = configOf(config);
299
+ const target = normalize(path);
300
+ const lower = target.toLowerCase();
301
+ for (const [name, def] of Object.entries(contentSection(resolved))) {
302
+ const { source } = def;
303
+ if (source === undefined || source === "" || !isFileBacked(source)) {
304
+ continue;
305
+ }
306
+ const src = normalize(source).toLowerCase();
307
+ if (lower === src || lower.endsWith(`/${src}`)) {
308
+ return {
309
+ def,
310
+ dir: target.includes("/") ? target.slice(0, target.lastIndexOf("/")) : ".",
311
+ fileBacked: true,
312
+ isSourceRoot: false,
313
+ name,
314
+ ...resolveExt(def)
315
+ };
570
316
  }
571
317
  }
572
- return { mixed: false, value: first };
318
+ return collectionForDirectory(target.includes("/") ? target.slice(0, target.lastIndexOf("/")) : ".", resolved);
573
319
  }
574
320
 
575
321
  // src/utils/studio-utils.ts
@@ -621,7 +367,6 @@ function abbreviateValue(val) {
621
367
  };
622
368
  return map[val] || val;
623
369
  }
624
- var LOCALE_PLACEHOLDER = "{locale}";
625
370
  function isMediaFormat(format) {
626
371
  return format === "image" || format === "uri-reference";
627
372
  }
@@ -659,27 +404,17 @@ function findContentTypeSchema(documentPath, projectConfig) {
659
404
  if (!documentPath || !projectConfig?.content) {
660
405
  return null;
661
406
  }
662
- const docPath = documentPath.replaceAll("\\", "/");
663
- for (const [name, def] of Object.entries(projectConfig.content)) {
664
- if (!def.source || !def.schema) {
665
- continue;
666
- }
667
- const src = def.source.replace(/^\.\//, "").replace(/\/$/, "");
668
- const hasExt = src.includes(".") && !src.endsWith("/") && !src.includes(LOCALE_PLACEHOLDER);
669
- if (hasExt) {
670
- if (docPath === src || docPath.endsWith(`/${src}`)) {
671
- return { name, schema: def.schema };
672
- }
673
- } else {
674
- const ext = def.format === "json" ? ".json" : formatByName(def.format)?.extensions[0] ?? defaultContentFormat()?.extensions[0] ?? ".json";
675
- const prefix = src.includes(LOCALE_PLACEHOLDER) ? new RegExp(`^${src.split(LOCALE_PLACEHOLDER).map((part) => part.replaceAll(/[.*+?^${}()|[\]\\]/g, String.raw`\$&`)).join("[^/]+")}/`) : null;
676
- const matches = prefix ? prefix.test(docPath) : docPath.startsWith(`${src}/`);
677
- if (matches && docPath.endsWith(ext)) {
678
- return { name, schema: def.schema };
679
- }
407
+ const match = collectionForFile(documentPath, projectConfig);
408
+ if (!match || !match.def.schema) {
409
+ return null;
410
+ }
411
+ if (!match.fileBacked) {
412
+ const ext = match.ext ?? defaultContentFormat()?.extensions[0] ?? ".json";
413
+ if (!documentPath.toLowerCase().endsWith(ext.toLowerCase())) {
414
+ return null;
680
415
  }
681
416
  }
682
- return null;
417
+ return { name: match.name, schema: match.def.schema };
683
418
  }
684
419
  function friendlyNameToVar(name, prefix) {
685
420
  const slug = name.trim().toLowerCase().replaceAll(/[^a-z0-9\s-]/g, "").replaceAll(/\s+/g, "-").replaceAll(/-+/g, "-").replaceAll(/^-|-$/g, "");
@@ -712,67 +447,155 @@ function parseCemType(typeText) {
712
447
  function jsonClone(value) {
713
448
  return JSON.parse(JSON.stringify(value));
714
449
  }
715
- // src/tabs/patch-ops.ts
716
- var _consumer = null;
717
- function setPatchConsumer(consumer) {
718
- _consumer = consumer;
719
- }
720
- function getPatchConsumer() {
721
- return _consumer;
450
+
451
+ // src/tabs/selection.ts
452
+ function primarySelection(selection) {
453
+ return selection && selection.length > 0 ? selection.at(-1) : null;
722
454
  }
723
- var _recording = null;
724
- var _docOps = null;
725
- var _fmOps = null;
726
- var _invertible = true;
727
- function beginRecording() {
728
- _recording = [];
729
- _docOps = [];
730
- _fmOps = [];
731
- _invertible = true;
455
+ function selectionAnchor(selection) {
456
+ return selection && selection.length > 0 ? selection[0] : null;
732
457
  }
733
- function recordPatch(op) {
734
- if (_recording) {
735
- _recording.push(op);
458
+ function isSelected(selection, path) {
459
+ if (!path || !selection) {
460
+ return false;
736
461
  }
462
+ return selection.some((p) => pathsEqual(p, path));
737
463
  }
738
- function recordDocOp(pair) {
739
- if (_docOps) {
740
- _docOps.push(pair);
741
- }
464
+ function cloneSelection(paths) {
465
+ return clonePaths(paths);
742
466
  }
743
- function recordFmOp(op) {
744
- if (_fmOps) {
745
- _fmOps.push(op);
467
+ function clonePaths(paths) {
468
+ const out = [];
469
+ for (const path of paths) {
470
+ out.push([...path]);
746
471
  }
472
+ return out;
747
473
  }
748
- function endRecording() {
749
- const record = {
750
- docOps: _docOps ?? [],
751
- fmOps: _fmOps ?? [],
752
- invertible: _invertible,
753
- ops: _recording ?? []
754
- };
755
- _recording = null;
756
- _docOps = null;
757
- _fmOps = null;
758
- _invertible = true;
759
- return record;
474
+ function toggleSelected(selection, path) {
475
+ if (isSelected(selection, path)) {
476
+ return clonePaths(selection.filter((p) => !pathsEqual(p, path)));
477
+ }
478
+ return [...clonePaths(selection), [...path]];
760
479
  }
761
-
762
- // src/tabs/transact.ts
763
- var HISTORY_LIMIT = 100;
764
- var CHECKPOINT_INTERVAL = 20;
765
- function patchHistoryEnabled() {
766
- try {
767
- return typeof localStorage === "undefined" || !localStorage.getItem("jx-legacy-history");
768
- } catch {
769
- return true;
480
+ function rangeSelection(rows, anchor, target) {
481
+ const to = rows.findIndex((p) => pathsEqual(p, target));
482
+ const from = anchor ? rows.findIndex((p) => pathsEqual(p, anchor)) : -1;
483
+ if (to === -1 || from === -1) {
484
+ return [[...target]];
770
485
  }
486
+ const slice = from <= to ? rows.slice(from, to + 1) : rows.slice(to, from + 1).toReversed();
487
+ return clonePaths(slice);
771
488
  }
772
- function setKeyPair(path, key, before, after) {
773
- return {
774
- forward: { key, op: "set-key", path, value: after },
775
- inverse: { key, op: "set-key", path, value: before }
489
+ function pruneSelection(selection, removed) {
490
+ return clonePaths(selection.filter((p) => !isAncestor(removed, p)));
491
+ }
492
+ function comparePaths(a, b) {
493
+ const len = Math.min(a.length, b.length);
494
+ for (let i = 0;i < len; i++) {
495
+ const x = a[i];
496
+ const y = b[i];
497
+ if (x === y) {
498
+ continue;
499
+ }
500
+ if (typeof x === "number" && typeof y === "number") {
501
+ return x - y;
502
+ }
503
+ return String(x) < String(y) ? -1 : 1;
504
+ }
505
+ return a.length - b.length;
506
+ }
507
+ function topLevelSelection(selection) {
508
+ return clonePaths(selection.filter((p) => !selection.some((q) => !pathsEqual(q, p) && isAncestor(q, p))));
509
+ }
510
+ function isSpliceablePath(path) {
511
+ return path.at(-2) === "children" && typeof path.at(-1) === "number";
512
+ }
513
+ function structuralBatch(selection) {
514
+ return topLevelSelection(selection).filter((path) => isSpliceablePath(path)).toSorted((a, b) => comparePaths(b, a));
515
+ }
516
+ function uniquePaths(paths) {
517
+ const out = [];
518
+ for (const path of paths) {
519
+ if (!out.some((p) => pathsEqual(p, path))) {
520
+ out.push([...path]);
521
+ }
522
+ }
523
+ return out;
524
+ }
525
+ function unifyValues(values) {
526
+ if (values.length === 0) {
527
+ return { mixed: false, value: undefined };
528
+ }
529
+ const first = values[0];
530
+ const key = JSON.stringify(first ?? null);
531
+ for (let i = 1;i < values.length; i++) {
532
+ if (JSON.stringify(values[i] ?? null) !== key) {
533
+ return { mixed: true, value: undefined };
534
+ }
535
+ }
536
+ return { mixed: false, value: first };
537
+ }
538
+ // src/tabs/patch-ops.ts
539
+ var _consumer = null;
540
+ function setPatchConsumer(consumer) {
541
+ _consumer = consumer;
542
+ }
543
+ function getPatchConsumer() {
544
+ return _consumer;
545
+ }
546
+ var _recording = null;
547
+ var _docOps = null;
548
+ var _fmOps = null;
549
+ var _invertible = true;
550
+ function beginRecording() {
551
+ _recording = [];
552
+ _docOps = [];
553
+ _fmOps = [];
554
+ _invertible = true;
555
+ }
556
+ function recordPatch(op) {
557
+ if (_recording) {
558
+ _recording.push(op);
559
+ }
560
+ }
561
+ function recordDocOp(pair) {
562
+ if (_docOps) {
563
+ _docOps.push(pair);
564
+ }
565
+ }
566
+ function recordFmOp(op) {
567
+ if (_fmOps) {
568
+ _fmOps.push(op);
569
+ }
570
+ }
571
+ function endRecording() {
572
+ const record = {
573
+ docOps: _docOps ?? [],
574
+ fmOps: _fmOps ?? [],
575
+ invertible: _invertible,
576
+ ops: _recording ?? []
577
+ };
578
+ _recording = null;
579
+ _docOps = null;
580
+ _fmOps = null;
581
+ _invertible = true;
582
+ return record;
583
+ }
584
+
585
+ // src/tabs/transact.ts
586
+ var HISTORY_LIMIT = 100;
587
+ var CHECKPOINT_INTERVAL = 20;
588
+ function patchHistoryEnabled() {
589
+ try {
590
+ return typeof localStorage === "undefined" || !localStorage.getItem("jx-legacy-history");
591
+ } catch {
592
+ return true;
593
+ }
594
+ }
595
+ function setKeyPair(path, key, before, after) {
596
+ return {
597
+ forward: { key, op: "set-key", path, value: after },
598
+ inverse: { key, op: "set-key", path, value: before }
776
599
  };
777
600
  }
778
601
  function shiftPrefixedIndex(parentPath, path, spliceIndex, delta, inclusive) {
@@ -1380,6 +1203,24 @@ function mutateUpdateMediaNestedStyle(tab, path, mediaName, selector, prop, valu
1380
1203
  recordDocOp(setKeyPair(path, "style", styleBefore, cloneValue(node.style)));
1381
1204
  recordPatch({ op: "set-style", path });
1382
1205
  }
1206
+ function pruneEmptyStylePath(root, stylePath) {
1207
+ const owners = [root];
1208
+ for (const seg of stylePath) {
1209
+ const next = getNestedStyle(owners.at(-1), seg);
1210
+ if (!next) {
1211
+ break;
1212
+ }
1213
+ owners.push(next);
1214
+ }
1215
+ for (let i = owners.length - 2;i >= 0; i--) {
1216
+ const owner = owners[i];
1217
+ const seg = stylePath[i];
1218
+ const block = getNestedStyle(owner, seg);
1219
+ if (block && Object.keys(block).length === 0) {
1220
+ delete owner[seg];
1221
+ }
1222
+ }
1223
+ }
1383
1224
  function mutateUpdateNestedStylePath(tab, path, stylePath, prop, value) {
1384
1225
  const node = getNodeAtPath(tab.doc.document, path);
1385
1226
  const styleBefore = cloneValue(node.style);
@@ -1392,15 +1233,7 @@ function mutateUpdateNestedStylePath(tab, path, stylePath, prop, value) {
1392
1233
  }
1393
1234
  if (value === undefined || value === "") {
1394
1235
  delete obj[prop];
1395
- let cur = node.style;
1396
- for (let i = 0;i < stylePath.length && cur; i++) {
1397
- const child = getNestedStyle(cur, stylePath[i]);
1398
- if (child && Object.keys(child).length === 0) {
1399
- delete cur[stylePath[i]];
1400
- break;
1401
- }
1402
- cur = child;
1403
- }
1236
+ pruneEmptyStylePath(node.style, stylePath);
1404
1237
  } else {
1405
1238
  obj[prop] = value;
1406
1239
  }
@@ -1424,15 +1257,7 @@ function mutateUpdateMediaNestedStylePath(tab, path, mediaName, stylePath, prop,
1424
1257
  }
1425
1258
  if (value === undefined || value === "") {
1426
1259
  delete obj[prop];
1427
- let cur = media;
1428
- for (let i = 0;i < stylePath.length && cur; i++) {
1429
- const child = getNestedStyle(cur, stylePath[i]);
1430
- if (child && Object.keys(child).length === 0) {
1431
- delete cur[stylePath[i]];
1432
- break;
1433
- }
1434
- cur = child;
1435
- }
1260
+ pruneEmptyStylePath(media, stylePath);
1436
1261
  if (Object.keys(media).length === 0) {
1437
1262
  delete node.style[key];
1438
1263
  }
@@ -1575,743 +1400,116 @@ function applyFmState(tab, field, value) {
1575
1400
  }
1576
1401
  }
1577
1402
 
1578
- // src/ui/regions.ts
1579
- var REGION_ATTR = "data-jx-region";
1580
- var REGION_SURFACES = [
1581
- "rail",
1582
- "navigator",
1583
- "inspector",
1584
- "pane",
1585
- "dock.bottom",
1586
- "statusbar",
1587
- "commandbar",
1588
- "overlay"
1589
- ];
1590
- var DOTTED_SURFACES = new Set(["dock.bottom"]);
1591
- function parseRegionId(id) {
1592
- const slash = id.indexOf("/");
1593
- const head = slash === -1 ? id : id.slice(0, slash);
1594
- const part = slash === -1 ? undefined : id.slice(slash + 1);
1595
- if (part === "" || head === "") {
1596
- return null;
1597
- }
1598
- let surface = head;
1599
- let instance;
1600
- if (!DOTTED_SURFACES.has(head)) {
1601
- const dot = head.indexOf(".");
1602
- if (dot !== -1) {
1603
- surface = head.slice(0, dot);
1604
- instance = head.slice(dot + 1);
1605
- if (instance === "") {
1606
- return null;
1607
- }
1608
- }
1609
- }
1610
- if (!REGION_SURFACES.includes(surface)) {
1611
- return null;
1612
- }
1613
- return instance === undefined ? part === undefined ? { surface } : { part, surface } : part === undefined ? { instance, surface } : { instance, part, surface };
1403
+ // src/commands/context.ts
1404
+ var EDITOR_KIND_BY_MODE = {
1405
+ design: "canvas",
1406
+ edit: "canvas",
1407
+ entry: "entry",
1408
+ "git-diff": "diff",
1409
+ grid: "grid",
1410
+ manage: "library",
1411
+ media: "media",
1412
+ preview: "canvas",
1413
+ settings: "config",
1414
+ source: "code",
1415
+ stylebook: "config"
1416
+ };
1417
+ var EDITOR_KIND_LABELS = {
1418
+ canvas: "Canvas",
1419
+ code: "Code",
1420
+ config: "Project Styles",
1421
+ diff: "Diff",
1422
+ entry: "Entry",
1423
+ grid: "Grid",
1424
+ media: "Media",
1425
+ library: "Library",
1426
+ none: "None"
1427
+ };
1428
+ function editorKindForMode(mode) {
1429
+ return EDITOR_KIND_BY_MODE[mode] ?? "canvas";
1614
1430
  }
1615
- function isRegionId(id) {
1616
- return parseRegionId(id) !== null;
1431
+ var CANVAS_VIEW_BY_MODE = {
1432
+ design: "design",
1433
+ edit: "edit",
1434
+ preview: "preview"
1435
+ };
1436
+ function canvasViewForMode(mode) {
1437
+ return CANVAS_VIEW_BY_MODE[mode] ?? "design";
1617
1438
  }
1618
- function canonicalRegionId(id) {
1619
- if (id === "pane") {
1620
- return "pane.primary";
1621
- }
1622
- return id.startsWith("pane/") ? `pane.primary/${id.slice(5)}` : id;
1439
+ var inCanvas = (ctx) => ctx.editor.kind === "canvas";
1440
+ var hasSelection = (ctx) => inCanvas(ctx) && ctx.selection.count > 0;
1441
+ var hasElementSelection = (ctx) => hasSelection(ctx) && !ctx.selection.isRoot;
1442
+ function emptyContext() {
1443
+ return {
1444
+ project: { open: false, isSite: false, isRepo: false, isMultilingual: false },
1445
+ git: { ahead: 0, behind: 0, dirtyCount: 0 },
1446
+ document: { open: false, dirty: false, mode: "", canUndo: false, canRedo: false },
1447
+ editor: { kind: "none" },
1448
+ canvas: { view: "design" },
1449
+ pane: { count: 1, derived: false },
1450
+ selection: {
1451
+ count: 0,
1452
+ paths: [],
1453
+ kind: "",
1454
+ isRoot: false,
1455
+ isComponentInstance: false,
1456
+ isLayoutNode: false,
1457
+ isRepeater: false
1458
+ },
1459
+ caret: { active: false, inCanvas: false },
1460
+ focus: { region: "pane" },
1461
+ modal: { open: false },
1462
+ collab: { attached: false, readOnly: false, sourceCanonical: false },
1463
+ ai: { configured: false, streaming: false, waiting: false },
1464
+ capability: {
1465
+ gitClone: false,
1466
+ importSite: false,
1467
+ openProjectInNewWindow: false,
1468
+ dataRows: false,
1469
+ windowControls: false,
1470
+ findReferences: false
1471
+ }
1472
+ };
1623
1473
  }
1624
- function regionRoot(root) {
1625
- return root ?? document;
1474
+ function makeContext(patch = {}) {
1475
+ const base = emptyContext();
1476
+ for (const key of Object.keys(patch)) {
1477
+ const group = patch[key];
1478
+ if (group) {
1479
+ Object.assign(base[key], group);
1480
+ }
1481
+ }
1482
+ return base;
1626
1483
  }
1627
- var DERIVED_RESOLVERS = [
1628
- {
1629
- locate: ([, prop], root) => {
1630
- const row = resolveRegion(`inspector/field:${prop}`, root);
1631
- const browse = row?.querySelector(".media-picker-browse");
1632
- return browse ? [browse] : [];
1633
- },
1634
- pattern: /^inspector\/field:(.+)\/browse$/
1635
- },
1636
- {
1637
- locate: ([, prop], root) => {
1638
- const inspector = resolveRegion("inspector", root);
1639
- return inspector ? [...inspector.querySelectorAll(`[data-prop="${CSS.escape(prop)}"]`)] : [];
1640
- },
1641
- pattern: /^inspector\/field:(.+)$/
1484
+ var MODAL_STACK = ["palette"];
1485
+ var CARET_STACK = ["caret", "global"];
1486
+ var DOCK_STACK = ["dock", "global"];
1487
+ var CANVAS_STACK = ["canvas", "global"];
1488
+ var GRID_STACK = ["grid", "global"];
1489
+ var CODE_STACK = ["code", "global"];
1490
+ var GLOBAL_STACK = ["global"];
1491
+ function keyScopeStack(ctx) {
1492
+ if (ctx.modal.open) {
1493
+ return MODAL_STACK;
1642
1494
  }
1643
- ];
1644
- function resolveAllRegions(id, root) {
1645
- const canonical = canonicalRegionId(id);
1646
- if (!isRegionId(canonical)) {
1647
- return [];
1495
+ if (ctx.caret.active) {
1496
+ return CARET_STACK;
1648
1497
  }
1649
- const stamped = [
1650
- ...regionRoot(root).querySelectorAll(`[${REGION_ATTR}="${CSS.escape(canonical)}"]`)
1651
- ];
1652
- if (stamped.length > 0) {
1653
- return stamped;
1498
+ if (ctx.focus.region !== "pane") {
1499
+ return DOCK_STACK;
1654
1500
  }
1655
- for (const resolver of DERIVED_RESOLVERS) {
1656
- const match = canonical.match(resolver.pattern);
1657
- if (match) {
1658
- return resolver.locate(match, regionRoot(root));
1501
+ switch (ctx.editor.kind) {
1502
+ case "grid": {
1503
+ return GRID_STACK;
1659
1504
  }
1660
- }
1661
- return [];
1662
- }
1663
- function resolveRegion(id, root) {
1664
- const all = resolveAllRegions(id, root);
1665
- return all.at(-1) ?? null;
1666
- }
1667
- var REGION_FOR_FOCUS = {
1668
- dock: "dock.bottom",
1669
- inspector: "inspector",
1670
- navigator: "navigator",
1671
- pane: "pane.primary",
1672
- rail: "rail",
1673
- status: "statusbar"
1674
- };
1675
- function paneRegion(paneId, part) {
1676
- return part ? `pane.${paneId}/${part}` : `pane.${paneId}`;
1677
- }
1678
- function paneStripRegion(paneId) {
1679
- return paneRegion(paneId, "tabs");
1680
- }
1681
- function navigatorPanelRegion(panelId) {
1682
- return `navigator/panel:${panelId}`;
1683
- }
1684
- function inspectorTabRegion(tabId) {
1685
- return `inspector/tab:${tabId}`;
1686
- }
1687
- var NAVIGATOR_STATEMENTS_REGION = "navigator/statements";
1688
- function inspectorStatementsRegion(eventKey) {
1689
- return `inspector/statements:${eventKey}`;
1690
- }
1691
- function bottomPanelRegion(panelId) {
1692
- return `dock.bottom/panel:${panelId}`;
1693
- }
1694
- var OVERLAY_INSTANCE = {
1695
- dialog: "dialog",
1696
- modal: "dialog",
1697
- popover: "menu",
1698
- toast: "toasts"
1699
- };
1700
- function overlayRegion(layer, id) {
1701
- const instance = OVERLAY_INSTANCE[layer];
1702
- return id ? `overlay.${instance}:${id}` : `overlay.${instance}`;
1703
- }
1704
-
1705
- // src/commands/active-registry.ts
1706
- var _registry = shallowRef(null);
1707
- function setActiveRegistry(registry) {
1708
- _registry.value = registry;
1709
- }
1710
- function activeRegistry() {
1711
- return _registry.value;
1712
- }
1713
-
1714
- // src/ui/layers.ts
1715
- var _popoverLayer;
1716
- var _modalLayer;
1717
- var _dialogLayer;
1718
- var _toastLayer;
1719
- function layerHost(kind) {
1720
- const host = kind === "popover" ? _popoverLayer : kind === "modal" ? _modalLayer : kind === "toast" ? _toastLayer : _dialogLayer;
1721
- return host || document.body;
1722
- }
1723
- function initLayers() {
1724
- _popoverLayer = document.querySelector("#layer-popover");
1725
- _modalLayer = document.querySelector("#layer-modal");
1726
- _dialogLayer = document.querySelector("#layer-dialog");
1727
- _toastLayer = document.querySelector("#layer-toast");
1728
- mountToastHost();
1729
- }
1730
- var UNDERLAID = "sp-dialog-wrapper[open], sp-underlay[open]";
1731
- function isModalOpen() {
1732
- return Boolean(_dialogLayer?.querySelector(UNDERLAID) || _modalLayer?.querySelector(UNDERLAID));
1733
- }
1734
- var BODY_FOCUSABLE = 'a[href], input, textarea, select, button, sp-textfield, sp-button, sp-action-button, sp-picker, sp-checkbox, sp-menu-item, [tabindex]:not([tabindex="-1"])';
1735
- function focusablesIn(slot) {
1736
- return [...slot.querySelectorAll(BODY_FOCUSABLE)].filter((el) => !el.hasAttribute("disabled") && el.getAttribute("aria-hidden") !== "true");
1737
- }
1738
- function focusOverlay(slot) {
1739
- requestAnimationFrame(() => {
1740
- if (!slot.isConnected || slot.contains(document.activeElement)) {
1741
- return;
1505
+ case "code": {
1506
+ return CODE_STACK;
1742
1507
  }
1743
- const wrapper = slot.querySelector("sp-dialog-wrapper");
1744
- const target = focusablesIn(slot)[0] ?? wrapper?.shadowRoot?.querySelector("sp-button") ?? slot;
1745
- target.focus();
1746
- });
1747
- }
1748
- function trapTab(slot, e) {
1749
- e.preventDefault();
1750
- const items = focusablesIn(slot);
1751
- if (items.length === 0) {
1752
- return;
1753
- }
1754
- const at = items.indexOf(document.activeElement);
1755
- const next = e.shiftKey ? items[at <= 0 ? items.length - 1 : at - 1] : items[at === -1 || at === items.length - 1 ? 0 : at + 1];
1756
- next?.focus();
1757
- }
1758
- function openOverlaySlot(opts) {
1759
- const slot = document.createElement("div");
1760
- slot.style.pointerEvents = "auto";
1761
- slot.setAttribute(REGION_ATTR, overlayRegion(opts.kind, opts.regionId));
1762
- slot.tabIndex = -1;
1763
- slot.style.outline = "none";
1764
- opts.layer.append(slot);
1765
- const restoreTo = document.activeElement instanceof HTMLElement ? document.activeElement : null;
1766
- const onKeydown = (e) => {
1767
- if (e.key === "Escape") {
1768
- opts.onEscape?.(e, slot);
1769
- return;
1508
+ case "canvas": {
1509
+ return ctx.canvas.view === "preview" ? GLOBAL_STACK : CANVAS_STACK;
1770
1510
  }
1771
- if (e.key === "Tab" && opts.trapFocus) {
1772
- trapTab(slot, e);
1773
- }
1774
- };
1775
- slot.addEventListener("keydown", onKeydown);
1776
- return {
1777
- release() {
1778
- slot.removeEventListener("keydown", onKeydown);
1779
- render(nothing, slot);
1780
- slot.remove();
1781
- if (restoreTo?.isConnected) {
1782
- restoreTo.focus();
1783
- }
1784
- },
1785
- slot
1786
- };
1787
- }
1788
- function showDialog(templateFn, opts = {}) {
1789
- return new Promise((resolve) => {
1790
- const { release, slot } = openOverlaySlot({
1791
- kind: "dialog",
1792
- layer: layerHost("dialog"),
1793
- regionId: opts.region,
1794
- onEscape(e, host) {
1795
- const wrapper = host.querySelector("sp-dialog-wrapper");
1796
- if (!wrapper) {
1797
- return;
1798
- }
1799
- e.preventDefault();
1800
- e.stopPropagation();
1801
- wrapper.dispatchEvent(new Event("close", { bubbles: true }));
1802
- }
1803
- });
1804
- slot.setAttribute("role", "dialog");
1805
- slot.setAttribute("aria-modal", "true");
1806
- let resolved = false;
1807
- const done = (value) => {
1808
- if (resolved) {
1809
- return;
1810
- }
1811
- resolved = true;
1812
- release();
1813
- resolve(value);
1814
- };
1815
- render(templateFn(done), slot);
1816
- const headline = opts.label ?? slot.querySelector("sp-dialog-wrapper")?.getAttribute("headline") ?? null;
1817
- if (headline !== null && headline !== "") {
1818
- slot.setAttribute("aria-label", headline);
1819
- }
1820
- focusOverlay(slot);
1821
- });
1822
- }
1823
- function showConfirmDialog(headline, message, opts = {}) {
1824
- const { confirmLabel = "Confirm", cancelLabel = "Cancel", destructive = false } = opts;
1825
- return showDialog((done) => html`
1826
- <sp-dialog-wrapper
1827
- open
1828
- underlay
1829
- headline=${headline}
1830
- confirm-label=${confirmLabel}
1831
- cancel-label=${cancelLabel}
1832
- size="s"
1833
- @confirm=${() => done(true)}
1834
- @cancel=${() => done(false)}
1835
- @close=${() => done(false)}
1836
- class=${destructive ? "dialog-destructive" : ""}
1837
- >
1838
- <p>${message}</p>
1839
- </sp-dialog-wrapper>
1840
- `);
1841
- }
1842
- function showSaveDiscardDialog(headline, message, opts = {}) {
1843
- const { saveLabel = "Save", discardLabel = "Discard", cancelLabel = "Cancel" } = opts;
1844
- return showDialog((done) => html`
1845
- <sp-dialog-wrapper
1846
- open
1847
- underlay
1848
- headline=${headline}
1849
- confirm-label=${saveLabel}
1850
- secondary-label=${discardLabel}
1851
- cancel-label=${cancelLabel}
1852
- size="s"
1853
- @confirm=${() => done("save")}
1854
- @secondary=${() => done("discard")}
1855
- @cancel=${() => done("cancel")}
1856
- @close=${() => done("cancel")}
1857
- >
1858
- <p>${message}</p>
1859
- </sp-dialog-wrapper>
1860
- `);
1861
- }
1862
- function showPromptDialog(headline, opts = {}) {
1863
- const {
1864
- cancelLabel = "Cancel",
1865
- confirmLabel = "OK",
1866
- message,
1867
- placeholder = "",
1868
- select = "all",
1869
- validate,
1870
- value: initialValue = ""
1871
- } = opts;
1872
- const check = (candidate) => validate ? validate(candidate) : candidate.trim() ? "" : "Enter a value.";
1873
- let value = initialValue;
1874
- let error = "";
1875
- let wrapperEl = null;
1876
- let focusRequested = false;
1877
- return showDialog((done) => {
1878
- function rerender() {
1879
- const host = wrapperEl?.parentElement;
1880
- if (host) {
1881
- render(buildTpl(), host);
1882
- }
1883
- }
1884
- function confirm() {
1885
- error = check(value);
1886
- if (error) {
1887
- rerender();
1888
- return;
1889
- }
1890
- done(value.trim());
1891
- }
1892
- function onInput(e) {
1893
- value = e.target.value || "";
1894
- const next = check(value);
1895
- if (next !== error) {
1896
- error = next;
1897
- rerender();
1898
- }
1899
- }
1900
- function onKeydown(e) {
1901
- if (e.key === "Enter") {
1902
- confirm();
1903
- }
1904
- }
1905
- function onWrapperRef(el) {
1906
- if (el) {
1907
- wrapperEl = el;
1908
- }
1909
- }
1910
- function onFieldRef(el) {
1911
- if (!el || focusRequested) {
1912
- return;
1913
- }
1914
- focusRequested = true;
1915
- const field = el;
1916
- requestAnimationFrame(() => {
1917
- field.focus();
1918
- const input = field.shadowRoot?.querySelector("input");
1919
- if (!input || select === "none") {
1920
- return;
1921
- }
1922
- if (select === "stem") {
1923
- const dot = input.value.lastIndexOf(".");
1924
- input.setSelectionRange(0, dot > 0 ? dot : input.value.length);
1925
- return;
1926
- }
1927
- input.select();
1928
- });
1929
- }
1930
- function buildTpl() {
1931
- return html`
1932
- <sp-dialog-wrapper
1933
- open
1934
- underlay
1935
- headline=${headline}
1936
- confirm-label=${confirmLabel}
1937
- cancel-label=${cancelLabel}
1938
- size="s"
1939
- @confirm=${confirm}
1940
- @cancel=${() => done(null)}
1941
- @close=${() => done(null)}
1942
- ${ref(onWrapperRef)}
1943
- >
1944
- ${message ? html`<p style="margin:0 0 8px">${message}</p>` : nothing}
1945
- <sp-textfield
1946
- style="width:100%"
1947
- placeholder=${placeholder}
1948
- value=${value}
1949
- ?invalid=${Boolean(error)}
1950
- @input=${onInput}
1951
- @keydown=${onKeydown}
1952
- ${ref(onFieldRef)}
1953
- >
1954
- ${error ? html`<sp-help-text slot="negative-help-text">${error}</sp-help-text>` : nothing}
1955
- </sp-textfield>
1956
- </sp-dialog-wrapper>
1957
- `;
1958
- }
1959
- return buildTpl();
1960
- });
1961
- }
1962
- function openModal(template, opts) {
1963
- const { release, slot } = openOverlaySlot({
1964
- kind: "modal",
1965
- layer: layerHost("modal"),
1966
- onEscape(e) {
1967
- if (opts.dismissible === false) {
1968
- return;
1969
- }
1970
- e.preventDefault();
1971
- e.stopPropagation();
1972
- (opts.onDismiss ?? handle.close)();
1973
- },
1974
- regionId: opts.region,
1975
- trapFocus: true
1976
- });
1977
- slot.setAttribute("role", "dialog");
1978
- slot.setAttribute("aria-modal", "true");
1979
- slot.setAttribute("aria-label", opts.label);
1980
- const handle = {
1981
- close() {
1982
- release();
1983
- },
1984
- host: slot,
1985
- update(tpl) {
1986
- render(tpl, slot);
1987
- }
1988
- };
1989
- render(template, slot);
1990
- focusOverlay(slot);
1991
- return handle;
1992
- }
1993
- function renderPopover(template, opts = {}) {
1994
- const kind = opts.layer ?? "popover";
1995
- const slot = document.createElement("div");
1996
- slot.style.pointerEvents = "auto";
1997
- slot.setAttribute(REGION_ATTR, overlayRegion(kind, opts.region));
1998
- layerHost(kind).append(slot);
1999
- render(template, slot);
2000
- let outsideClickHandler = null;
2001
- if (opts.dismissOnOutsideClick !== false) {
2002
- outsideClickHandler = (e) => {
2003
- if (!slot.contains(e.target)) {
2004
- handle.dismiss();
2005
- opts.onDismiss?.();
2006
- }
2007
- };
2008
- requestAnimationFrame(() => {
2009
- if (outsideClickHandler) {
2010
- document.addEventListener("mousedown", outsideClickHandler, true);
2011
- }
2012
- });
2013
- }
2014
- const handle = {
2015
- dismiss() {
2016
- if (outsideClickHandler) {
2017
- document.removeEventListener("mousedown", outsideClickHandler, true);
2018
- outsideClickHandler = null;
2019
- }
2020
- render(nothing, slot);
2021
- slot.remove();
2022
- },
2023
- host: slot,
2024
- update(tpl) {
2025
- render(tpl, slot);
2026
- }
2027
- };
2028
- return handle;
2029
- }
2030
- var _namedSlots = new Map;
2031
- function popoverLayerFor(anchor) {
2032
- if (anchor?.closest("#layer-dialog")) {
2033
- return "dialog";
2034
- }
2035
- return anchor?.closest("#layer-modal") ? "modal" : "popover";
2036
- }
2037
- function getLayerSlot(layer, id) {
2038
- const key = `${layer}:${id}`;
2039
- let slot = _namedSlots.get(key);
2040
- if (slot && slot.parentElement) {
2041
- return slot;
2042
- }
2043
- slot = document.createElement("div");
2044
- slot.style.pointerEvents = "auto";
2045
- slot.setAttribute(REGION_ATTR, overlayRegion(layer, id));
2046
- layerHost(layer).append(slot);
2047
- _namedSlots.set(key, slot);
2048
- return slot;
2049
- }
2050
- var TOAST_ENTER_MS = 180;
2051
- var TOAST_ICON = {
2052
- error: "✕",
2053
- info: "ℹ",
2054
- success: "✓",
2055
- warn: "!"
2056
- };
2057
- var _toastScope = null;
2058
- var _toastTimers = new Map;
2059
- var _toastEntering = new Set;
2060
- function reducedMotion() {
2061
- return globalThis.matchMedia?.("(prefers-reduced-motion: reduce)").matches === true;
2062
- }
2063
- function toastsAreHeld() {
2064
- try {
2065
- return new URLSearchParams(globalThis.location?.search ?? "").get("automation") === "1";
2066
- } catch {
2067
- return false;
2068
- }
2069
- }
2070
- function overlayIdleBlockers() {
2071
- return [..._toastEntering].map((id) => `overlay: toast ${id} settling in`);
2072
- }
2073
- function retireToast(id) {
2074
- const timer = _toastTimers.get(id);
2075
- if (timer !== undefined) {
2076
- clearTimeout(timer);
2077
- _toastTimers.delete(id);
2078
- }
2079
- _toastEntering.delete(id);
2080
- dismiss(id);
2081
- }
2082
- function scheduleToast(record) {
2083
- if (_toastTimers.has(record.id) || _toastEntering.has(record.id)) {
2084
- return;
2085
- }
2086
- const settle = reducedMotion() ? 0 : TOAST_ENTER_MS;
2087
- if (settle > 0) {
2088
- _toastEntering.add(record.id);
2089
- setTimeout(() => _toastEntering.delete(record.id), settle);
2090
- }
2091
- const rest = record.timeoutMs ?? 0;
2092
- if (rest <= 0 || toastsAreHeld()) {
2093
- return;
2094
- }
2095
- _toastTimers.set(record.id, setTimeout(() => retireToast(record.id), rest));
2096
- }
2097
- function toastActionTpl(record) {
2098
- const registry = record.action === undefined ? null : activeRegistry();
2099
- const id = record.action;
2100
- if (!registry || id === undefined || !registry.get(id) || !registry.isVisible(id)) {
2101
- return nothing;
2102
- }
2103
- const command = registry.get(id);
2104
- const reason = registry.disabledReason(id);
2105
- return html`
2106
- <button
2107
- class="toast-action"
2108
- ?disabled=${reason !== undefined}
2109
- title=${reason === undefined ? command.title : `${command.title} — requires ${reason}`}
2110
- @click=${() => {
2111
- retireToast(record.id);
2112
- registry.run(id, record.actionArgs);
2113
- }}
2114
- >
2115
- ${command.title}
2116
- </button>
2117
- `;
2118
- }
2119
- function toastTpl(record) {
2120
- return html`
2121
- <div class="toast toast--${record.severity}">
2122
- <span class="toast-icon" aria-hidden="true">${TOAST_ICON[record.severity]}</span>
2123
- <span class="toast-message">${record.message}</span>
2124
- ${toastActionTpl(record)}
2125
- <button
2126
- class="toast-dismiss"
2127
- title="Dismiss"
2128
- aria-label="Dismiss notification"
2129
- @click=${() => retireToast(record.id)}
2130
- >
2131
- <span aria-hidden="true">×</span>
2132
- </button>
2133
- </div>
2134
- `;
2135
- }
2136
- function toastStackTemplate() {
2137
- return html`
2138
- <div class="toast-stack">
2139
- ${repeat(toasts, (record) => record.id, (record) => toastTpl(record))}
2140
- </div>
2141
- `;
2142
- }
2143
- function mountToastHost() {
2144
- unmountToastHost();
2145
- if (!_toastLayer) {
2146
- return;
2147
- }
2148
- _toastScope = effectScope();
2149
- _toastScope.run(() => {
2150
- effect(() => {
2151
- toasts.length;
2152
- activeRegistry();
2153
- for (const record of toasts) {
2154
- scheduleToast(record);
2155
- }
2156
- render(toastStackTemplate(), _toastLayer);
2157
- });
2158
- });
2159
- }
2160
- function unmountToastHost() {
2161
- _toastScope?.stop();
2162
- _toastScope = null;
2163
- for (const timer of _toastTimers.values()) {
2164
- clearTimeout(timer);
2165
- }
2166
- _toastTimers.clear();
2167
- _toastEntering.clear();
2168
- if (_toastLayer) {
2169
- render(nothing, _toastLayer);
2170
- }
2171
- }
2172
-
2173
- // src/collab/collab-state.ts
2174
- var states = new WeakMap;
2175
- var attachedPaths = new Map;
2176
- function collabState(tab) {
2177
- const key = toRaw(tab);
2178
- let state = states.get(key);
2179
- if (!state) {
2180
- state = reactive({
2181
- active: false,
2182
- attachError: "",
2183
- peers: [],
2184
- readOnly: false,
2185
- sourceCanonical: false,
2186
- status: "unavailable"
2187
- });
2188
- states.set(key, state);
2189
- }
2190
- return state;
2191
- }
2192
- function registerCollabPath(path) {
2193
- attachedPaths.set(path, (attachedPaths.get(path) ?? 0) + 1);
2194
- }
2195
- function unregisterCollabPath(path) {
2196
- const count = attachedPaths.get(path) ?? 0;
2197
- if (count <= 1) {
2198
- attachedPaths.delete(path);
2199
- } else {
2200
- attachedPaths.set(path, count - 1);
2201
- }
2202
- }
2203
- function isCollabPath(path) {
2204
- return attachedPaths.has(path.replaceAll("\\", "/"));
2205
- }
2206
-
2207
- // src/commands/context.ts
2208
- var EDITOR_KIND_BY_MODE = {
2209
- design: "canvas",
2210
- edit: "canvas",
2211
- entry: "entry",
2212
- "git-diff": "diff",
2213
- grid: "grid",
2214
- manage: "library",
2215
- preview: "canvas",
2216
- settings: "config",
2217
- source: "code",
2218
- stylebook: "config"
2219
- };
2220
- var EDITOR_KIND_LABELS = {
2221
- canvas: "Canvas",
2222
- code: "Code",
2223
- config: "Project Styles",
2224
- diff: "Diff",
2225
- entry: "Entry",
2226
- grid: "Grid",
2227
- library: "Library",
2228
- none: "None"
2229
- };
2230
- function editorKindForMode(mode) {
2231
- return EDITOR_KIND_BY_MODE[mode] ?? "canvas";
2232
- }
2233
- var CANVAS_VIEW_BY_MODE = {
2234
- design: "design",
2235
- edit: "edit",
2236
- preview: "preview"
2237
- };
2238
- function canvasViewForMode(mode) {
2239
- return CANVAS_VIEW_BY_MODE[mode] ?? "design";
2240
- }
2241
- var inCanvas = (ctx) => ctx.editor.kind === "canvas";
2242
- var hasSelection = (ctx) => inCanvas(ctx) && ctx.selection.count > 0;
2243
- var hasElementSelection = (ctx) => hasSelection(ctx) && !ctx.selection.isRoot;
2244
- function emptyContext() {
2245
- return {
2246
- project: { open: false, isSite: false, isRepo: false, isMultilingual: false },
2247
- git: { ahead: 0, behind: 0, dirtyCount: 0 },
2248
- document: { open: false, dirty: false, mode: "", canUndo: false, canRedo: false },
2249
- editor: { kind: "none" },
2250
- canvas: { view: "design" },
2251
- pane: { count: 1, derived: false },
2252
- selection: {
2253
- count: 0,
2254
- paths: [],
2255
- kind: "",
2256
- isRoot: false,
2257
- isComponentInstance: false,
2258
- isLayoutNode: false,
2259
- isRepeater: false
2260
- },
2261
- caret: { active: false, inCanvas: false },
2262
- focus: { region: "pane" },
2263
- modal: { open: false },
2264
- collab: { attached: false, readOnly: false, sourceCanonical: false },
2265
- ai: { configured: false, streaming: false },
2266
- capability: {
2267
- gitClone: false,
2268
- importSite: false,
2269
- openProjectInNewWindow: false,
2270
- dataRows: false,
2271
- windowControls: false,
2272
- findReferences: false
2273
- }
2274
- };
2275
- }
2276
- function makeContext(patch = {}) {
2277
- const base = emptyContext();
2278
- for (const key of Object.keys(patch)) {
2279
- const group = patch[key];
2280
- if (group) {
2281
- Object.assign(base[key], group);
2282
- }
2283
- }
2284
- return base;
2285
- }
2286
- var MODAL_STACK = ["palette"];
2287
- var CARET_STACK = ["caret", "global"];
2288
- var DOCK_STACK = ["dock", "global"];
2289
- var CANVAS_STACK = ["canvas", "global"];
2290
- var GRID_STACK = ["grid", "global"];
2291
- var CODE_STACK = ["code", "global"];
2292
- var GLOBAL_STACK = ["global"];
2293
- function keyScopeStack(ctx) {
2294
- if (ctx.modal.open) {
2295
- return MODAL_STACK;
2296
- }
2297
- if (ctx.caret.active) {
2298
- return CARET_STACK;
2299
- }
2300
- if (ctx.focus.region !== "pane") {
2301
- return DOCK_STACK;
2302
- }
2303
- switch (ctx.editor.kind) {
2304
- case "grid": {
2305
- return GRID_STACK;
2306
- }
2307
- case "code": {
2308
- return CODE_STACK;
2309
- }
2310
- case "canvas": {
2311
- return ctx.canvas.view === "preview" ? GLOBAL_STACK : CANVAS_STACK;
2312
- }
2313
- default: {
2314
- return GLOBAL_STACK;
1511
+ default: {
1512
+ return GLOBAL_STACK;
2315
1513
  }
2316
1514
  }
2317
1515
  }
@@ -2349,7 +1547,7 @@ function createTab({
2349
1547
  id,
2350
1548
  documentPath = null,
2351
1549
  fileHandle = null,
2352
- document: document2,
1550
+ document,
2353
1551
  frontmatter,
2354
1552
  sourceFormat = null,
2355
1553
  capabilities,
@@ -2357,7 +1555,7 @@ function createTab({
2357
1555
  preview: previewTab = false
2358
1556
  }) {
2359
1557
  const scope = effectScope();
2360
- normalizeArrayChildren(document2);
1558
+ normalizeArrayChildren(document);
2361
1559
  const resolvedModes = capabilities?.modes ?? inferModes(documentPath, sourceFormat);
2362
1560
  const initialCanvasMode = resolvedModes.find((m) => m !== "preview") ?? "edit";
2363
1561
  const initialPreview = resolvedModes[0] === "preview";
@@ -2366,7 +1564,7 @@ function createTab({
2366
1564
  doc: reactive({
2367
1565
  content: { frontmatter: frontmatter || {} },
2368
1566
  dirty: false,
2369
- document: document2,
1567
+ document,
2370
1568
  handlersSource: null,
2371
1569
  mode: inferDocumentMode(documentPath, sourceFormat),
2372
1570
  sourceFormat
@@ -2375,7 +1573,7 @@ function createTab({
2375
1573
  fileHandle,
2376
1574
  history: reactive({
2377
1575
  index: 0,
2378
- snapshots: [{ document: structuredClone(document2), selection: [] }]
1576
+ snapshots: [{ document: structuredClone(document), selection: [] }]
2379
1577
  }),
2380
1578
  id,
2381
1579
  pinned: false,
@@ -2439,6 +1637,40 @@ function disposeTab(tab) {
2439
1637
  tab.scope.stop();
2440
1638
  }
2441
1639
 
1640
+ // src/collab/collab-state.ts
1641
+ var states = new WeakMap;
1642
+ var attachedPaths = new Map;
1643
+ function collabState(tab) {
1644
+ const key = toRaw(tab);
1645
+ let state = states.get(key);
1646
+ if (!state) {
1647
+ state = reactive({
1648
+ active: false,
1649
+ attachError: "",
1650
+ peers: [],
1651
+ readOnly: false,
1652
+ sourceCanonical: false,
1653
+ status: "unavailable"
1654
+ });
1655
+ states.set(key, state);
1656
+ }
1657
+ return state;
1658
+ }
1659
+ function registerCollabPath(path) {
1660
+ attachedPaths.set(path, (attachedPaths.get(path) ?? 0) + 1);
1661
+ }
1662
+ function unregisterCollabPath(path) {
1663
+ const count = attachedPaths.get(path) ?? 0;
1664
+ if (count <= 1) {
1665
+ attachedPaths.delete(path);
1666
+ } else {
1667
+ attachedPaths.set(path, count - 1);
1668
+ }
1669
+ }
1670
+ function isCollabPath(path) {
1671
+ return attachedPaths.has(path.replaceAll("\\", "/"));
1672
+ }
1673
+
2442
1674
  // src/collab/collab-session.ts
2443
1675
  var SYNC_TIMEOUT_MS = 8000;
2444
1676
  var MIRROR_DEBOUNCE_MS = 800;
@@ -3116,14 +2348,14 @@ function createSession(tab, path, collab, handle) {
3116
2348
  }
3117
2349
  });
3118
2350
  effect(() => {
3119
- const ref2 = toRaw(tab.doc.document);
2351
+ const ref = toRaw(tab.doc.document);
3120
2352
  queueMicrotask(() => {
3121
2353
  const current = runtimeOf(tab)?.session;
3122
2354
  if (current !== session || !session.synced || !session.canWrite) {
3123
2355
  return;
3124
2356
  }
3125
2357
  const nowRef = toRaw(tab.doc.document);
3126
- if (nowRef !== ref2 || nowRef === session.lastSeenRef) {
2358
+ if (nowRef !== ref || nowRef === session.lastSeenRef) {
3127
2359
  return;
3128
2360
  }
3129
2361
  session.lastSeenRef = nowRef;
@@ -3245,10 +2477,19 @@ function argsSchema(properties, required = Object.keys(properties)) {
3245
2477
  function enumProperty(declared, description) {
3246
2478
  return { description, enum: [...declared], type: "string" };
3247
2479
  }
3248
- function numberProperty(description, bounds = {}) {
3249
- return { description, type: "number", ...bounds };
3250
- }
3251
- function booleanProperty(description) {
2480
+ function derivedEnumProperty(declared, description) {
2481
+ return {
2482
+ description,
2483
+ get enum() {
2484
+ return [...declared()];
2485
+ },
2486
+ type: "string"
2487
+ };
2488
+ }
2489
+ function numberProperty(description, bounds = {}) {
2490
+ return { description, type: "number", ...bounds };
2491
+ }
2492
+ function booleanProperty(description) {
3252
2493
  return { description, type: "boolean" };
3253
2494
  }
3254
2495
  function pathProperty(description, nullable = false) {
@@ -3273,6 +2514,7 @@ function stringProperty(description) {
3273
2514
  var CLOSED_TAB_LIMIT = 20;
3274
2515
  var PRIMARY_PANE = "primary";
3275
2516
  var SECONDARY_PANE = "secondary";
2517
+ var MAX_PANES = 2;
3276
2518
  var workspace = reactive({
3277
2519
  activePaneId: PRIMARY_PANE,
3278
2520
  clipboard: null,
@@ -3338,6 +2580,12 @@ function focusPane(paneId) {
3338
2580
  function isPaneFocused(paneId) {
3339
2581
  return workspace.activePaneId === paneId;
3340
2582
  }
2583
+ function focusOtherPane() {
2584
+ const other = workspace.panes.find((pane) => pane.id !== workspace.activePaneId);
2585
+ if (other) {
2586
+ focusPane(other.id);
2587
+ }
2588
+ }
3341
2589
  function splitRight() {
3342
2590
  const source = activePane();
3343
2591
  const tabId = source.activeTabId;
@@ -3633,6 +2881,13 @@ function cycleTab(step) {
3633
2881
  setActiveTab(id, { cycling: true });
3634
2882
  return id;
3635
2883
  }
2884
+ function endTabCycle() {
2885
+ const settled = workspace.activeTabId;
2886
+ resetTabCycle();
2887
+ if (settled) {
2888
+ promoteMru(settled);
2889
+ }
2890
+ }
3636
2891
  function replaceAllTabs(newTabOpts) {
3637
2892
  const oldIds = [...workspace.tabs.keys()];
3638
2893
  const oldTabs = [...workspace.tabs.values()];
@@ -3902,1267 +3157,7 @@ function runUnsplit() {
3902
3157
  closePane(derived?.id ?? SECONDARY_PANE);
3903
3158
  }
3904
3159
 
3905
- // src/workspace/session.ts
3906
- var PANE_IDS = new Set([PRIMARY_PANE, SECONDARY_PANE]);
3907
- var SCHEMES = new Set(["auto", "light", "dark"]);
3908
- function uiOf(tab) {
3909
- const { ui } = tab.session;
3910
- return {
3911
- activeMedia: ui.activeMedia,
3912
- canvasMode: ui.canvasMode,
3913
- editZoom: ui.editZoom,
3914
- preview: ui.preview,
3915
- previewColorScheme: ui.previewColorScheme,
3916
- previewLocale: ui.previewLocale,
3917
- showLayout: ui.showLayout,
3918
- zoom: ui.zoom
3919
- };
3920
- }
3921
- function captureSession() {
3922
- const ui = {};
3923
- const panes = [];
3924
- for (const pane of workspace.panes) {
3925
- if (pane.derived) {
3926
- continue;
3927
- }
3928
- const files = [];
3929
- for (const tabId of pane.tabOrder) {
3930
- const tab = workspace.tabs.get(tabId);
3931
- if (!tab?.documentPath) {
3932
- continue;
3933
- }
3934
- files.push(tab.documentPath);
3935
- ui[tab.documentPath] = uiOf(tab);
3936
- }
3937
- if (files.length === 0) {
3938
- continue;
3939
- }
3940
- const activeTab2 = pane.activeTabId ? workspace.tabs.get(pane.activeTabId) : null;
3941
- panes.push({
3942
- activeFile: activeTab2?.documentPath ?? null,
3943
- files,
3944
- id: pane.id
3945
- });
3946
- }
3947
- return { focusedPane: workspace.activePaneId, panes, ui };
3948
- }
3949
- function readTabUi(value) {
3950
- if (typeof value !== "object" || value === null) {
3951
- return {};
3952
- }
3953
- const raw = value;
3954
- const out = {};
3955
- if (typeof raw.canvasMode === "string") {
3956
- out.canvasMode = raw.canvasMode;
3957
- }
3958
- if (typeof raw.preview === "boolean") {
3959
- out.preview = raw.preview;
3960
- }
3961
- if (typeof raw.zoom === "number" && Number.isFinite(raw.zoom)) {
3962
- out.zoom = raw.zoom;
3963
- }
3964
- if (typeof raw.editZoom === "number" && Number.isFinite(raw.editZoom)) {
3965
- out.editZoom = raw.editZoom;
3966
- }
3967
- if (raw.activeMedia === null || typeof raw.activeMedia === "string") {
3968
- out.activeMedia = raw.activeMedia;
3969
- }
3970
- if (typeof raw.previewColorScheme === "string" && SCHEMES.has(raw.previewColorScheme)) {
3971
- out.previewColorScheme = raw.previewColorScheme;
3972
- }
3973
- if (raw.previewLocale === null || isWellFormedLocale(raw.previewLocale)) {
3974
- out.previewLocale = raw.previewLocale;
3975
- }
3976
- if (typeof raw.showLayout === "boolean") {
3977
- out.showLayout = raw.showLayout;
3978
- }
3979
- return out;
3980
- }
3981
- function readSession(value) {
3982
- if (typeof value !== "object" || value === null) {
3983
- return null;
3984
- }
3985
- const raw = value;
3986
- if (!Array.isArray(raw.panes)) {
3987
- return null;
3988
- }
3989
- const panes = [];
3990
- for (const entry of raw.panes) {
3991
- if (typeof entry !== "object" || entry === null) {
3992
- continue;
3993
- }
3994
- const pane = entry;
3995
- if (typeof pane.id !== "string" || !PANE_IDS.has(pane.id)) {
3996
- continue;
3997
- }
3998
- const files = Array.isArray(pane.files) ? pane.files.filter((file) => typeof file === "string" && file !== "") : [];
3999
- if (files.length === 0) {
4000
- continue;
4001
- }
4002
- panes.push({
4003
- activeFile: typeof pane.activeFile === "string" && files.includes(pane.activeFile) ? pane.activeFile : null,
4004
- files,
4005
- id: pane.id
4006
- });
4007
- }
4008
- if (panes.length === 0) {
4009
- return null;
4010
- }
4011
- const ui = {};
4012
- if (typeof raw.ui === "object" && raw.ui !== null) {
4013
- for (const [path, entry] of Object.entries(raw.ui)) {
4014
- ui[path] = readTabUi(entry);
4015
- }
4016
- }
4017
- return {
4018
- focusedPane: typeof raw.focusedPane === "string" && PANE_IDS.has(raw.focusedPane) ? raw.focusedPane : PRIMARY_PANE,
4019
- panes,
4020
- ui
4021
- };
4022
- }
4023
- async function restoreSession(session, deps) {
4024
- let opened = 0;
4025
- if (session.panes.some((pane) => pane.id === SECONDARY_PANE)) {
4026
- deps.ensureSecondPane();
4027
- }
4028
- for (const pane of session.panes) {
4029
- for (const file of pane.files) {
4030
- try {
4031
- await deps.openFile(file, pane.id);
4032
- } catch {}
4033
- if ([...workspace.tabs.values()].some((tab) => tab.documentPath === file)) {
4034
- opened += 1;
4035
- }
4036
- }
4037
- }
4038
- if (opened === 0) {
4039
- return 0;
4040
- }
4041
- for (const tab of workspace.tabs.values()) {
4042
- const stored = tab.documentPath ? session.ui[tab.documentPath] : undefined;
4043
- if (!stored) {
4044
- continue;
4045
- }
4046
- const { ui } = tab.session;
4047
- if (stored.canvasMode && tab.capabilities.modes.includes(stored.canvasMode)) {
4048
- ui.canvasMode = stored.canvasMode;
4049
- }
4050
- if (stored.preview !== undefined) {
4051
- ui.preview = stored.preview;
4052
- }
4053
- if (stored.zoom !== undefined) {
4054
- ui.zoom = stored.zoom;
4055
- }
4056
- if (stored.editZoom !== undefined) {
4057
- ui.editZoom = stored.editZoom;
4058
- }
4059
- if (stored.activeMedia !== undefined) {
4060
- ui.activeMedia = stored.activeMedia;
4061
- }
4062
- if (stored.previewColorScheme !== undefined) {
4063
- ui.previewColorScheme = stored.previewColorScheme;
4064
- }
4065
- if (stored.previewLocale !== undefined) {
4066
- ui.previewLocale = stored.previewLocale;
4067
- }
4068
- if (stored.showLayout !== undefined) {
4069
- ui.showLayout = stored.showLayout;
4070
- }
4071
- }
4072
- for (const stored of session.panes) {
4073
- const pane = workspace.panes.find((p) => p.id === stored.id);
4074
- if (!pane || !stored.activeFile) {
4075
- continue;
4076
- }
4077
- const match = pane.tabOrder.find((tabId) => workspace.tabs.get(tabId)?.documentPath === stored.activeFile);
4078
- if (match) {
4079
- pane.activeTabId = match;
4080
- }
4081
- }
4082
- if (workspace.panes.some((pane) => pane.id === session.focusedPane)) {
4083
- focusPane(session.focusedPane);
4084
- }
4085
- return opened;
4086
- }
4087
-
4088
- // src/services/profile.ts
4089
- var STARTUP_PROFILES = {
4090
- default: {
4091
- description: "Resume whatever was persisted — layout, recents, settings, chat history.",
4092
- id: "default",
4093
- reset: "none"
4094
- },
4095
- fresh: {
4096
- description: "A first-run app: no layout, no recents, no settings, no history.",
4097
- id: "fresh",
4098
- reset: "app-state"
4099
- }
4100
- };
4101
- var DEFAULT_PROFILE_ID = "default";
4102
- var OWNED_KEY_PATTERN = /^jx[-._:]/;
4103
- function isStudioStorageKey(key) {
4104
- return OWNED_KEY_PATTERN.test(key);
4105
- }
4106
- function clearStudioStorage() {
4107
- const store = globalThis.localStorage;
4108
- if (!store) {
4109
- return [];
4110
- }
4111
- const doomed = [];
4112
- try {
4113
- for (let i = 0;i < store.length; i++) {
4114
- const key = store.key(i);
4115
- if (key && isStudioStorageKey(key)) {
4116
- doomed.push(key);
4117
- }
4118
- }
4119
- for (const key of doomed) {
4120
- store.removeItem(key);
4121
- }
4122
- } catch {}
4123
- return doomed;
4124
- }
4125
- function applyProfile(id) {
4126
- const profile = STARTUP_PROFILES[id];
4127
- if (!profile) {
4128
- throw new RangeError(`unknown startup profile "${id}"; declared: ${Object.keys(STARTUP_PROFILES).join(", ")}`);
4129
- }
4130
- if (profile.reset === "app-state") {
4131
- clearStudioStorage();
4132
- }
4133
- return profile;
4134
- }
4135
- var _startup = null;
4136
- function applyStartupProfile(search) {
4137
- if (_startup) {
4138
- return _startup;
4139
- }
4140
- const query = new URLSearchParams(search ?? globalThis.location?.search ?? "");
4141
- const profile = applyProfile(query.get("profile") ?? DEFAULT_PROFILE_ID);
4142
- const clockParam = query.get("clock");
4143
- _startup = { clock: clockParam === null ? null : pinClock(clockParam), profile };
4144
- return _startup;
4145
- }
4146
-
4147
- // src/shell.ts
4148
- var NAVIGATOR_PANEL_IDS = [
4149
- "files",
4150
- "search",
4151
- "git",
4152
- "i18n",
4153
- "layers",
4154
- "page",
4155
- "data",
4156
- "packages",
4157
- "insert"
4158
- ];
4159
- var RENAMED_PANEL_IDS = {
4160
- blocks: "insert",
4161
- head: "page",
4162
- imports: "packages",
4163
- state: "data"
4164
- };
4165
- var DEFAULT_PANEL_ID = "layers";
4166
- function isNavigatorPanelId(value) {
4167
- return typeof value === "string" && NAVIGATOR_PANEL_IDS.includes(value);
4168
- }
4169
- function requireNavigatorPanelId(value, source) {
4170
- if (!isNavigatorPanelId(value)) {
4171
- throw new Error(`${source}: "${value}" is not a declared Navigator panel id.`);
4172
- }
4173
- return value;
4174
- }
4175
- function migratePanelId(stored) {
4176
- if (typeof stored !== "string") {
4177
- return null;
4178
- }
4179
- const renamed = RENAMED_PANEL_IDS[stored];
4180
- if (renamed) {
4181
- return renamed;
4182
- }
4183
- return isNavigatorPanelId(stored) ? stored : null;
4184
- }
4185
- var INSPECTOR_TAB_IDS = ["properties", "style", "events", "assistant"];
4186
- var DEFAULT_INSPECTOR_TAB = "properties";
4187
- function isInspectorTabId(value) {
4188
- return typeof value === "string" && INSPECTOR_TAB_IDS.includes(value);
4189
- }
4190
- var BOTTOM_TAB_IDS = ["problems", "logic", "activity"];
4191
- var DEFAULT_BOTTOM_TAB = "problems";
4192
- function isBottomTabId(value) {
4193
- return typeof value === "string" && BOTTOM_TAB_IDS.includes(value);
4194
- }
4195
- var CHROME_THEMES = ["light", "dark"];
4196
- var DOCK_STORAGE_KEY = "jx-studio-panel-widths";
4197
- var PROJECT_STORAGE_PREFIX = "jx-studio-project::";
4198
- var DEFAULT_THEME = "dark";
4199
- function isChromeTheme(value) {
4200
- return typeof value === "string" && CHROME_THEMES.includes(value);
4201
- }
4202
- function readPersistedTheme() {
4203
- const stored = readStoredSetting(SETTINGS.theme);
4204
- return isChromeTheme(stored) ? stored : DEFAULT_THEME;
4205
- }
4206
- var DOCK_IDS = ["left", "right", "bottom"];
4207
- var DOCK_DEFAULTS = {
4208
- bottom: { collapsed: true, size: 220 },
4209
- left: { collapsed: false, size: 240 },
4210
- right: { collapsed: false, size: 280 }
4211
- };
4212
- var DOCK_DEFAULT_SIZES = {
4213
- bottom: DOCK_DEFAULTS.bottom.size,
4214
- left: DOCK_DEFAULTS.left.size,
4215
- right: DOCK_DEFAULTS.right.size
4216
- };
4217
- var DOCK_CSS_VAR = {
4218
- bottom: "--dock-h-bottom",
4219
- left: "--panel-w-left",
4220
- right: "--panel-w-right"
4221
- };
4222
- var DOCK_CLASS = {
4223
- bottom: "bottom-collapsed",
4224
- left: "left-collapsed",
4225
- right: "right-collapsed"
4226
- };
4227
- var DEFAULT_PANE_SPLIT = 0.5;
4228
- function clampPaneSplit(value) {
4229
- return typeof value === "number" && value >= 0.2 && value <= 0.8 ? value : DEFAULT_PANE_SPLIT;
4230
- }
4231
- function setPaneSplit(value) {
4232
- shell.paneSplit = Math.min(0.8, Math.max(0.2, value));
4233
- }
4234
- function readPersistedDocks() {
4235
- try {
4236
- return JSON.parse(localStorage.getItem(DOCK_STORAGE_KEY) || "{}");
4237
- } catch {
4238
- return {};
4239
- }
4240
- }
4241
- function freshGit() {
4242
- return {
4243
- branches: null,
4244
- commitMessage: "",
4245
- diffState: null,
4246
- error: null,
4247
- lastUpdated: null,
4248
- loading: false,
4249
- logEntries: null,
4250
- status: null,
4251
- subTab: "changes"
4252
- };
4253
- }
4254
- function freshStylebook() {
4255
- return { customizedOnly: false, filter: "", selection: null, tab: "elements" };
4256
- }
4257
- function builtInLayouts() {
4258
- return [
4259
- {
4260
- bottomTab: "problems",
4261
- docks: {
4262
- bottom: { collapsed: true, size: 220 },
4263
- left: { collapsed: false, size: 240 },
4264
- right: { collapsed: true, size: 280 }
4265
- },
4266
- id: "write",
4267
- inspectorTab: "properties",
4268
- name: "Write",
4269
- navigatorPanel: "files"
4270
- },
4271
- {
4272
- bottomTab: "problems",
4273
- docks: {
4274
- bottom: { collapsed: true, size: 220 },
4275
- left: { collapsed: false, size: 240 },
4276
- right: { collapsed: false, size: 320 }
4277
- },
4278
- id: "design",
4279
- inspectorTab: "style",
4280
- name: "Design",
4281
- navigatorPanel: "layers"
4282
- },
4283
- {
4284
- bottomTab: "logic",
4285
- docks: {
4286
- bottom: { collapsed: true, size: 260 },
4287
- left: { collapsed: false, size: 280 },
4288
- right: { collapsed: false, size: 300 }
4289
- },
4290
- id: "build",
4291
- inspectorTab: "events",
4292
- name: "Build",
4293
- navigatorPanel: "data"
4294
- },
4295
- {
4296
- bottomTab: "activity",
4297
- docks: {
4298
- bottom: { collapsed: false, size: 240 },
4299
- left: { collapsed: false, size: 300 },
4300
- right: { collapsed: true, size: 280 }
4301
- },
4302
- id: "ship",
4303
- inspectorTab: "properties",
4304
- name: "Ship",
4305
- navigatorPanel: "git"
4306
- }
4307
- ];
4308
- }
4309
- var DEFAULT_LAYOUT_ID = "design";
4310
- function createShellState() {
4311
- const saved = readPersistedDocks();
4312
- const docks = {};
4313
- for (const id of DOCK_IDS) {
4314
- const width = saved[id];
4315
- const collapsed = saved[`${id}Collapsed`];
4316
- docks[id] = {
4317
- collapsed: typeof collapsed === "boolean" ? collapsed : DOCK_DEFAULTS[id].collapsed,
4318
- size: typeof width === "number" && width > 0 ? width : DOCK_DEFAULTS[id].size
4319
- };
4320
- }
4321
- return {
4322
- bottomTab: isBottomTabId(saved.bottomTab) ? saved.bottomTab : DEFAULT_BOTTOM_TAB,
4323
- docks,
4324
- focusRegion: "pane",
4325
- paneSplit: clampPaneSplit(saved.paneSplit),
4326
- git: freshGit(),
4327
- layout: DEFAULT_LAYOUT_ID,
4328
- layoutSelection: null,
4329
- layouts: builtInLayouts(),
4330
- leftTab: migratePanelId(saved.leftTab) ?? DEFAULT_PANEL_ID,
4331
- settingsTab: "stylebook",
4332
- stylebook: freshStylebook(),
4333
- theme: readPersistedTheme()
4334
- };
4335
- }
4336
- applyStartupProfile();
4337
- var shell = reactive(createShellState());
4338
- function persistDocks() {
4339
- try {
4340
- localStorage.setItem(DOCK_STORAGE_KEY, JSON.stringify({
4341
- bottom: shell.docks.bottom.size,
4342
- bottomCollapsed: shell.docks.bottom.collapsed,
4343
- bottomTab: shell.bottomTab,
4344
- left: shell.docks.left.size,
4345
- leftCollapsed: shell.docks.left.collapsed,
4346
- leftTab: shell.leftTab,
4347
- paneSplit: shell.paneSplit,
4348
- right: shell.docks.right.size,
4349
- rightCollapsed: shell.docks.right.collapsed
4350
- }));
4351
- } catch {}
4352
- }
4353
- var _layoutRoot;
4354
- function isLayoutPreset(value) {
4355
- if (typeof value !== "object" || value === null) {
4356
- return false;
4357
- }
4358
- const preset = value;
4359
- return typeof preset.id === "string" && preset.id !== "" && typeof preset.name === "string" && isNavigatorPanelId(preset.navigatorPanel) && isInspectorTabId(preset.inspectorTab) && isBottomTabId(preset.bottomTab) && DOCK_IDS.every((dock) => typeof preset.docks?.[dock]?.size === "number");
4360
- }
4361
- function rawPersistedProject(root) {
4362
- if (!root) {
4363
- return {};
4364
- }
4365
- try {
4366
- return JSON.parse(localStorage.getItem(`${PROJECT_STORAGE_PREFIX}${root}`) || "{}");
4367
- } catch {
4368
- return {};
4369
- }
4370
- }
4371
- function readPersistedProject(root) {
4372
- const fresh = { activeLayout: null, layouts: builtInLayouts() };
4373
- if (!root) {
4374
- return fresh;
4375
- }
4376
- const parsed = rawPersistedProject(root);
4377
- const layouts = Array.isArray(parsed.layouts) ? parsed.layouts.filter(isLayoutPreset) : [];
4378
- if (layouts.length === 0) {
4379
- return fresh;
4380
- }
4381
- return {
4382
- activeLayout: typeof parsed.activeLayout === "string" ? parsed.activeLayout : null,
4383
- layouts
4384
- };
4385
- }
4386
- function persistedSession(root) {
4387
- return readSession(rawPersistedProject(root).session);
4388
- }
4389
- var _sessionRoot = null;
4390
- function markSessionRestored(root) {
4391
- _sessionRoot = root;
4392
- }
4393
- function flushSession() {
4394
- persistProjectShell();
4395
- }
4396
- function persistProjectShell() {
4397
- const root = workspace.projectRoot;
4398
- if (!root) {
4399
- return;
4400
- }
4401
- try {
4402
- localStorage.setItem(`${PROJECT_STORAGE_PREFIX}${root}`, JSON.stringify({
4403
- activeLayout: shell.layout,
4404
- layouts: shell.layouts,
4405
- session: root === _sessionRoot ? captureSession() : rawPersistedProject(root).session
4406
- }));
4407
- } catch {}
4408
- }
4409
- function syncProjectLayouts(root) {
4410
- if (root === _layoutRoot) {
4411
- return;
4412
- }
4413
- _layoutRoot = root;
4414
- const { activeLayout, layouts } = readPersistedProject(root);
4415
- shell.layouts = layouts;
4416
- const has = (id) => id !== null && layouts.some((preset) => preset.id === id);
4417
- shell.layout = has(activeLayout) ? activeLayout : has(shell.layout) ? shell.layout : has(DEFAULT_LAYOUT_ID) ? DEFAULT_LAYOUT_ID : layouts[0]?.id ?? DEFAULT_LAYOUT_ID;
4418
- }
4419
- function layoutById(id) {
4420
- return shell.layouts.find((preset) => preset.id === id);
4421
- }
4422
- function layoutIdFor(name) {
4423
- const slug = name.trim().toLowerCase().replaceAll(/[^a-z0-9]+/g, "-").replaceAll(/^-|-$/g, "");
4424
- return slug || "layout";
4425
- }
4426
- function applyLayout(id, deps) {
4427
- const preset = layoutById(id);
4428
- if (!preset) {
4429
- throw new RangeError(`no layout named "${id}" — this project has: ${shell.layouts.map((p) => p.id).join(", ")}`);
4430
- }
4431
- shell.layout = preset.id;
4432
- shell.leftTab = preset.navigatorPanel;
4433
- shell.bottomTab = preset.bottomTab;
4434
- for (const dock of DOCK_IDS) {
4435
- shell.docks[dock].collapsed = preset.docks[dock].collapsed;
4436
- shell.docks[dock].size = preset.docks[dock].size;
4437
- }
4438
- deps.setInspectorTab(preset.inspectorTab);
4439
- persistDocks();
4440
- persistProjectShell();
4441
- }
4442
- function saveLayout(name, deps) {
4443
- const trimmed = name.trim();
4444
- const id = layoutIdFor(trimmed);
4445
- const docks = {};
4446
- for (const dock of DOCK_IDS) {
4447
- docks[dock] = { collapsed: shell.docks[dock].collapsed, size: shell.docks[dock].size };
4448
- }
4449
- const preset = {
4450
- bottomTab: isBottomTabId(shell.bottomTab) ? shell.bottomTab : DEFAULT_BOTTOM_TAB,
4451
- docks,
4452
- id,
4453
- inspectorTab: deps.inspectorTab(),
4454
- name: trimmed || id,
4455
- navigatorPanel: migratePanelId(shell.leftTab) ?? DEFAULT_PANEL_ID
4456
- };
4457
- const existing = shell.layouts.findIndex((entry) => entry.id === id);
4458
- if (existing === -1) {
4459
- shell.layouts.push(preset);
4460
- } else {
4461
- shell.layouts.splice(existing, 1, preset);
4462
- }
4463
- shell.layout = id;
4464
- persistProjectShell();
4465
- return preset;
4466
- }
4467
- function renameLayout(id, name) {
4468
- const preset = layoutById(id);
4469
- const trimmed = name.trim();
4470
- if (!preset || !trimmed) {
4471
- return;
4472
- }
4473
- preset.name = trimmed;
4474
- persistProjectShell();
4475
- }
4476
- function deleteLayout(id) {
4477
- if (shell.layouts.length < 2) {
4478
- return;
4479
- }
4480
- const index = shell.layouts.findIndex((preset) => preset.id === id);
4481
- if (index === -1) {
4482
- return;
4483
- }
4484
- shell.layouts.splice(index, 1);
4485
- if (shell.layout === id) {
4486
- shell.layout = shell.layouts[0].id;
4487
- }
4488
- persistProjectShell();
4489
- }
4490
- function resetLayout(deps) {
4491
- applyLayout(shell.layout, deps);
4492
- }
4493
- function monacoTheme(theme = shell.theme) {
4494
- return theme === "light" ? "vs" : "vs-dark";
4495
- }
4496
- function applyChromeTheme() {
4497
- document.querySelector("sp-theme")?.setAttribute("color", shell.theme);
4498
- document.documentElement.dataset.theme = shell.theme;
4499
- loadedMonaco()?.editor.setTheme(monacoTheme());
4500
- }
4501
- function applyDockLayout() {
4502
- const root = document.documentElement;
4503
- for (const id of DOCK_IDS) {
4504
- root.style.setProperty(DOCK_CSS_VAR[id], `${shell.docks[id].size}px`);
4505
- }
4506
- const app = document.querySelector("#app");
4507
- if (!app) {
4508
- return;
4509
- }
4510
- for (const id of DOCK_IDS) {
4511
- app.classList.toggle(DOCK_CLASS[id], shell.docks[id].collapsed);
4512
- }
4513
- }
4514
- var _scope = null;
4515
- var _surfaces2 = [];
4516
- function registerShellSurface(surface) {
4517
- if (!_surfaces2.includes(surface)) {
4518
- _surfaces2.push(surface);
4519
- }
4520
- }
4521
- function mountShell() {
4522
- if (_scope) {
4523
- return;
4524
- }
4525
- for (const surface of _surfaces2) {
4526
- surface.mount();
4527
- }
4528
- _scope = effectScope();
4529
- _scope.run(() => {
4530
- effect(() => {
4531
- applyDockLayout();
4532
- });
4533
- effect(() => {
4534
- applyChromeTheme();
4535
- });
4536
- effect(() => {
4537
- syncProjectLayouts(workspace.projectRoot);
4538
- });
4539
- effect(() => {
4540
- const root = workspace.projectRoot;
4541
- workspace.activePaneId;
4542
- for (const pane of workspace.panes) {
4543
- pane.id;
4544
- pane.activeTabId;
4545
- pane.tabOrder.join("\x00");
4546
- }
4547
- if (root) {
4548
- persistProjectShell();
4549
- }
4550
- });
4551
- });
4552
- }
4553
- function setDockCollapsed(dock, collapsed) {
4554
- if (shell.docks[dock].collapsed === collapsed) {
4555
- persistDocks();
4556
- return;
4557
- }
4558
- shell.docks[dock].collapsed = collapsed;
4559
- persistDocks();
4560
- }
4561
- function toggleDock(dock) {
4562
- setDockCollapsed(dock, !shell.docks[dock].collapsed);
4563
- }
4564
- function setDockSize(dock, size) {
4565
- shell.docks[dock].size = size;
4566
- }
4567
- function setActivityTab(tab) {
4568
- shell.leftTab = tab;
4569
- setDockCollapsed("left", false);
4570
- persistDocks();
4571
- }
4572
- function setBottomTab(tab) {
4573
- shell.bottomTab = tab;
4574
- setDockCollapsed("bottom", false);
4575
- persistDocks();
4576
- }
4577
- function setChromeTheme(theme) {
4578
- if (shell.theme === theme) {
4579
- return;
4580
- }
4581
- shell.theme = theme;
4582
- setSetting(SETTINGS.theme, theme);
4583
- }
4584
- function toggleActivityTab(tab) {
4585
- if (tab === shell.leftTab && !shell.docks.left.collapsed) {
4586
- setDockCollapsed("left", true);
4587
- return;
4588
- }
4589
- setActivityTab(tab);
4590
- }
4591
- function setLayoutSelection(selection) {
4592
- shell.layoutSelection = selection;
4593
- }
4594
- function resetProjectShell() {
4595
- Object.assign(shell.git, freshGit());
4596
- Object.assign(shell.stylebook, freshStylebook());
4597
- shell.settingsTab = "stylebook";
4598
- shell.layoutSelection = null;
4599
- _sessionRoot = null;
4600
- _layoutRoot = undefined;
4601
- }
4602
- var projectOpen = (ctx) => ctx.project.open;
4603
- function shellViewCommands(deps) {
4604
- return [
4605
- {
4606
- args: argsSchema({
4607
- tab: enumProperty(NAVIGATOR_PANEL_IDS, "Which Navigator panel to show.")
4608
- }),
4609
- category: "View",
4610
- id: "view.setActivity",
4611
- level: "application",
4612
- menus: ["commandbar/overflow", "palette"],
4613
- group: "4_docks",
4614
- requires: "an open project",
4615
- when: projectOpen,
4616
- aiTool: {
4617
- description: "Show one of the Navigator panels (Files, Search, Source Control, Languages, Outline, " + "Page, Data, Packages, Insert) and open the Navigator dock if it is closed. Languages is " + "the translation-parity grid, and it exists only in a project that declares two or more " + "locales. Problems is a Bottom dock tab — use show_bottom_tab for it.",
4618
- name: "show_navigator_panel"
4619
- },
4620
- run: (_ctx, args) => {
4621
- const tab = enumArg("view.setActivity", args, "tab", NAVIGATOR_PANEL_IDS);
4622
- if (deps.panelAvailable && !deps.panelAvailable(tab)) {
4623
- throw new RangeError(`command "view.setActivity" argument "tab": "${tab}" names a panel that is registered but not ` + `available right now, so showing it would persist a Navigator tab nothing can draw`);
4624
- }
4625
- setActivityTab(tab);
4626
- },
4627
- title: "Show Navigator Panel"
4628
- },
4629
- {
4630
- args: argsSchema({
4631
- tab: enumProperty(INSPECTOR_TAB_IDS, "Which Inspector tab to show.")
4632
- }),
4633
- category: "View",
4634
- id: "view.setRightTab",
4635
- level: "document",
4636
- menus: ["palette"],
4637
- group: "4_docks",
4638
- requires: "an open document",
4639
- when: (ctx) => ctx.document.open,
4640
- run: (_ctx, args) => {
4641
- deps.setInspectorTab(enumArg("view.setRightTab", args, "tab", INSPECTOR_TAB_IDS));
4642
- },
4643
- title: "Show Inspector Tab"
4644
- },
4645
- {
4646
- args: argsSchema({
4647
- open: booleanProperty("True to show the Navigator dock, false to collapse it.")
4648
- }),
4649
- category: "View",
4650
- id: "view.setNavigator",
4651
- level: "application",
4652
- menus: ["palette"],
4653
- group: "4_docks",
4654
- run: (_ctx, args) => {
4655
- setDockCollapsed("left", !booleanArg("view.setNavigator", args, "open"));
4656
- },
4657
- title: "Show Navigator Dock"
4658
- },
4659
- {
4660
- args: argsSchema({
4661
- open: booleanProperty("True to show the Inspector dock, false to collapse it.")
4662
- }),
4663
- category: "View",
4664
- id: "view.setRightPanel",
4665
- level: "application",
4666
- menus: ["palette"],
4667
- group: "4_docks",
4668
- run: (_ctx, args) => {
4669
- setDockCollapsed("right", !booleanArg("view.setRightPanel", args, "open"));
4670
- },
4671
- title: "Show Inspector Dock"
4672
- },
4673
- {
4674
- args: argsSchema({
4675
- open: booleanProperty("True to show the Bottom dock, false to collapse it.")
4676
- }),
4677
- category: "View",
4678
- id: "view.setBottomDock",
4679
- level: "application",
4680
- menus: ["palette"],
4681
- group: "4_docks",
4682
- run: (_ctx, args) => {
4683
- setDockCollapsed("bottom", !booleanArg("view.setBottomDock", args, "open"));
4684
- },
4685
- title: "Show Bottom Dock"
4686
- },
4687
- {
4688
- category: "View",
4689
- id: "view.toggleBottomDock",
4690
- keybinding: "mod+j",
4691
- level: "application",
4692
- menus: ["commandbar/overflow", "palette"],
4693
- group: "4_docks",
4694
- run: () => {
4695
- toggleDock("bottom");
4696
- },
4697
- title: "Toggle Bottom Dock"
4698
- },
4699
- {
4700
- args: argsSchema({
4701
- tab: enumProperty(BOTTOM_TAB_IDS, "Which Bottom dock tab to show.")
4702
- }),
4703
- category: "View",
4704
- id: "view.setBottomTab",
4705
- level: "application",
4706
- menus: ["palette"],
4707
- group: "4_docks",
4708
- aiTool: {
4709
- description: "Show one of the Bottom dock's tabs (Problems, Diff, Logic, Activity) and open the " + "Bottom dock if it is closed.",
4710
- name: "show_bottom_tab"
4711
- },
4712
- run: (_ctx, args) => {
4713
- const tab = enumArg("view.setBottomTab", args, "tab", BOTTOM_TAB_IDS);
4714
- if (deps.panelAvailable && !deps.panelAvailable(tab)) {
4715
- throw new RangeError(`command "view.setBottomTab" argument "tab": "${tab}" names a panel that is registered but not ` + `available right now, so showing it would persist a Bottom dock tab nothing can draw`);
4716
- }
4717
- setBottomTab(tab);
4718
- },
4719
- title: "Show Bottom Dock Tab"
4720
- },
4721
- {
4722
- args: argsSchema({
4723
- open: booleanProperty("True to show the Assistant tab, false to step off it.")
4724
- }),
4725
- category: "View",
4726
- id: "view.setAssistant",
4727
- level: "application",
4728
- menus: ["never"],
4729
- group: "4_docks",
4730
- run: (_ctx, args) => {
4731
- if (booleanArg("view.setAssistant", args, "open")) {
4732
- setDockCollapsed("right", false);
4733
- deps.setInspectorTab("assistant");
4734
- return;
4735
- }
4736
- if (deps.inspectorTab() === "assistant") {
4737
- deps.setInspectorTab(DEFAULT_INSPECTOR_TAB);
4738
- }
4739
- },
4740
- title: "Show Assistant"
4741
- },
4742
- {
4743
- args: argsSchema({
4744
- layout: stringProperty("The layout's id — one of this project's saved layouts.")
4745
- }),
4746
- category: "View",
4747
- id: "view.setLayout",
4748
- level: "application",
4749
- menus: ["palette"],
4750
- group: "4_layouts",
4751
- requires: "an open project",
4752
- when: projectOpen,
4753
- aiTool: {
4754
- description: "Adopt one of the project's named layouts (Write, Design, Build, Ship, or one the user " + "saved): its Navigator panel, its dock widths and visibility, and its Inspector tab.",
4755
- name: "set_layout"
4756
- },
4757
- run: (_ctx, args) => {
4758
- applyLayout(stringArg("view.setLayout", args, "layout"), deps);
4759
- },
4760
- title: "Switch Layout"
4761
- },
4762
- {
4763
- args: argsSchema({
4764
- name: stringProperty("What to call this arrangement. An existing name is overwritten.")
4765
- }),
4766
- category: "View",
4767
- id: "view.saveLayout",
4768
- level: "application",
4769
- menus: ["palette"],
4770
- group: "4_layouts",
4771
- requires: "an open project",
4772
- when: projectOpen,
4773
- run: (_ctx, args) => {
4774
- saveLayout(stringArg("view.saveLayout", args, "name"), deps);
4775
- },
4776
- title: "Save Layout"
4777
- },
4778
- {
4779
- args: argsSchema({
4780
- layout: stringProperty("The layout's id."),
4781
- name: stringProperty("Its new name. The id, and every reference to it, is unchanged.")
4782
- }),
4783
- category: "View",
4784
- id: "view.renameLayout",
4785
- level: "application",
4786
- menus: ["palette"],
4787
- group: "4_layouts",
4788
- requires: "an open project",
4789
- when: projectOpen,
4790
- run: (_ctx, args) => {
4791
- renameLayout(stringArg("view.renameLayout", args, "layout"), stringArg("view.renameLayout", args, "name"));
4792
- },
4793
- title: "Rename Layout"
4794
- },
4795
- {
4796
- args: argsSchema({
4797
- layout: stringProperty("The layout's id. The last remaining layout is kept.")
4798
- }),
4799
- category: "View",
4800
- id: "view.deleteLayout",
4801
- level: "application",
4802
- menus: ["palette"],
4803
- group: "4_layouts",
4804
- requires: "an open project",
4805
- when: projectOpen,
4806
- run: (_ctx, args) => {
4807
- deleteLayout(stringArg("view.deleteLayout", args, "layout"));
4808
- },
4809
- title: "Delete Layout"
4810
- },
4811
- {
4812
- category: "View",
4813
- id: "view.resetLayout",
4814
- level: "application",
4815
- menus: ["palette"],
4816
- group: "4_layouts",
4817
- requires: "an open project",
4818
- when: projectOpen,
4819
- run: () => {
4820
- resetLayout(deps);
4821
- },
4822
- title: "Reset Layout"
4823
- },
4824
- {
4825
- args: argsSchema({
4826
- color: enumProperty(CHROME_THEMES, "The Studio chrome's theme.")
4827
- }),
4828
- category: "View",
4829
- id: "view.setTheme",
4830
- level: "application",
4831
- menus: ["palette"],
4832
- group: "6_appearance",
4833
- run: (_ctx, args) => {
4834
- setChromeTheme(enumArg("view.setTheme", args, "color", CHROME_THEMES));
4835
- },
4836
- title: "Set Theme"
4837
- }
4838
- ];
4839
- }
4840
- function registerShellViewCommands(registry, deps) {
4841
- registry.registerAll(shellViewCommands(deps));
4842
- }
4843
-
4844
- // src/tabs/project-config.ts
4845
- function serializeProjectConfig(config) {
4846
- return JSON.stringify(config, null, 2);
4847
- }
4848
- var _bound = null;
4849
- var _detached = null;
4850
- var _committed = null;
4851
- var _seeding = null;
4852
- function openConfigTab() {
4853
- const tab = workspace.tabs.get(PROJECT_CONFIG_PATH);
4854
- return tab ? toRaw(tab) : null;
4855
- }
4856
- function unbind() {
4857
- if (_detached) {
4858
- disposeTab(_detached);
4859
- _detached = null;
4860
- }
4861
- _bound = null;
4862
- _committed = null;
4863
- _seeding = null;
4864
- }
4865
- function seedCommitted() {
4866
- _seeding ??= (async () => {
4867
- try {
4868
- const text = await getPlatform().readFile(PROJECT_CONFIG_PATH);
4869
- _committed = serializeProjectConfig(JSON.parse(text));
4870
- } catch {
4871
- _committed = null;
4872
- }
4873
- })();
4874
- return _seeding;
4875
- }
4876
- function syncProjectState(config) {
4877
- const state = requireProjectState();
4878
- if (state.projectConfig === config) {
4879
- return;
4880
- }
4881
- setProjectState({ ...state, projectConfig: config });
4882
- setWorkspaceProject(workspace.projectRoot, config);
4883
- }
4884
- function liveConfig() {
4885
- const config = requireProjectState()?.projectConfig;
4886
- return config ? toRaw(config) : null;
4887
- }
4888
- function replaceContents(target, source) {
4889
- for (const key of Object.keys(target)) {
4890
- if (!Object.hasOwn(source, key)) {
4891
- delete target[key];
4892
- }
4893
- }
4894
- Object.assign(target, source);
4895
- }
4896
- function bind(tab) {
4897
- _bound = tab;
4898
- tab.scope.run(() => {
4899
- effect(() => {
4900
- const doc = tab.doc.document;
4901
- if (_bound !== tab || !requireProjectState()) {
4902
- return;
4903
- }
4904
- syncProjectState(toRaw(doc));
4905
- });
4906
- });
4907
- return tab;
4908
- }
4909
- function projectConfigDocument() {
4910
- const state = requireProjectState();
4911
- const open = openConfigTab();
4912
- if (_bound && (_bound !== (open ?? _detached) || toRaw(_bound.doc.document) !== state.projectConfig)) {
4913
- unbind();
4914
- }
4915
- if (_bound) {
4916
- return _bound;
4917
- }
4918
- if (open) {
4919
- return bind(open);
4920
- }
4921
- _detached = createTab({
4922
- document: state.projectConfig ?? {},
4923
- documentPath: PROJECT_CONFIG_PATH,
4924
- id: PROJECT_CONFIG_PATH
4925
- });
4926
- return bind(_detached);
4927
- }
4928
- function extensionsKey(config) {
4929
- return JSON.stringify(config.extensions ?? null);
4930
- }
4931
- async function refreshExtensionSurfaces() {
4932
- const { loadFormats, refreshExtensionUi, refreshFormats } = await import("./format-host-8fr3545e.js");
4933
- refreshFormats();
4934
- loadFormats();
4935
- refreshExtensionUi(getPlatform());
4936
- }
4937
- async function commitProjectConfig(patch) {
4938
- const live = liveConfig();
4939
- const tab = projectConfigDocument();
4940
- await seedCommitted();
4941
- const current = toRaw(tab.doc.document);
4942
- const currentText = serializeProjectConfig(current);
4943
- const rival = live && live !== current && serializeProjectConfig(live) !== currentText ? live : null;
4944
- if (rival && tab.doc.dirty) {
4945
- const error = new Error(`${PROJECT_CONFIG_PATH} has unsaved changes in its editor tab`);
4946
- notify.error(`Could not save ${PROJECT_CONFIG_PATH} — the file is open with unsaved changes. Save or ` + `revert that tab, then make the settings change again.`, { key: `save:${PROJECT_CONFIG_PATH}`, path: PROJECT_CONFIG_PATH, source: "Settings" });
4947
- return { error, ok: false };
4948
- }
4949
- const base = rival ?? current;
4950
- const next = patch ? { ...base, ...patch } : base;
4951
- const text = serializeProjectConfig(next);
4952
- if (!rival && !tab.doc.dirty && text === _committed) {
4953
- return { ok: true };
4954
- }
4955
- const before = extensionsKey(current);
4956
- transactDoc(tab, (t) => {
4957
- const doc = t.doc.document;
4958
- if (rival) {
4959
- replaceContents(doc, rival);
4960
- }
4961
- for (const [key, value] of Object.entries(patch ?? {})) {
4962
- doc[key] = value;
4963
- }
4964
- });
4965
- const config = toRaw(tab.doc.document);
4966
- const output = serializeProjectConfig(config);
4967
- try {
4968
- await getPlatform().writeFile(PROJECT_CONFIG_PATH, output);
4969
- } catch (error) {
4970
- notify.error(`Could not save ${PROJECT_CONFIG_PATH} — ${errorMessage(error)}`, {
4971
- key: `save:${PROJECT_CONFIG_PATH}`,
4972
- path: PROJECT_CONFIG_PATH,
4973
- source: "Settings"
4974
- });
4975
- return { error, ok: false };
4976
- }
4977
- _committed = output;
4978
- tab.doc.dirty = false;
4979
- if (extensionsKey(config) !== before) {
4980
- await refreshExtensionSurfaces();
4981
- }
4982
- return { ok: true };
4983
- }
4984
- async function adoptProjectConfig(config) {
4985
- if (!requireProjectState()) {
4986
- setWorkspaceProject(workspace.projectRoot, config);
4987
- return;
4988
- }
4989
- const tab = projectConfigDocument();
4990
- transactDoc(tab, (t) => replaceContents(t.doc.document, config), { skipHistory: true });
4991
- tab.doc.dirty = false;
4992
- await seedCommitted();
4993
- _committed = serializeProjectConfig(config);
4994
- }
4995
-
4996
- // src/site-context.ts
4997
- function getEffectiveMedia(docMedia) {
4998
- const siteMedia = projectState?.projectConfig?.$media;
4999
- if (!siteMedia) {
5000
- return docMedia || {};
5001
- }
5002
- if (!docMedia) {
5003
- return { ...siteMedia };
5004
- }
5005
- return { ...siteMedia, ...docMedia };
5006
- }
5007
- function getEffectiveStyle(docStyle) {
5008
- const siteStyle = projectState?.projectConfig?.style;
5009
- if (!siteStyle) {
5010
- return docStyle || {};
5011
- }
5012
- if (!docStyle) {
5013
- return { ...siteStyle };
5014
- }
5015
- const merged = { ...siteStyle };
5016
- for (const [k, v] of Object.entries(docStyle)) {
5017
- merged[k] = typeof v === "object" && v !== null && typeof merged[k] === "object" && merged[k] !== null ? { ...merged[k], ...v } : v;
5018
- }
5019
- return merged;
5020
- }
5021
- function getEffectiveImports(docImports) {
5022
- const siteImports = projectState?.projectConfig?.imports;
5023
- if (!siteImports) {
5024
- return docImports || {};
5025
- }
5026
- if (!docImports) {
5027
- return { ...siteImports };
5028
- }
5029
- return { ...siteImports, ...docImports };
5030
- }
5031
- function getEffectiveElements(docElements) {
5032
- const siteElements = projectState?.projectConfig?.$elements;
5033
- if (!siteElements?.length) {
5034
- return docElements || [];
5035
- }
5036
- if (!docElements?.length) {
5037
- return [...siteElements];
5038
- }
5039
- const seen = new Set;
5040
- const merged = [];
5041
- for (const entry of [...siteElements, ...docElements]) {
5042
- const key = typeof entry === "string" ? entry : entry?.$ref;
5043
- if (key && !seen.has(key)) {
5044
- seen.add(key);
5045
- merged.push(entry);
5046
- }
5047
- }
5048
- return merged;
5049
- }
5050
- function getEffectiveHead(docHead) {
5051
- const siteHead = projectState?.projectConfig?.$head;
5052
- if (!siteHead?.length) {
5053
- return docHead || [];
5054
- }
5055
- if (!docHead?.length) {
5056
- return [...siteHead];
5057
- }
5058
- const seen = new Set;
5059
- const merged = [];
5060
- for (const entry of [...siteHead, ...docHead]) {
5061
- const key = String(entry?.attributes?.href || entry?.attributes?.src || "") || JSON.stringify(entry);
5062
- if (!seen.has(key)) {
5063
- seen.add(key);
5064
- merged.push(entry);
5065
- }
5066
- }
5067
- return merged;
5068
- }
5069
- function getEffectiveLocales() {
5070
- const config = projectState?.projectConfig;
5071
- return config ? resolveI18n(config).i18n : null;
5072
- }
5073
- var layoutCache = new Map;
5074
- function invalidateLayoutCache() {
5075
- layoutCache.clear();
5076
- }
5077
- function getEffectiveLayoutPath(docLayout) {
5078
- if (docLayout === false) {
5079
- return null;
5080
- }
5081
- const defaultLayout = projectState?.projectConfig?.defaults?.layout;
5082
- return docLayout || defaultLayout || null;
5083
- }
5084
- async function resolveLayoutDoc(layoutPath) {
5085
- const normalized = layoutPath.replace(/^\.\//, "");
5086
- if (layoutCache.has(normalized)) {
5087
- return structuredClone(layoutCache.get(normalized));
5088
- }
5089
- try {
5090
- const platform = getPlatform();
5091
- const content = await platform.readFile(normalized);
5092
- const doc = JSON.parse(content);
5093
- layoutCache.set(normalized, doc);
5094
- return structuredClone(doc);
5095
- } catch {
5096
- return null;
5097
- }
5098
- }
5099
- function distributePageIntoLayout(layoutDoc, pageDoc) {
5100
- const pageChildren = pageDoc.children ?? [];
5101
- const children = Array.isArray(pageChildren) ? pageChildren : [];
5102
- const named = new Map;
5103
- const defaults = [];
5104
- for (const child of children) {
5105
- const slotName = typeof child === "object" ? child.attributes?.slot : undefined;
5106
- if (typeof child === "object" && typeof slotName === "string" && slotName) {
5107
- if (!named.has(slotName)) {
5108
- named.set(slotName, []);
5109
- }
5110
- named.get(slotName).push(child);
5111
- } else {
5112
- defaults.push(child);
5113
- }
5114
- }
5115
- fillSlots(layoutDoc, named, defaults);
5116
- if (pageDoc.state) {
5117
- layoutDoc.state = { ...layoutDoc.state, ...pageDoc.state };
5118
- }
5119
- if (pageDoc.$media) {
5120
- layoutDoc.$media = { ...layoutDoc.$media, ...pageDoc.$media };
5121
- }
5122
- if (pageDoc.style) {
5123
- layoutDoc.style = { ...layoutDoc.style, ...pageDoc.style };
5124
- }
5125
- if (pageDoc.attributes) {
5126
- layoutDoc.attributes = { ...layoutDoc.attributes, ...pageDoc.attributes };
5127
- }
5128
- return layoutDoc;
5129
- }
5130
- function fillSlots(node, named, defaults) {
5131
- if (!node || typeof node !== "object") {
5132
- return;
5133
- }
5134
- if (!Array.isArray(node.children)) {
5135
- return;
5136
- }
5137
- const newChildren = [];
5138
- for (const child of node.children) {
5139
- if (child && typeof child === "object" && child.tagName === "slot") {
5140
- const slotName = child.attributes?.name;
5141
- if (typeof slotName === "string" && slotName && named.has(slotName)) {
5142
- newChildren.push(...named.get(slotName));
5143
- named.delete(slotName);
5144
- } else if (!slotName && defaults.length > 0) {
5145
- newChildren.push(...defaults);
5146
- } else if (Array.isArray(child.children)) {
5147
- newChildren.push(...child.children);
5148
- }
5149
- } else {
5150
- if (typeof child !== "string") {
5151
- fillSlots(child, named, defaults);
5152
- }
5153
- newChildren.push(child);
5154
- }
5155
- }
5156
- node.children = newChildren;
5157
- }
5158
- async function updateSiteConfig(patch) {
5159
- const result = await commitProjectConfig(patch);
5160
- if (!result.ok) {
5161
- throw result.error;
5162
- }
5163
- }
5164
-
5165
- export { errorMessage, LANGUAGE_TAG_PATTERN, canonicalizeLocale, isWellFormedLocale, localeDirection, localeLabel, resolveI18n, localeOfPath, servedLocaleOfPath, translationKeyOfPath, translationPathFor, EDITOR_KIND_LABELS, editorKindForMode, canvasViewForMode, inCanvas, hasSelection, hasElementSelection, emptyContext, makeContext, keyScopeStack, view, STAGE_CLASS, nextRenderGeneration, surfaceForPane, allCanvasSurfaces, stageContaining, registerCanvasSurface, createPaneSurface, setSurfaceTeardown, disposePaneSurface, BUFFER_COMMIT, bufferWrites, commitBufferWrites, cancelBufferWrites, bufferIsLive, bufferMovedOn, commitTabBuffers, tabBufferUnsaved, PROJECT_CONFIG_PATH, editorKindOf, editorKindsOf, modeForEditorKind, primarySelection, selectionAnchor, isSelected, cloneSelection, toggleSelected, rangeSelection, isSpliceablePath, structuralBatch, uniquePaths, unifyValues, setPatchConsumer, recordDocOp, transactDoc, transact, beginBatch, endBatch, isBatching, batchTab, setHistoryDelegate, canUndo, canRedo, undo, redo, mutateInsertNode, mutateRemoveNode, mutateDuplicateNode, mutateRemoveNodes, mutateDuplicateNodes, mutateWrapNode, mutateMoveNode, mutateUpdateProperty, mutateUpdateStyle, mutateUpdateAttribute, mutateUpdateMediaStyle, mutateUpdateNestedStyle, mutateUpdateMediaNestedStyle, mutateUpdateNestedStylePath, mutateUpdateMediaNestedStylePath, mutateReplaceStyle, mutateAddDef, mutateRemoveDef, mutateUpdateDef, mutateRenameDef, mutateUpdateProp, mutateAddSwitchCase, mutateRemoveSwitchCase, mutateRenameSwitchCase, mutateUpdateFrontmatter, serializeProjectConfig, commitProjectConfig, adoptProjectConfig, REGION_ATTR, resolveRegion, REGION_FOR_FOCUS, paneRegion, paneStripRegion, navigatorPanelRegion, inspectorTabRegion, NAVIGATOR_STATEMENTS_REGION, inspectorStatementsRegion, bottomPanelRegion, overlayRegion, setActiveRegistry, activeRegistry, initLayers, isModalOpen, showDialog, showConfirmDialog, showSaveDiscardDialog, showPromptDialog, openModal, renderPopover, popoverLayerFor, getLayerSlot, overlayIdleBlockers, getEffectiveMedia, getEffectiveStyle, getEffectiveImports, getEffectiveElements, getEffectiveHead, getEffectiveLocales, invalidateLayoutCache, getEffectiveLayoutPath, resolveLayoutDoc, distributePageIntoLayout, updateSiteConfig, collabState, isCollabPath, stringArg, optionalStringArg, boundedNumberArg, booleanArg, enumArg, pathArg, nullablePathArg, pathListArg, argsSchema, enumProperty, numberProperty, booleanProperty, pathProperty, pathListProperty, stringProperty, restoreSession, camelToKebab, camelToLabel, toCamelCase, kebabToLabel, propLabel, attrLabel, abbreviateValue, isMediaFormat, inferInputType, findContentTypeSchema, friendlyNameToVar, varDisplayName, parseCemType, jsonClone, configureCollabSerializer, configureCollabParser, configureCollabNotifier, collabSourceContext, collabReadOnly, collabSave, flushAllCollab, setCollabEnabled, isCollabEnabled, PRIMARY_PANE, workspace, activeTab, activePane, paneById, paneOfTab, focusPane, isPaneFocused, sidePane, receivingPane, detachTab, insertIntoPane, setWorkspaceProject, isTabActive, tabIsLive, openTab, promoteTab, promoteDirtyPreviewTabs, setTabPinned, moveTab, closeTab, closeAllTabs, replaceAllTabs, activateTab, moveTabToPane, renameTab, registerTabCommands, requireNavigatorPanelId, DEFAULT_INSPECTOR_TAB, isInspectorTabId, BOTTOM_TAB_IDS, DEFAULT_BOTTOM_TAB, isBottomTabId, CHROME_THEMES, DOCK_IDS, DOCK_DEFAULT_SIZES, setPaneSplit, shell, persistDocks, persistedSession, markSessionRestored, flushSession, monacoTheme, registerShellSurface, mountShell, setDockCollapsed, setDockSize, setActivityTab, setBottomTab, setChromeTheme, toggleActivityTab, setLayoutSelection, resetProjectShell, registerShellViewCommands };
3160
+ export { LANGUAGE_TAG_PATTERN, canonicalizeLocale, isWellFormedLocale, localeDirection, localeLabel, resolveI18n, localeOfPath, servedLocaleOfPath, translationKeyOfPath, translationPathFor, EDITOR_KIND_LABELS, editorKindForMode, canvasViewForMode, inCanvas, hasSelection, hasElementSelection, emptyContext, makeContext, keyScopeStack, PROJECT_CONFIG_PATH, createTab, editorKindOf, editorKindsOf, modeForEditorKind, disposeTab, primarySelection, selectionAnchor, isSelected, cloneSelection, toggleSelected, rangeSelection, isSpliceablePath, structuralBatch, uniquePaths, unifyValues, setPatchConsumer, recordDocOp, transactDoc, transact, beginBatch, endBatch, isBatching, batchTab, setHistoryDelegate, canUndo, canRedo, undo, redo, mutateInsertNode, mutateRemoveNode, mutateDuplicateNode, mutateRemoveNodes, mutateDuplicateNodes, mutateWrapNode, mutateMoveNode, mutateUpdateProperty, mutateUpdateStyle, mutateUpdateAttribute, mutateUpdateMediaStyle, mutateUpdateNestedStyle, mutateUpdateMediaNestedStyle, mutateUpdateNestedStylePath, mutateUpdateMediaNestedStylePath, mutateReplaceStyle, mutateAddDef, mutateRemoveDef, mutateUpdateDef, mutateRenameDef, mutateUpdateProp, mutateAddSwitchCase, mutateRemoveSwitchCase, mutateRenameSwitchCase, mutateUpdateFrontmatter, collabState, isCollabPath, stringArg, optionalStringArg, boundedNumberArg, booleanArg, enumArg, pathArg, nullablePathArg, pathListArg, argsSchema, enumProperty, derivedEnumProperty, numberProperty, booleanProperty, pathProperty, pathListProperty, stringProperty, LOCALE_PLACEHOLDER, collectionForDirectory, collectionForFile, camelToKebab, camelToLabel, toCamelCase, kebabToLabel, propLabel, attrLabel, abbreviateValue, isMediaFormat, inferInputType, findContentTypeSchema, friendlyNameToVar, varDisplayName, parseCemType, jsonClone, configureCollabSerializer, configureCollabParser, configureCollabNotifier, collabSourceContext, collabReadOnly, collabSave, flushAllCollab, setCollabEnabled, isCollabEnabled, PRIMARY_PANE, SECONDARY_PANE, MAX_PANES, workspace, activeTab, activePane, paneById, paneOfTab, focusPane, isPaneFocused, focusOtherPane, splitRight, sidePane, receivingPane, closePane, detachTab, insertIntoPane, setWorkspaceProject, isTabActive, tabIsLive, openTab, promoteTab, promoteDirtyPreviewTabs, setTabPinned, moveTab, closeTab, closeAllTabs, takeClosedTabPath, cycleTab, endTabCycle, replaceAllTabs, activateTab, moveTabToPane, renameTab, registerTabCommands, tabCommands, paneCommands };
5166
3161
 
5167
- //# debugId=D6F07440896FC9CC64756E2164756E21
5168
- //# sourceMappingURL=studio-r5xbtctf.js.map
3162
+ //# debugId=FC372971F7D8792C64756E2164756E21
3163
+ //# sourceMappingURL=studio-2zkz8evn.js.map