@jxsuite/studio 0.13.0 → 0.15.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 (38) hide show
  1. package/dist/studio.js +467 -625
  2. package/dist/studio.js.map +37 -37
  3. package/package.json +1 -1
  4. package/src/canvas/canvas-live-render.js +25 -0
  5. package/src/canvas/canvas-render.js +20 -2
  6. package/src/canvas/canvas-utils.js +1 -2
  7. package/src/editor/component-inline-edit.js +1 -3
  8. package/src/editor/content-inline-edit.js +1 -3
  9. package/src/editor/context-menu.js +73 -35
  10. package/src/editor/insertion-helper.js +0 -1
  11. package/src/editor/shortcuts.js +39 -45
  12. package/src/files/file-ops.js +57 -108
  13. package/src/files/files.js +13 -8
  14. package/src/panels/activity-bar.js +6 -4
  15. package/src/panels/canvas-dnd.js +4 -10
  16. package/src/panels/dnd.js +45 -7
  17. package/src/panels/editors.js +14 -16
  18. package/src/panels/elements-panel.js +13 -13
  19. package/src/panels/git-panel.js +2 -1
  20. package/src/panels/layers-panel.js +11 -12
  21. package/src/panels/left-panel.js +2 -2
  22. package/src/panels/panel-events.js +19 -25
  23. package/src/panels/preview-render.js +7 -3
  24. package/src/panels/pseudo-preview.js +9 -8
  25. package/src/panels/statusbar.js +8 -16
  26. package/src/panels/style-inputs.js +2 -3
  27. package/src/panels/style-utils.js +8 -6
  28. package/src/panels/stylebook-layers-panel.js +5 -5
  29. package/src/panels/stylebook-panel.js +16 -16
  30. package/src/panels/toolbar.js +2 -2
  31. package/src/state.js +2 -3
  32. package/src/store.js +2 -133
  33. package/src/studio.js +111 -239
  34. package/src/tabs/tab.js +19 -4
  35. package/src/tabs/transact.js +19 -1
  36. package/src/ui/color-selector.js +4 -5
  37. package/src/view.js +3 -0
  38. package/src/workspace/workspace.js +5 -0
package/dist/studio.js CHANGED
@@ -1435,8 +1435,8 @@ var init_reactivity = __esm(() => {
1435
1435
  // src/tabs/tab.js
1436
1436
  function createDefaultUi() {
1437
1437
  return {
1438
- leftTab: "layers",
1439
1438
  rightTab: "properties",
1439
+ canvasMode: "design",
1440
1440
  zoom: 1,
1441
1441
  activeMedia: null,
1442
1442
  activeSelector: null,
@@ -1460,7 +1460,14 @@ function createDefaultUi() {
1460
1460
  pendingInlineEdit: null
1461
1461
  };
1462
1462
  }
1463
- function createTab({ id, documentPath = null, fileHandle = null, document: document2, frontmatter }) {
1463
+ function createTab({
1464
+ id,
1465
+ documentPath = null,
1466
+ fileHandle = null,
1467
+ document: document2,
1468
+ frontmatter,
1469
+ sourceFormat = null
1470
+ }) {
1464
1471
  const scope = effectScope();
1465
1472
  const tab = scope.run(() => ({
1466
1473
  id,
@@ -1469,8 +1476,9 @@ function createTab({ id, documentPath = null, fileHandle = null, document: docum
1469
1476
  scope,
1470
1477
  doc: reactive({
1471
1478
  document: document2,
1479
+ sourceFormat,
1472
1480
  content: { frontmatter: frontmatter || {} },
1473
- mode: documentPath?.endsWith(".md") ? "content" : "component",
1481
+ mode: sourceFormat === "md" ? "content" : documentPath?.endsWith(".md") ? "content" : "component",
1474
1482
  handlersSource: null,
1475
1483
  dirty: false
1476
1484
  }),
@@ -1497,6 +1505,14 @@ var init_tab = __esm(() => {
1497
1505
  });
1498
1506
 
1499
1507
  // src/workspace/workspace.js
1508
+ var exports_workspace = {};
1509
+ __export(exports_workspace, {
1510
+ workspace: () => workspace,
1511
+ openTab: () => openTab,
1512
+ closeTab: () => closeTab,
1513
+ activeTab: () => activeTab,
1514
+ activateTab: () => activateTab
1515
+ });
1500
1516
  function openTab(opts) {
1501
1517
  const tab = createTab(opts);
1502
1518
  workspace.tabs.set(tab.id, tab);
@@ -1527,6 +1543,8 @@ var init_workspace = __esm(() => {
1527
1543
  projectRoot: null,
1528
1544
  projectConfig: null,
1529
1545
  componentRegistry: [],
1546
+ clipboard: null,
1547
+ styleClipboard: null,
1530
1548
  fileTree: {
1531
1549
  dirs: new Map,
1532
1550
  expanded: new Set,
@@ -1625,124 +1643,9 @@ function nodeLabel(node) {
1625
1643
  }
1626
1644
  return tag + suffix;
1627
1645
  }
1628
- function createState(doc) {
1629
- const initial = { document: doc, selection: null };
1630
- return {
1631
- document: doc,
1632
- selection: null,
1633
- hover: null,
1634
- history: [initial],
1635
- historyIndex: 0,
1636
- dirty: false,
1637
- fileHandle: null,
1638
- documentPath: null,
1639
- documentStack: [],
1640
- handlersSource: null,
1641
- mode: "component",
1642
- content: { frontmatter: {} },
1643
- ui: {
1644
- leftTab: "layers",
1645
- rightTab: "properties",
1646
- zoom: 1,
1647
- activeMedia: null,
1648
- activeSelector: null,
1649
- featureToggles: {},
1650
- styleSections: {},
1651
- inspectorSections: {},
1652
- styleShorthands: {},
1653
- styleFilter: "",
1654
- styleFilterActive: false,
1655
- editingFunction: null,
1656
- stylebookSelection: null,
1657
- stylebookTab: "elements",
1658
- stylebookFilter: "",
1659
- stylebookCustomizedOnly: false,
1660
- settingsTab: "stylebook",
1661
- gitStatus: null,
1662
- gitBranches: null,
1663
- gitCommitMessage: "",
1664
- gitLoading: false,
1665
- gitError: null,
1666
- pendingInlineEdit: null
1667
- },
1668
- canvas: {
1669
- status: "idle",
1670
- scope: null,
1671
- error: null
1672
- }
1673
- };
1674
- }
1675
- function toFlat(doc, session) {
1676
- return { ...doc, ...session };
1677
- }
1678
- function fromFlat(S) {
1679
- const {
1680
- document: document2,
1681
- dirty,
1682
- fileHandle,
1683
- documentPath,
1684
- documentStack,
1685
- handlersSource,
1686
- mode,
1687
- content,
1688
- history,
1689
- historyIndex,
1690
- selection,
1691
- hover,
1692
- ui,
1693
- canvas
1694
- } = S;
1695
- return {
1696
- doc: {
1697
- document: document2,
1698
- dirty,
1699
- fileHandle,
1700
- documentPath,
1701
- documentStack,
1702
- handlersSource,
1703
- mode,
1704
- content,
1705
- history,
1706
- historyIndex
1707
- },
1708
- session: { selection, hover, ui, canvas }
1709
- };
1710
- }
1711
1646
  function setProjectState(ps) {
1712
1647
  projectState = ps;
1713
1648
  }
1714
- function hoverNode(state, path) {
1715
- return { ...state, hover: path };
1716
- }
1717
- function pushDocument(state, doc, documentPath) {
1718
- const frame = {
1719
- document: state.document,
1720
- selection: state.selection,
1721
- fileHandle: state.fileHandle,
1722
- documentPath: state.documentPath,
1723
- dirty: state.dirty,
1724
- history: state.history,
1725
- historyIndex: state.historyIndex,
1726
- mode: state.mode
1727
- };
1728
- const newState = createState(doc);
1729
- newState.documentStack = [...state.documentStack || [], frame];
1730
- newState.documentPath = documentPath;
1731
- newState.ui = { ...state.ui, leftTab: "layers", activeMedia: null, activeSelector: null };
1732
- return newState;
1733
- }
1734
- function popDocument(state) {
1735
- if (!state.documentStack || state.documentStack.length === 0)
1736
- return state;
1737
- const stack = [...state.documentStack];
1738
- const frame = stack.pop();
1739
- return {
1740
- ...state,
1741
- ...frame,
1742
- documentStack: stack,
1743
- ui: { ...state.ui, leftTab: "layers" }
1744
- };
1745
- }
1746
1649
  var projectState = null;
1747
1650
 
1748
1651
  // src/store.js
@@ -1814,36 +1717,6 @@ function renderOnly(...names) {
1814
1717
  }
1815
1718
  }
1816
1719
  }
1817
- function setUpdateFn(fn) {
1818
- _updateFn = fn;
1819
- }
1820
- function setGetStateFn(fn) {
1821
- _getStateFn = fn;
1822
- }
1823
- function getState() {
1824
- const tab = activeTab.value;
1825
- if (tab) {
1826
- return {
1827
- document: tab.doc.document,
1828
- mode: tab.doc.mode,
1829
- dirty: tab.doc.dirty,
1830
- handlersSource: tab.doc.handlersSource,
1831
- content: tab.doc.content,
1832
- documentPath: tab.documentPath,
1833
- fileHandle: tab.fileHandle,
1834
- selection: tab.session.selection,
1835
- hover: tab.session.hover,
1836
- clipboard: tab.session.clipboard,
1837
- ui: tab.session.ui,
1838
- canvas: tab.session.canvas,
1839
- documentStack: tab.session.documentStack
1840
- };
1841
- }
1842
- return _getStateFn();
1843
- }
1844
- function setUpdateSessionFn(fn) {
1845
- _updateSessionFn = fn;
1846
- }
1847
1720
  function updateSession(patch) {
1848
1721
  const tab = activeTab.value;
1849
1722
  if (tab) {
@@ -1865,14 +1738,12 @@ function updateSession(patch) {
1865
1738
  }
1866
1739
  }
1867
1740
  }
1868
- _updateSessionFn(patch);
1869
1741
  }
1870
1742
  function updateUi(field, value) {
1871
1743
  const tab = activeTab.value;
1872
1744
  if (tab) {
1873
1745
  tab.session.ui[field] = value;
1874
1746
  }
1875
- _updateSessionFn({ ui: { [field]: value } });
1876
1747
  }
1877
1748
  function updateCanvas(patch) {
1878
1749
  const tab = activeTab.value;
@@ -1881,27 +1752,8 @@ function updateCanvas(patch) {
1881
1752
  tab.session.canvas[k] = v;
1882
1753
  }
1883
1754
  }
1884
- _updateSessionFn({ canvas: patch });
1885
- }
1886
- function addUpdateMiddleware(fn) {
1887
- _updateMiddleware.push(fn);
1888
1755
  }
1889
- function runUpdateMiddleware(state) {
1890
- for (const mw of _updateMiddleware)
1891
- mw(state);
1892
- }
1893
- function addPostRenderHook(fn) {
1894
- _postRenderHooks.push(fn);
1895
- }
1896
- function runPostRenderHooks(prevDoc, prevSel) {
1897
- for (const hook of _postRenderHooks)
1898
- hook(prevDoc, prevSel);
1899
- }
1900
- var canvasWrap, activityBar, leftPanel, rightPanel, toolbarEl, statusbarEl, elToPath, canvasPanels, VOID_ELEMENTS, COMMON_SELECTORS, _styleDebounceTimers, _renderers, _updateFn = () => {
1901
- throw new Error("update() called before setUpdateFn() — bootstrap not complete");
1902
- }, _getStateFn = () => null, _updateSessionFn = () => {
1903
- throw new Error("updateSession() called before setUpdateSessionFn() — bootstrap not complete");
1904
- }, _updateMiddleware, _postRenderHooks;
1756
+ var canvasWrap, activityBar, leftPanel, rightPanel, toolbarEl, statusbarEl, elToPath, canvasPanels, VOID_ELEMENTS, COMMON_SELECTORS, _styleDebounceTimers, _renderers;
1905
1757
  var init_store = __esm(() => {
1906
1758
  init_workspace();
1907
1759
  canvasWrap = document.querySelector("#canvas-wrap");
@@ -1943,8 +1795,6 @@ var init_store = __esm(() => {
1943
1795
  ];
1944
1796
  _styleDebounceTimers = new Map;
1945
1797
  _renderers = new Map;
1946
- _updateMiddleware = [];
1947
- _postRenderHooks = [];
1948
1798
  });
1949
1799
 
1950
1800
  // ../../node_modules/bind-event-listener/dist/bind.js
@@ -42471,8 +42321,9 @@ init_reactivity();
42471
42321
  var HISTORY_LIMIT = 100;
42472
42322
  function transactDoc(tab, mutationFn, { skipHistory = false } = {}) {
42473
42323
  mutationFn(tab);
42324
+ const raw = toRaw(tab.doc.document);
42325
+ tab.doc.document = { ...raw };
42474
42326
  if (!skipHistory) {
42475
- const raw = toRaw(tab.doc.document);
42476
42327
  const snapshot = {
42477
42328
  document: structuredClone(raw),
42478
42329
  selection: tab.session.selection ? [...tab.session.selection] : null
@@ -42651,6 +42502,14 @@ function mutateUpdateMediaNestedStyle(tab, path, mediaName, selector, prop, valu
42651
42502
  if (Object.keys(node.style).length === 0)
42652
42503
  delete node.style;
42653
42504
  }
42505
+ function mutateReplaceStyle(tab, path, style) {
42506
+ const node = getNodeAtPath(tab.doc.document, path);
42507
+ if (style && Object.keys(style).length > 0) {
42508
+ node.style = style;
42509
+ } else {
42510
+ delete node.style;
42511
+ }
42512
+ }
42654
42513
  function mutateAddDef(tab, name, def2) {
42655
42514
  const doc = tab.doc.document;
42656
42515
  if (!doc.state)
@@ -42770,6 +42629,7 @@ var view = {
42770
42629
  showAddBreakpointForm: false,
42771
42630
  addBreakpointPreview: "",
42772
42631
  layoutSelection: null,
42632
+ leftTab: "layers",
42773
42633
  autosaveTimer: null
42774
42634
  };
42775
42635
  // data/elements-meta.json
@@ -46364,22 +46224,19 @@ function mountStatusbar() {
46364
46224
  tab.doc.document;
46365
46225
  tab.doc.mode;
46366
46226
  tab.session.selection;
46367
- renderStatusbar({
46368
- mode: tab.doc.mode,
46369
- document: tab.doc.document,
46370
- selection: tab.session.selection
46371
- });
46227
+ renderStatusbar();
46372
46228
  });
46373
46229
  });
46374
46230
  }
46375
- function renderStatusbar(S) {
46231
+ function renderStatusbar() {
46232
+ const tab = activeTab.value;
46376
46233
  const parts = [];
46377
- if (S.mode === "content")
46234
+ if (tab?.doc.mode === "content")
46378
46235
  parts.push("Content Mode");
46379
- if (S.selection) {
46380
- const node = getNodeAtPath(S.document, S.selection);
46236
+ if (tab?.session.selection) {
46237
+ const node = getNodeAtPath(tab.doc.document, tab.session.selection);
46381
46238
  parts.push(`Selected: ${nodeLabel(node)}`);
46382
- parts.push(`Path: ${S.selection.join(" > ") || "root"}`);
46239
+ parts.push(`Path: ${tab.session.selection.join(" > ") || "root"}`);
46383
46240
  }
46384
46241
  if (statusMsg)
46385
46242
  parts.push(statusMsg);
@@ -47041,8 +46898,7 @@ function enterComponentInlineEdit(el, path) {
47041
46898
  if (view.componentInlineEdit && view.componentInlineEdit.el === el) {
47042
46899
  return;
47043
46900
  }
47044
- const S = getState();
47045
- const node = getNodeAtPath(S.document, path);
46901
+ const node = getNodeAtPath(activeTab.value?.doc.document, path);
47046
46902
  if (!node)
47047
46903
  return;
47048
46904
  const tc = node.textContent;
@@ -47443,8 +47299,7 @@ function enterInlineEdit(el, path) {
47443
47299
  }
47444
47300
  startEditing(el, path, {
47445
47301
  onCommit(commitPath, children, textContent) {
47446
- const S = getState();
47447
- const node = getNodeAtPath(S.document, commitPath);
47302
+ const node = getNodeAtPath(activeTab.value?.doc.document, commitPath);
47448
47303
  if (children) {
47449
47304
  if (node && JSON.stringify(node.children) === JSON.stringify(children))
47450
47305
  return;
@@ -47968,9 +47823,7 @@ function applyTransform() {
47968
47823
  return;
47969
47824
  const zoom = _ctx4.getZoom();
47970
47825
  view.panzoomWrap.style.transform = `translate(${view.panX}px, ${view.panY}px) scale(${zoom})`;
47971
- const label = document.querySelector(".zoom-indicator-label");
47972
- if (label)
47973
- label.textContent = `${Math.round(zoom * 100)}%`;
47826
+ renderZoomIndicator();
47974
47827
  renderOnly("overlays");
47975
47828
  if (_ctx4.getCanvasMode() === "settings")
47976
47829
  _ctx4.renderStylebookOverlays();
@@ -175185,6 +175038,23 @@ async function renderCanvasLive(gen, doc, canvasEl) {
175185
175038
  } else {
175186
175039
  canvasEl.removeAttribute("data-content-mode");
175187
175040
  }
175041
+ const platform3 = globalThis.__jxPlatform;
175042
+ if (platform3?.resolveAssetUrl && docBase) {
175043
+ const mediaEls = el.querySelectorAll("img[src], video[src], source[src], video[poster]");
175044
+ for (const node of mediaEls) {
175045
+ for (const attr of ["src", "poster"]) {
175046
+ const val = node.getAttribute(attr);
175047
+ if (val && !val.startsWith("data:") && !val.startsWith("blob:") && !val.startsWith("http")) {
175048
+ try {
175049
+ const resolved = new URL(val, docBase).pathname.replace(/^\//, "");
175050
+ const dataUrl = await platform3.resolveAssetUrl(resolved);
175051
+ if (dataUrl)
175052
+ node.setAttribute(attr, dataUrl);
175053
+ } catch {}
175054
+ }
175055
+ }
175056
+ }
175057
+ }
175188
175058
  canvasEl.appendChild(el);
175189
175059
  if (canvasMode === "design" || canvasMode === "edit") {
175190
175060
  requestAnimationFrame(() => {
@@ -175207,6 +175077,7 @@ async function renderCanvasLive(gen, doc, canvasEl) {
175207
175077
 
175208
175078
  // src/panels/preview-render.js
175209
175079
  init_store();
175080
+ init_workspace();
175210
175081
 
175211
175082
  // src/panels/signals-panel.js
175212
175083
  init_workspace();
@@ -176126,7 +175997,7 @@ function renderCanvasNode(node, path, parent, activeBreakpoints, featureToggles)
176126
175997
  if (typeof node.textContent === "string") {
176127
175998
  el.textContent = node.textContent;
176128
175999
  } else if (typeof node.textContent === "object" && node.textContent?.$ref) {
176129
- const resolved = resolveDefaultForCanvas(node.textContent, getState().document.state);
176000
+ const resolved = resolveDefaultForCanvas(node.textContent, activeTab.value?.doc.document?.state);
176130
176001
  el.textContent = resolved;
176131
176002
  el.style.opacity = "0.7";
176132
176003
  el.style.fontStyle = "italic";
@@ -176141,7 +176012,7 @@ function renderCanvasNode(node, path, parent, activeBreakpoints, featureToggles)
176141
176012
  for (const [attr, val] of Object.entries(node.attributes)) {
176142
176013
  try {
176143
176014
  if (typeof val === "object" && val?.$ref) {
176144
- const resolved = resolveDefaultForCanvas(val, getState().document.state);
176015
+ const resolved = resolveDefaultForCanvas(val, activeTab.value?.doc.document?.state);
176145
176016
  el.setAttribute(attr, resolved);
176146
176017
  } else {
176147
176018
  el.setAttribute(attr, val);
@@ -176177,6 +176048,7 @@ function renderCanvasNode(node, path, parent, activeBreakpoints, featureToggles)
176177
176048
 
176178
176049
  // src/panels/canvas-dnd.js
176179
176050
  init_store();
176051
+ init_workspace();
176180
176052
  // ../../node_modules/@babel/runtime/helpers/esm/objectWithoutPropertiesLoose.js
176181
176053
  function _objectWithoutPropertiesLoose(r, e) {
176182
176054
  if (r == null)
@@ -177647,8 +177519,7 @@ registerBlockType( name, settings );`
177647
177519
  var _ctx6 = null;
177648
177520
  function renderStylebookMode(ctx) {
177649
177521
  _ctx6 = ctx;
177650
- const S = getState();
177651
- const settingsTab = S.ui.settingsTab || "stylebook";
177522
+ const settingsTab = activeTab.value?.session.ui.settingsTab || "stylebook";
177652
177523
  const settingsChromeBarTpl = html`
177653
177524
  <div
177654
177525
  class="sb-chrome settings-top-chrome"
@@ -177682,10 +177553,11 @@ function renderStylebookMode(ctx) {
177682
177553
  return;
177683
177554
  }
177684
177555
  view.stylebookElToTag = new WeakMap;
177685
- const rootStyle = getEffectiveStyle(S.document.style);
177686
- const filter = (S.ui.stylebookFilter || "").toLowerCase();
177687
- const customizedOnly = S.ui.stylebookCustomizedOnly;
177688
- const { sizeBreakpoints, baseWidth } = parseMediaEntries(getEffectiveMedia(S.document.$media));
177556
+ const tab = activeTab.value;
177557
+ const rootStyle = getEffectiveStyle(tab?.doc.document?.style);
177558
+ const filter = (tab?.session.ui.stylebookFilter || "").toLowerCase();
177559
+ const customizedOnly = tab?.session.ui.stylebookCustomizedOnly;
177560
+ const { sizeBreakpoints, baseWidth } = parseMediaEntries(getEffectiveMedia(tab?.doc.document?.$media));
177689
177561
  const hasMedia = sizeBreakpoints.length > 0;
177690
177562
  const onTabClick = (t) => {
177691
177563
  updateUi("stylebookTab", t);
@@ -177694,7 +177566,7 @@ function renderStylebookMode(ctx) {
177694
177566
  updateUi("stylebookFilter", e.target.value);
177695
177567
  };
177696
177568
  const onCustomizedToggle = () => {
177697
- updateUi("stylebookCustomizedOnly", !S.ui.stylebookCustomizedOnly);
177569
+ updateUi("stylebookCustomizedOnly", !tab?.session.ui.stylebookCustomizedOnly);
177698
177570
  };
177699
177571
  const chromeBarTpl = html`
177700
177572
  ${settingsChromeBarTpl}
@@ -177704,7 +177576,7 @@ function renderStylebookMode(ctx) {
177704
177576
  >
177705
177577
  <sp-tabs
177706
177578
  size="s"
177707
- selected=${S.ui.stylebookTab || "elements"}
177579
+ selected=${tab?.session.ui.stylebookTab || "elements"}
177708
177580
  @change=${(e) => {
177709
177581
  onTabClick(e.target.selected);
177710
177582
  }}
@@ -177713,16 +177585,16 @@ function renderStylebookMode(ctx) {
177713
177585
  <sp-tab label=${t.charAt(0).toUpperCase() + t.slice(1)} value=${t}></sp-tab>
177714
177586
  `)}
177715
177587
  </sp-tabs>
177716
- ${S.ui.stylebookTab === "elements" ? html`
177588
+ ${tab?.session.ui.stylebookTab === "elements" ? html`
177717
177589
  <input
177718
177590
  class="field-input"
177719
177591
  style="flex:1;max-width:200px;margin-left:8px"
177720
177592
  placeholder="Filter…"
177721
- .value=${S.ui.stylebookFilter}
177593
+ .value=${tab?.session.ui.stylebookFilter}
177722
177594
  @input=${onFilterInput2}
177723
177595
  />
177724
177596
  <button
177725
- class="tb-toggle${S.ui.stylebookCustomizedOnly ? " active" : ""}"
177597
+ class="tb-toggle${tab?.session.ui.stylebookCustomizedOnly ? " active" : ""}"
177726
177598
  style="margin-left:4px"
177727
177599
  @click=${onCustomizedToggle}
177728
177600
  >
@@ -177751,7 +177623,7 @@ function renderStylebookMode(ctx) {
177751
177623
  }
177752
177624
  const renderIntoPanel = (panel, activeBreakpoints) => {
177753
177625
  panel.canvas.classList.add("sb-canvas");
177754
- if (S.ui.stylebookTab === "elements") {
177626
+ if (tab?.session.ui.stylebookTab === "elements") {
177755
177627
  renderStylebookElementsIntoCanvas(panel.canvas, rootStyle, filter, customizedOnly, activeBreakpoints);
177756
177628
  for (const child of panel.canvas.querySelectorAll("*")) {
177757
177629
  child.style.pointerEvents = "none";
@@ -177764,7 +177636,7 @@ function renderStylebookMode(ctx) {
177764
177636
  };
177765
177637
  let panelEntries;
177766
177638
  if (!hasMedia) {
177767
- const effectiveMedia = getEffectiveMedia(S.document.$media);
177639
+ const effectiveMedia = getEffectiveMedia(tab?.doc.document?.$media);
177768
177640
  const hasBaseWidth = effectiveMedia && effectiveMedia["--"];
177769
177641
  const label = hasBaseWidth ? `${mediaDisplayName("--")} (${baseWidth}px)` : null;
177770
177642
  const entry = ctx.canvasPanelTemplate(hasBaseWidth ? "base" : null, label, !hasBaseWidth, hasBaseWidth ? baseWidth : undefined);
@@ -177817,8 +177689,7 @@ function renderStylebookOverlays() {
177817
177689
  return;
177818
177690
  if (canvasPanels.length === 0)
177819
177691
  return;
177820
- const S = getState();
177821
- const selectedTag = S.ui.stylebookSelection;
177692
+ const selectedTag = activeTab.value?.session.ui.stylebookSelection;
177822
177693
  for (const panel of canvasPanels) {
177823
177694
  const hoverTag = panel._lastHoverTag;
177824
177695
  const boxes = [];
@@ -178507,6 +178378,7 @@ function registerLayersDnD() {
178507
178378
  const rowPath = row.dataset.path.split("/").map((s) => /^\d+$/.test(s) ? parseInt(s) : s);
178508
178379
  const rowDepth = parseInt(row.dataset.dndDepth) || 0;
178509
178380
  const isVoid = row.hasAttribute("data-dnd-void");
178381
+ const isExpanded = row.hasAttribute("data-dnd-expanded");
178510
178382
  const cleanup = combine(draggable({
178511
178383
  element: row,
178512
178384
  canDrag({ element: _el, input }) {
@@ -178521,9 +178393,15 @@ function registerLayersDnD() {
178521
178393
  onDragStart() {
178522
178394
  row.classList.add("dragging");
178523
178395
  view.layerDragSourceHeight = row.offsetHeight;
178396
+ if (isExpanded) {
178397
+ hideDescendantRows(row, container);
178398
+ }
178524
178399
  },
178525
178400
  onDrop() {
178526
178401
  row.classList.remove("dragging");
178402
+ if (isExpanded) {
178403
+ renderOnly("leftPanel");
178404
+ }
178527
178405
  }
178528
178406
  }), dropTargetForElements({
178529
178407
  element: row,
@@ -178539,6 +178417,7 @@ function registerLayersDnD() {
178539
178417
  element,
178540
178418
  currentLevel: rowDepth,
178541
178419
  indentPerLevel: 16,
178420
+ mode: isExpanded ? "expanded" : "standard",
178542
178421
  block: isVoid ? ["make-child"] : []
178543
178422
  });
178544
178423
  },
@@ -178568,7 +178447,17 @@ function registerLayersDnD() {
178568
178447
  return;
178569
178448
  const srcData = source.data;
178570
178449
  const targetPath = target.data.path;
178450
+ const srcRow = srcData.type === "tree-node" && source.element;
178451
+ const wasExpanded = srcRow && srcRow.hasAttribute("data-dnd-expanded");
178571
178452
  applyDropInstruction(instruction, srcData, targetPath);
178453
+ if (wasExpanded) {
178454
+ const tab = activeTab.value;
178455
+ const newPath = tab?.session.selection;
178456
+ if (newPath) {
178457
+ const collapsed = view._layersCollapsed || (view._layersCollapsed = new Set);
178458
+ collapsed.add(newPath.join("/"));
178459
+ }
178460
+ }
178572
178461
  }
178573
178462
  });
178574
178463
  view.dndCleanups.push(monitorCleanup);
@@ -178634,6 +178523,15 @@ function registerElementsDnD() {
178634
178523
  });
178635
178524
  });
178636
178525
  }
178526
+ function hideDescendantRows(parentRow, container) {
178527
+ const prefix = parentRow.dataset.path + "/";
178528
+ const rows = container.querySelectorAll(".layers-tree .layer-row");
178529
+ for (const r of rows) {
178530
+ if (r.dataset.path?.startsWith(prefix)) {
178531
+ r.style.display = "none";
178532
+ }
178533
+ }
178534
+ }
178637
178535
  function showLayerDropGap(rowEl, data, container) {
178638
178536
  const instruction = extractInstruction(data);
178639
178537
  if (view._currentDropTargetRow && view._currentDropTargetRow !== rowEl) {
@@ -178674,7 +178572,7 @@ function clearLayerDropGap(container) {
178674
178572
  r.style.transform = "";
178675
178573
  }
178676
178574
  function applyDropInstruction(instruction, srcData, targetPath) {
178677
- const S = getState();
178575
+ const document2 = activeTab.value?.doc.document;
178678
178576
  if (srcData.type === "tree-node") {
178679
178577
  const fromPath = srcData.path;
178680
178578
  const targetParent = parentElementPath(targetPath);
@@ -178687,7 +178585,7 @@ function applyDropInstruction(instruction, srcData, targetPath) {
178687
178585
  transactDoc(activeTab.value, (t) => mutateMoveNode(t, fromPath, targetParent, targetIdx + 1));
178688
178586
  break;
178689
178587
  case "make-child": {
178690
- const target = getNodeAtPath(S.document, targetPath);
178588
+ const target = getNodeAtPath(document2, targetPath);
178691
178589
  const len = target?.children?.length || 0;
178692
178590
  transactDoc(activeTab.value, (t) => mutateMoveNode(t, fromPath, targetPath, len));
178693
178591
  break;
@@ -178704,7 +178602,7 @@ function applyDropInstruction(instruction, srcData, targetPath) {
178704
178602
  transactDoc(activeTab.value, (t) => mutateInsertNode(t, targetParent, targetIdx + 1, structuredClone(srcData.fragment)));
178705
178603
  break;
178706
178604
  case "make-child": {
178707
- const target = getNodeAtPath(S.document, targetPath);
178605
+ const target = getNodeAtPath(document2, targetPath);
178708
178606
  const len = target?.children?.length || 0;
178709
178607
  transactDoc(activeTab.value, (t) => mutateInsertNode(t, targetPath, len, structuredClone(srcData.fragment)));
178710
178608
  break;
@@ -178714,8 +178612,8 @@ function applyDropInstruction(instruction, srcData, targetPath) {
178714
178612
  if (tag3 && tag3.includes("-")) {
178715
178613
  const comp = componentRegistry.find((c) => c.tagName === tag3);
178716
178614
  if (comp) {
178717
- const currentS = getState();
178718
- const elements = currentS.document.$elements || [];
178615
+ const tab = activeTab.value;
178616
+ const elements = tab?.doc.document?.$elements || [];
178719
178617
  if (comp.source === "npm") {
178720
178618
  const specifier = comp.modulePath ? `${comp.package}/${comp.modulePath}` : comp.package;
178721
178619
  const alreadyImported = elements.some((e) => e === specifier || e === comp.package);
@@ -178729,7 +178627,7 @@ function applyDropInstruction(instruction, srcData, targetPath) {
178729
178627
  } else {
178730
178628
  const alreadyImported = elements.some((e) => e.$ref && (e.$ref === `./${comp.path}` || e.$ref.endsWith(comp.path.split("/").pop())));
178731
178629
  if (!alreadyImported) {
178732
- const relPath = computeRelativePath(currentS.documentPath, comp.path);
178630
+ const relPath = computeRelativePath(tab?.documentPath, comp.path);
178733
178631
  transact(activeTab.value, (doc) => {
178734
178632
  if (!doc.$elements)
178735
178633
  doc.$elements = [];
@@ -178773,12 +178671,12 @@ function registerPanelDnD(panel) {
178773
178671
  }
178774
178672
  });
178775
178673
  view.canvasDndCleanups.push(monitorCleanup);
178776
- const S = getState();
178674
+ const document2 = activeTab.value?.doc.document;
178777
178675
  for (const el of allEls) {
178778
178676
  const elPath = elToPath.get(el);
178779
178677
  if (!elPath)
178780
178678
  continue;
178781
- const node = getNodeAtPath(S.document, elPath);
178679
+ const node = getNodeAtPath(document2, elPath);
178782
178680
  const tag3 = (node?.tagName || "div").toLowerCase();
178783
178681
  const hasElementChildren = node?.children?.some((c) => c != null && typeof c === "object");
178784
178682
  const isLeaf2 = VOID_ELEMENTS.has(tag3) || !hasElementChildren;
@@ -178897,42 +178795,46 @@ init_workspace();
178897
178795
  init_store();
178898
178796
  init_workspace();
178899
178797
  init_components();
178900
- var clipboard = null;
178901
- function copyNode(S) {
178902
- if (!S.selection)
178798
+ function copyNode() {
178799
+ const tab = activeTab.value;
178800
+ if (!tab?.session.selection)
178903
178801
  return;
178904
- const node = getNodeAtPath(S.document, S.selection);
178802
+ const node = getNodeAtPath(tab.doc.document, tab.session.selection);
178905
178803
  if (!node)
178906
178804
  return;
178907
- clipboard = structuredClone(node);
178805
+ workspace.clipboard = structuredClone(node);
178908
178806
  statusMessage("Copied");
178909
178807
  }
178910
- function cutNode(S) {
178911
- if (!S.selection || S.selection.length < 2)
178808
+ function cutNode() {
178809
+ const tab = activeTab.value;
178810
+ if (!tab?.session.selection || tab.session.selection.length < 2)
178912
178811
  return;
178913
- const sel = S.selection;
178914
- const node = getNodeAtPath(S.document, sel);
178812
+ const sel = tab.session.selection;
178813
+ const node = getNodeAtPath(tab.doc.document, sel);
178915
178814
  if (!node)
178916
178815
  return;
178917
- clipboard = structuredClone(node);
178918
- transactDoc(activeTab.value, (t) => mutateRemoveNode(t, sel));
178816
+ workspace.clipboard = structuredClone(node);
178817
+ transactDoc(tab, (t) => mutateRemoveNode(t, sel));
178919
178818
  statusMessage("Cut");
178920
178819
  }
178921
- function pasteNode(S) {
178922
- if (!clipboard)
178820
+ function pasteNode() {
178821
+ if (!workspace.clipboard)
178822
+ return;
178823
+ const tab = activeTab.value;
178824
+ if (!tab)
178923
178825
  return;
178924
- const clip = clipboard;
178925
- const pPath = S.selection || [];
178926
- const parent = getNodeAtPath(S.document, pPath);
178826
+ const clip = workspace.clipboard;
178827
+ const pPath = tab.session.selection || [];
178828
+ const parent = getNodeAtPath(tab.doc.document, pPath);
178927
178829
  if (!parent)
178928
178830
  return;
178929
- if (S.selection && S.selection.length >= 2) {
178930
- const pp = parentElementPath(S.selection);
178931
- const idx = childIndex(S.selection);
178932
- transactDoc(activeTab.value, (t) => mutateInsertNode(t, pp, idx + 1, structuredClone(clip)));
178831
+ if (tab.session.selection && tab.session.selection.length >= 2) {
178832
+ const pp = parentElementPath(tab.session.selection);
178833
+ const idx = childIndex(tab.session.selection);
178834
+ transactDoc(tab, (t) => mutateInsertNode(t, pp, idx + 1, structuredClone(clip)));
178933
178835
  } else {
178934
178836
  const idx = parent.children ? parent.children.length : 0;
178935
- transactDoc(activeTab.value, (t) => mutateInsertNode(t, pPath, idx, structuredClone(clip)));
178837
+ transactDoc(tab, (t) => mutateInsertNode(t, pPath, idx, structuredClone(clip)));
178936
178838
  }
178937
178839
  statusMessage("Pasted");
178938
178840
  }
@@ -178946,21 +178848,43 @@ document.addEventListener("click", () => {
178946
178848
  function dismissContextMenu() {
178947
178849
  ctxMenu.removeAttribute("open");
178948
178850
  }
178949
- function showContextMenu(e, path, S, opts = {}) {
178851
+ function showContextMenu(e, path, opts = {}) {
178950
178852
  e.preventDefault();
178951
178853
  ctxMenu.removeAttribute("open");
178952
- const node = getNodeAtPath(S.document, path);
178854
+ const tab = activeTab.value;
178855
+ const node = getNodeAtPath(tab?.doc.document, path);
178953
178856
  if (!node)
178954
178857
  return;
178955
- activeTab.value.session.selection = path;
178858
+ tab.session.selection = path;
178956
178859
  const items = [];
178957
- items.push({ label: "Copy", action: () => copyNode(S) });
178860
+ items.push({ label: "Copy", action: () => copyNode() });
178958
178861
  if (path.length >= 2) {
178959
- items.push({ label: "Cut", action: () => cutNode(S) });
178862
+ items.push({ label: "Cut", action: () => cutNode() });
178960
178863
  items.push({
178961
178864
  label: "Duplicate",
178962
178865
  action: () => transactDoc(activeTab.value, (t) => mutateDuplicateNode(t, path))
178963
178866
  });
178867
+ if (node.style) {
178868
+ items.push({
178869
+ label: "Copy styles",
178870
+ action: () => {
178871
+ workspace.styleClipboard = JSON.parse(JSON.stringify(node.style));
178872
+ statusMessage("Styles copied");
178873
+ }
178874
+ });
178875
+ }
178876
+ if (workspace.styleClipboard) {
178877
+ items.push({
178878
+ label: "Paste styles",
178879
+ action: () => {
178880
+ if (!workspace.styleClipboard)
178881
+ return;
178882
+ const style = JSON.parse(JSON.stringify(workspace.styleClipboard));
178883
+ transactDoc(activeTab.value, (t) => mutateReplaceStyle(t, path, style));
178884
+ statusMessage("Styles pasted");
178885
+ }
178886
+ });
178887
+ }
178964
178888
  items.push({ label: "—" });
178965
178889
  items.push({
178966
178890
  label: "Insert before",
@@ -179004,8 +178928,8 @@ function showContextMenu(e, path, S, opts = {}) {
179004
178928
  danger: true
179005
178929
  });
179006
178930
  }
179007
- if (clipboard) {
179008
- const clip = clipboard;
178931
+ if (workspace.clipboard) {
178932
+ const clip = workspace.clipboard;
179009
178933
  items.push({ label: "—" });
179010
178934
  items.push({
179011
178935
  label: "Paste inside",
@@ -179253,7 +179177,7 @@ function registerPanelEvents(panel) {
179253
179177
  if (isEditing()) {
179254
179178
  stopEditing();
179255
179179
  }
179256
- const S = _ctx8.getState();
179180
+ const tab = activeTab.value;
179257
179181
  const canvasMode = _ctx8.getCanvasMode();
179258
179182
  const elements = withPanelPointerEvents(() => document.elementsFromPoint(e.clientX, e.clientY));
179259
179183
  for (const el of elements) {
@@ -179267,16 +179191,15 @@ function registerPanelEvents(panel) {
179267
179191
  view.layoutSelection = null;
179268
179192
  const originalPath = elToPath.get(el);
179269
179193
  if (originalPath) {
179270
- let path = bubbleInlinePath(S.document, originalPath);
179194
+ let path = bubbleInlinePath(tab?.doc.document, originalPath);
179271
179195
  const newMedia = mediaName === "base" ? null : mediaName ?? null;
179272
- const withMedia = { ...S, ui: { ...S.ui, activeMedia: newMedia } };
179273
179196
  const resolvedEl = path === originalPath ? el : findCanvasElement(path, canvas) || el;
179274
- if (pathsEqual(path, S.selection) && isEditableBlock(resolvedEl) && (canvasMode === "edit" || S.mode === "content")) {
179275
- _ctx8.setState(withMedia);
179197
+ if (pathsEqual(path, tab?.session.selection) && isEditableBlock(resolvedEl) && (canvasMode === "edit" || tab?.doc.mode === "content")) {
179198
+ activeTab.value.session.ui.activeMedia = newMedia;
179276
179199
  _ctx8.enterInlineEdit(resolvedEl, path);
179277
179200
  return;
179278
179201
  }
179279
- if (canvasMode === "design" && S.mode !== "content") {
179202
+ if (canvasMode === "design" && tab?.doc.mode !== "content") {
179280
179203
  updateUi("pendingInlineEdit", { path, mediaName });
179281
179204
  activeTab.value.session.ui.activeMedia = newMedia;
179282
179205
  activeTab.value.session.selection = path;
@@ -179300,13 +179223,13 @@ function registerPanelEvents(panel) {
179300
179223
  const canvasMode = _ctx8.getCanvasMode();
179301
179224
  if (canvasMode !== "edit" && canvasMode !== "design")
179302
179225
  return;
179303
- const S = _ctx8.getState();
179226
+ const tab = activeTab.value;
179304
179227
  const elements = withPanelPointerEvents(() => document.elementsFromPoint(e.clientX, e.clientY));
179305
179228
  for (const el of elements) {
179306
179229
  if (canvas.contains(el) && el !== canvas) {
179307
179230
  const originalPath = elToPath.get(el);
179308
179231
  if (originalPath) {
179309
- const path = bubbleInlinePath(S.document, originalPath);
179232
+ const path = bubbleInlinePath(tab?.doc.document, originalPath);
179310
179233
  const resolvedEl = path === originalPath ? el : findCanvasElement(path, canvas) || el;
179311
179234
  if (isEditableBlock(resolvedEl)) {
179312
179235
  const newMedia = mediaName === "base" ? null : mediaName ?? null;
@@ -179326,14 +179249,14 @@ function registerPanelEvents(panel) {
179326
179249
  if (e.clientX >= r.left && e.clientX <= r.right && e.clientY >= r.top && e.clientY <= r.bottom)
179327
179250
  return;
179328
179251
  }
179329
- const S = _ctx8.getState();
179252
+ const tab = activeTab.value;
179330
179253
  const elements = withPanelPointerEvents(() => document.elementsFromPoint(e.clientX, e.clientY));
179331
179254
  for (const el of elements) {
179332
179255
  if (canvas.contains(el) && el !== canvas) {
179333
179256
  let path = elToPath.get(el);
179334
179257
  if (path) {
179335
- path = bubbleInlinePath(S.document, path);
179336
- showContextMenu(e, path, S, { onEditComponent: _ctx8.navigateToComponent });
179258
+ path = bubbleInlinePath(tab?.doc.document, path);
179259
+ showContextMenu(e, path, { onEditComponent: _ctx8.navigateToComponent });
179337
179260
  return;
179338
179261
  }
179339
179262
  }
@@ -179347,31 +179270,29 @@ function registerPanelEvents(panel) {
179347
179270
  if (e.clientX >= r.left && e.clientX <= r.right && e.clientY >= r.top && e.clientY <= r.bottom)
179348
179271
  return;
179349
179272
  }
179350
- let S = _ctx8.getState();
179273
+ const tab = activeTab.value;
179351
179274
  const el = withPanelPointerEvents(() => document.elementFromPoint(e.clientX, e.clientY));
179352
179275
  if (el && canvas.contains(el) && el !== canvas) {
179353
179276
  let path = elToPath.get(el);
179354
179277
  if (path) {
179355
- path = bubbleInlinePath(S.document, path);
179356
- if (!pathsEqual(path, S.hover)) {
179357
- _ctx8.setState(hoverNode(S, path));
179278
+ path = bubbleInlinePath(tab?.doc.document, path);
179279
+ if (!pathsEqual(path, tab?.session.hover)) {
179280
+ activeTab.value.session.hover = path;
179358
179281
  renderOnly("overlays");
179359
179282
  }
179360
179283
  }
179361
- } else if (S.hover) {
179362
- _ctx8.setState(hoverNode(S, null));
179284
+ } else if (tab?.session.hover) {
179285
+ activeTab.value.session.hover = null;
179363
179286
  renderOnly("overlays");
179364
179287
  }
179365
179288
  }, opts);
179366
179289
  overlayClk.addEventListener("mouseleave", () => {
179367
- const S = _ctx8.getState();
179368
- if (S.hover) {
179369
- _ctx8.setState(hoverNode(S, null));
179290
+ if (activeTab.value?.session.hover) {
179291
+ activeTab.value.session.hover = null;
179370
179292
  renderOnly("overlays");
179371
179293
  }
179372
179294
  }, opts);
179373
179295
  mount2({
179374
- getState: _ctx8.getState,
179375
179296
  getCanvasMode: _ctx8.getCanvasMode,
179376
179297
  withPanelPointerEvents,
179377
179298
  effectiveZoom,
@@ -179482,6 +179403,7 @@ function computeDocumentDiff(originalDoc, currentDoc) {
179482
179403
 
179483
179404
  // src/panels/pseudo-preview.js
179484
179405
  init_store();
179406
+ init_workspace();
179485
179407
  var pseudoStyleHost = document.createElement("div");
179486
179408
  pseudoStyleHost.style.display = "contents";
179487
179409
  (document.querySelector("sp-theme") || document.body).appendChild(pseudoStyleHost);
@@ -179494,21 +179416,21 @@ function updateForcedPseudoPreview() {
179494
179416
  view.forcedAttrEl.removeAttribute("data-studio-forced");
179495
179417
  view.forcedAttrEl = null;
179496
179418
  }
179497
- const S = getState();
179498
- const sel = S.ui?.activeSelector;
179499
- if (!sel || !sel.startsWith(":") || !S.selection)
179419
+ const tab = activeTab.value;
179420
+ const sel = tab?.session.ui?.activeSelector;
179421
+ if (!sel || !sel.startsWith(":") || !tab?.session.selection)
179500
179422
  return;
179501
179423
  const panel = getActivePanel();
179502
179424
  if (!panel)
179503
179425
  return;
179504
- const el = findCanvasElement(S.selection, panel.canvas);
179426
+ const el = findCanvasElement(tab.session.selection, panel.canvas);
179505
179427
  if (!el)
179506
179428
  return;
179507
- const node = getNodeAtPath(S.document, S.selection);
179429
+ const node = getNodeAtPath(tab.doc.document, tab.session.selection);
179508
179430
  if (!node?.style)
179509
179431
  return;
179510
- const activeTab2 = S.ui.activeMedia;
179511
- const ctx = activeTab2 ? node.style[`@${activeTab2}`] || {} : node.style;
179432
+ const activeMedia = tab.session.ui.activeMedia;
179433
+ const ctx = activeMedia ? node.style[`@${activeMedia}`] || {} : node.style;
179512
179434
  const rules = ctx[sel];
179513
179435
  if (!rules || typeof rules !== "object")
179514
179436
  return;
@@ -180013,18 +179935,17 @@ function invalidateBrowseCache() {
180013
179935
  init_store();
180014
179936
  init_workspace();
180015
179937
  function getFunctionBody(editing) {
180016
- const S = getState();
179938
+ const document2 = activeTab.value?.doc.document;
180017
179939
  if (editing.type === "def") {
180018
- return S.document.state?.[editing.defName]?.body || "";
179940
+ return document2?.state?.[editing.defName]?.body || "";
180019
179941
  } else if (editing.type === "event") {
180020
- const node = getNodeAtPath(S.document, editing.path);
179942
+ const node = getNodeAtPath(document2, editing.path);
180021
179943
  return node?.[editing.eventKey]?.body || "";
180022
179944
  }
180023
179945
  return "";
180024
179946
  }
180025
179947
  function renderFunctionEditor() {
180026
- const S = getState();
180027
- const editing = S.ui.editingFunction;
179948
+ const editing = activeTab.value?.session.ui.editingFunction;
180028
179949
  if (view.functionEditor && view.functionEditor._editingTarget === JSON.stringify(editing)) {
180029
179950
  const body2 = getFunctionBody(editing);
180030
179951
  const currentVal = view.functionEditor.getValue();
@@ -180061,7 +179982,7 @@ function renderFunctionEditor() {
180061
179982
  })}
180062
179983
  ></div>`, canvasWrap);
180063
179984
  const body = getFunctionBody(editing);
180064
- const args = getFunctionArgs(editing, S);
179985
+ const args = getFunctionArgs(editing, activeTab.value?.doc.document);
180065
179986
  view.functionEditor = editor.create(editorContainer, {
180066
179987
  value: body,
180067
179988
  language: "javascript",
@@ -180097,13 +180018,13 @@ function renderFunctionEditor() {
180097
180018
  clearTimeout(syncDebounce);
180098
180019
  syncDebounce = setTimeout(() => {
180099
180020
  const newBody = view.functionEditor.getValue();
180100
- if (editing.type === "def") {
180101
- transactDoc(activeTab.value, (t) => mutateUpdateDef(t, editing.defName, { body: newBody }));
180102
- } else if (editing.type === "event") {
180103
- const S2 = getState();
180104
- const node = getNodeAtPath(S2.document, editing.path);
180105
- const current = node?.[editing.eventKey] || {};
180106
- transactDoc(activeTab.value, (t) => mutateUpdateProperty(t, editing.path, editing.eventKey, {
180021
+ const ed = editing;
180022
+ if (ed.type === "def") {
180023
+ transactDoc(activeTab.value, (t) => mutateUpdateDef(t, ed.defName, { body: newBody }));
180024
+ } else if (ed.type === "event") {
180025
+ const node = getNodeAtPath(activeTab.value?.doc.document, ed.path);
180026
+ const current = node?.[ed.eventKey] || {};
180027
+ transactDoc(activeTab.value, (t) => mutateUpdateProperty(t, ed.path, ed.eventKey, {
180107
180028
  ...current,
180108
180029
  $prototype: "Function",
180109
180030
  body: newBody
@@ -180131,8 +180052,7 @@ function registerFunctionCompletions() {
180131
180052
  languages.registerCompletionItemProvider("javascript", {
180132
180053
  triggerCharacters: ["."],
180133
180054
  provideCompletionItems(model, position) {
180134
- const S = getState();
180135
- const defs = S?.document?.state || {};
180055
+ const defs = activeTab.value?.doc.document?.state || {};
180136
180056
  const word = model.getWordUntilPosition(position);
180137
180057
  const range2 = {
180138
180058
  startLineNumber: position.lineNumber,
@@ -180573,8 +180493,9 @@ function renderCanvasIntoPanel(panel, activeBreakpoints, featureToggles, docOver
180573
180493
  statusMessage("Runtime render OK", 1500);
180574
180494
  if (gitDiffState && docOverride) {
180575
180495
  const isOriginal = docOverride === (gitDiffState.originalDoc || gitDiffState.original);
180576
- const origDoc = isOriginal ? docOverride : gitDiffState.currentDoc || tab?.doc.document;
180577
- const currDoc = isOriginal ? gitDiffState.currentDoc || tab?.doc.document : docOverride;
180496
+ const _tab = activeTab.value;
180497
+ const origDoc = isOriginal ? docOverride : gitDiffState.currentDoc || _tab?.doc.document;
180498
+ const currDoc = isOriginal ? gitDiffState.currentDoc || _tab?.doc.document : docOverride;
180578
180499
  const { byPath: diffMap } = computeDocumentDiff(origDoc, currDoc);
180579
180500
  const { elToPath: elToPath2 } = scope;
180580
180501
  if (elToPath2 instanceof WeakMap) {
@@ -180589,6 +180510,17 @@ function renderCanvasIntoPanel(panel, activeBreakpoints, featureToggles, docOver
180589
180510
  registerPanelEvents(panel);
180590
180511
  renderOverlays();
180591
180512
  updateForcedPseudoPreview();
180513
+ const currentTab = activeTab.value;
180514
+ if (currentTab?.session.ui?.pendingInlineEdit) {
180515
+ const { path, mediaName: mn } = currentTab.session.ui.pendingInlineEdit;
180516
+ currentTab.session.ui.pendingInlineEdit = null;
180517
+ const targetPanel = canvasPanels.find((p) => p.mediaName === mn) || canvasPanels[0];
180518
+ if (targetPanel) {
180519
+ const el = findCanvasElement(path, targetPanel.canvas);
180520
+ if (el)
180521
+ enterComponentInlineEdit(el, path);
180522
+ }
180523
+ }
180592
180524
  });
180593
180525
  }
180594
180526
  function applyDiffHighlightToCanvas(canvas, diffMap) {
@@ -184209,9 +184141,9 @@ function collapsePropsToAttrMap(propsObj) {
184209
184141
  }
184210
184142
 
184211
184143
  // src/files/file-ops.js
184212
- init_store();
184213
184144
  init_platform();
184214
- async function openFile({ S: _S, commit, renderToolbar: _renderToolbar }) {
184145
+ init_workspace();
184146
+ async function openFile() {
184215
184147
  try {
184216
184148
  if ("showOpenFilePicker" in window) {
184217
184149
  const [handle2] = await window.showOpenFilePicker({
@@ -184222,17 +184154,20 @@ async function openFile({ S: _S, commit, renderToolbar: _renderToolbar }) {
184222
184154
  });
184223
184155
  const file = await handle2.getFile();
184224
184156
  const text6 = await file.text();
184157
+ const documentPath = await locateDocument(handle2.name);
184225
184158
  if (handle2.name.endsWith(".md")) {
184226
- const newState = await loadMarkdown(text6, handle2);
184227
- commit(newState);
184159
+ const { document: document4, frontmatter: frontmatter2 } = await loadMarkdown(text6);
184160
+ openTab({
184161
+ id: handle2.name,
184162
+ documentPath,
184163
+ fileHandle: handle2,
184164
+ document: document4,
184165
+ frontmatter: frontmatter2,
184166
+ sourceFormat: "md"
184167
+ });
184228
184168
  } else {
184229
- const doc = JSON.parse(text6);
184230
- const newState = createState(doc);
184231
- newState.fileHandle = handle2;
184232
- newState.dirty = false;
184233
- newState.documentPath = await locateDocument(handle2.name);
184234
- await loadCompanionJS(handle2, newState);
184235
- commit(newState);
184169
+ const document4 = JSON.parse(text6);
184170
+ openTab({ id: handle2.name, documentPath, fileHandle: handle2, document: document4 });
184236
184171
  }
184237
184172
  statusMessage(`Opened ${handle2.name}`);
184238
184173
  } else {
@@ -184245,13 +184180,11 @@ async function openFile({ S: _S, commit, renderToolbar: _renderToolbar }) {
184245
184180
  return;
184246
184181
  const text6 = await file.text();
184247
184182
  if (file.name.endsWith(".md")) {
184248
- const newState = await loadMarkdown(text6, null);
184249
- commit(newState);
184183
+ const { document: document4, frontmatter: frontmatter2 } = await loadMarkdown(text6);
184184
+ openTab({ id: file.name, document: document4, frontmatter: frontmatter2, sourceFormat: "md" });
184250
184185
  } else {
184251
- const doc = JSON.parse(text6);
184252
- const newState = createState(doc);
184253
- newState.dirty = false;
184254
- commit(newState);
184186
+ const document4 = JSON.parse(text6);
184187
+ openTab({ id: file.name, document: document4 });
184255
184188
  }
184256
184189
  statusMessage(`Opened ${file.name}`);
184257
184190
  };
@@ -184262,59 +184195,37 @@ async function openFile({ S: _S, commit, renderToolbar: _renderToolbar }) {
184262
184195
  statusMessage(`Error: ${e.message}`);
184263
184196
  }
184264
184197
  }
184265
- async function loadMarkdown(source, fileHandle) {
184198
+ async function loadMarkdown(source) {
184266
184199
  const { transpileJxMarkdown: transpileJxMarkdown2 } = await Promise.resolve().then(() => (init_transpile(), exports_transpile));
184267
184200
  const doc = transpileJxMarkdown2(source);
184268
184201
  const isComponent = doc.tagName && String(doc.tagName).includes("-");
184269
184202
  if (isComponent) {
184270
- const newState2 = createState(doc);
184271
- newState2.sourceFormat = "md";
184272
- newState2.rawMarkdown = source;
184273
- newState2.fileHandle = fileHandle;
184274
- newState2.dirty = false;
184275
- return newState2;
184203
+ return { document: doc, frontmatter: {} };
184276
184204
  }
184277
- const contentDoc = {
184278
- children: doc.children ?? []
184279
- };
184205
+ const contentDoc = { children: doc.children ?? [] };
184280
184206
  const frontmatter2 = {};
184281
184207
  for (const [key, value2] of Object.entries(doc)) {
184282
184208
  if (key !== "children")
184283
184209
  frontmatter2[key] = value2;
184284
184210
  }
184285
- const newState = createState(contentDoc);
184286
- newState.sourceFormat = "md";
184287
- newState.mode = "content";
184288
- newState.content = { frontmatter: frontmatter2 };
184289
- newState.rawMarkdown = source;
184290
- newState.fileHandle = fileHandle;
184291
- newState.dirty = false;
184292
- return newState;
184211
+ return { document: contentDoc, frontmatter: frontmatter2 };
184293
184212
  }
184294
- async function loadCompanionJS(handle2, state) {
184295
- try {
184296
- if (handle2.getParent) {}
184297
- if (state.document.$handlers) {
184298
- state.handlersSource = `// Companion file: ${state.document.$handlers}
184299
- // (Read-only in builder — edit the JS file directly)`;
184300
- }
184301
- } catch {}
184302
- }
184303
- async function saveFile({ S, commit, renderToolbar }) {
184213
+ async function saveFile() {
184214
+ const tab = activeTab.value;
184215
+ if (!tab)
184216
+ return;
184304
184217
  try {
184305
- const output = serializeDocument(S);
184306
- if (S.documentPath) {
184218
+ const output = serializeDocument(tab);
184219
+ if (tab.documentPath) {
184307
184220
  const platform3 = getPlatform();
184308
- await platform3.writeFile(S.documentPath, output);
184309
- commit({ ...S, dirty: false });
184310
- renderToolbar();
184221
+ await platform3.writeFile(tab.documentPath, output);
184222
+ tab.doc.dirty = false;
184311
184223
  statusMessage("Saved");
184312
- } else if (S.fileHandle && "createWritable" in S.fileHandle) {
184313
- const writable = await S.fileHandle.createWritable();
184224
+ } else if (tab.fileHandle && "createWritable" in tab.fileHandle) {
184225
+ const writable = await tab.fileHandle.createWritable();
184314
184226
  await writable.write(output);
184315
184227
  await writable.close();
184316
- commit({ ...S, dirty: false });
184317
- renderToolbar();
184228
+ tab.doc.dirty = false;
184318
184229
  statusMessage("Saved");
184319
184230
  } else {
184320
184231
  statusMessage("No save target — use Export");
@@ -184324,15 +184235,18 @@ async function saveFile({ S, commit, renderToolbar }) {
184324
184235
  statusMessage(`Save error: ${e.message}`);
184325
184236
  }
184326
184237
  }
184327
- async function exportFile({ S, commit, renderToolbar }) {
184238
+ async function exportFile() {
184239
+ const tab = activeTab.value;
184240
+ if (!tab)
184241
+ return;
184328
184242
  try {
184329
- const isContent = S.mode === "content";
184330
- const output = serializeDocument(S);
184243
+ const isContent = tab.doc.mode === "content";
184244
+ const output = serializeDocument(tab);
184331
184245
  const mimeType = isContent ? "text/markdown" : "application/json";
184332
184246
  const ext = isContent ? ".md" : ".json";
184333
184247
  const description = isContent ? "Markdown Content" : "Jx Component";
184334
184248
  if ("showSaveFilePicker" in window) {
184335
- const suggestedName = S.documentPath ? S.documentPath.split("/").pop() : isContent ? "content.md" : "component.json";
184249
+ const suggestedName = tab.documentPath ? tab.documentPath.split("/").pop() : isContent ? "content.md" : "component.json";
184336
184250
  const handle2 = await window.showSaveFilePicker({
184337
184251
  suggestedName,
184338
184252
  types: [{ description, accept: { [mimeType]: [ext] } }]
@@ -184340,8 +184254,7 @@ async function exportFile({ S, commit, renderToolbar }) {
184340
184254
  const writable = await handle2.createWritable();
184341
184255
  await writable.write(output);
184342
184256
  await writable.close();
184343
- commit({ ...S, dirty: false });
184344
- renderToolbar();
184257
+ tab.doc.dirty = false;
184345
184258
  statusMessage(`Exported as ${handle2.name}`);
184346
184259
  } else {
184347
184260
  const blob = new Blob([output], { type: mimeType });
@@ -184351,8 +184264,7 @@ async function exportFile({ S, commit, renderToolbar }) {
184351
184264
  a.download = isContent ? "content.md" : "component.json";
184352
184265
  a.click();
184353
184266
  URL.revokeObjectURL(url);
184354
- commit({ ...S, dirty: false });
184355
- renderToolbar();
184267
+ tab.doc.dirty = false;
184356
184268
  statusMessage("Downloaded");
184357
184269
  }
184358
184270
  } catch (e) {
@@ -184360,14 +184272,14 @@ async function exportFile({ S, commit, renderToolbar }) {
184360
184272
  statusMessage(`Export error: ${e.message}`);
184361
184273
  }
184362
184274
  }
184363
- function serializeDocument(S) {
184364
- if (S.sourceFormat === "md") {
184365
- return jxDocToMd(S.document);
184275
+ function serializeDocument(tab) {
184276
+ if (tab.doc.sourceFormat === "md") {
184277
+ return jxDocToMd(tab.doc.document);
184366
184278
  }
184367
- if (S.mode === "content") {
184368
- const mdast = jxToMd(S.document);
184279
+ if (tab.doc.mode === "content") {
184280
+ const mdast = jxToMd(tab.doc.document);
184369
184281
  const md = unified().use(remarkDirective).use(remarkStringify, { bullet: "-", emphasis: "*", strong: "*" }).stringify(mdast);
184370
- const fm = S.content?.frontmatter;
184282
+ const fm = tab.doc.content?.frontmatter;
184371
184283
  const hasFrontmatter = fm && Object.keys(fm).length > 0;
184372
184284
  return hasFrontmatter ? `---
184373
184285
  ${stringify4(fm).trim()}
@@ -184375,7 +184287,7 @@ ${stringify4(fm).trim()}
184375
184287
 
184376
184288
  ${md}` : md;
184377
184289
  }
184378
- return JSON.stringify(S.document, null, 2);
184290
+ return JSON.stringify(tab.doc.document, null, 2);
184379
184291
  }
184380
184292
 
184381
184293
  // src/files/files.js
@@ -184427,7 +184339,7 @@ async function loadProject() {
184427
184339
  }
184428
184340
  } catch {}
184429
184341
  }
184430
- async function openProject({ S, commit, renderActivityBar, renderLeftPanel }) {
184342
+ async function openProject({ renderActivityBar, renderLeftPanel }) {
184431
184343
  try {
184432
184344
  const platform3 = getPlatform();
184433
184345
  const result = await platform3.openProject();
@@ -184466,7 +184378,7 @@ async function openProject({ S, commit, renderActivityBar, renderLeftPanel }) {
184466
184378
  }
184467
184379
  }
184468
184380
  projectState.projectDirs = foundDirs;
184469
- commit({ ...S, ui: { ...S.ui, leftTab: "files" } });
184381
+ view.leftTab = "files";
184470
184382
  renderActivityBar();
184471
184383
  renderLeftPanel();
184472
184384
  statusMessage(`Opened project: ${projectState.name}`);
@@ -184822,14 +184734,20 @@ async function openFileInTab(path2) {
184822
184734
  return;
184823
184735
  let document4, frontmatter2;
184824
184736
  if (path2.endsWith(".md")) {
184825
- const state = await loadMarkdown(content3, null);
184826
- document4 = state.document;
184827
- frontmatter2 = state.content?.frontmatter;
184737
+ const result = await loadMarkdown(content3);
184738
+ document4 = result.document;
184739
+ frontmatter2 = result.frontmatter;
184828
184740
  } else {
184829
184741
  document4 = JSON.parse(content3);
184830
184742
  }
184831
184743
  const id = path2;
184832
- openTab({ id, documentPath: path2, document: document4, frontmatter: frontmatter2 });
184744
+ openTab({
184745
+ id,
184746
+ documentPath: path2,
184747
+ document: document4,
184748
+ frontmatter: frontmatter2,
184749
+ sourceFormat: path2.endsWith(".md") ? "md" : null
184750
+ });
184833
184751
  projectState.selectedPath = path2;
184834
184752
  statusMessage(`Opened ${path2.split("/").pop()}`);
184835
184753
  } catch (e) {
@@ -187706,7 +187624,7 @@ function renderGitPanel(S, ctx) {
187706
187624
  }
187707
187625
  if (!_pollTimer) {
187708
187626
  _pollTimer = setInterval(() => {
187709
- if (S.ui.leftTab === "git" && !S.ui.gitLoading)
187627
+ if (view.leftTab === "git" && !S.ui.gitLoading)
187710
187628
  refreshGitStatus();
187711
187629
  }, 30000);
187712
187630
  }
@@ -210839,9 +210757,9 @@ class JxValueSelector extends LitElement {
210839
210757
  // src/ui/color-selector.js
210840
210758
  init_lit();
210841
210759
  init_store();
210760
+ init_workspace();
210842
210761
  function getColorVars() {
210843
- const S = getState();
210844
- const style2 = getEffectiveStyle(S?.document?.style);
210762
+ const style2 = getEffectiveStyle(activeTab.value?.doc.document?.style);
210845
210763
  if (!style2)
210846
210764
  return [];
210847
210765
  const vars = [];
@@ -210860,8 +210778,7 @@ function resolveColorForDisplay(val) {
210860
210778
  return "transparent";
210861
210779
  const m3 = val.match(/^var\((--[^)]+)\)$/);
210862
210780
  if (m3) {
210863
- const S = getState();
210864
- const style2 = getEffectiveStyle(S?.document?.style);
210781
+ const style2 = getEffectiveStyle(activeTab.value?.doc.document?.style);
210865
210782
  const resolved = style2?.[m3[1]];
210866
210783
  if (typeof resolved === "string")
210867
210784
  return resolved;
@@ -211262,7 +211179,7 @@ init_store();
211262
211179
  init_workspace();
211263
211180
  function initShortcuts(getContext) {
211264
211181
  canvasWrap.addEventListener("wheel", (e20) => {
211265
- const { S, setS, canvasMode, panX, panY, setPan, applyTransform: applyTransform2 } = getContext();
211182
+ const { canvasMode, panX, panY, setPan, applyTransform: applyTransform2 } = getContext();
211266
211183
  if (canvasMode === "edit")
211267
211184
  return;
211268
211185
  e20.preventDefault();
@@ -211270,12 +211187,12 @@ function initShortcuts(getContext) {
211270
211187
  const rect = canvasWrap.getBoundingClientRect();
211271
211188
  const cursorX = e20.clientX - rect.left;
211272
211189
  const cursorY = e20.clientY - rect.top;
211273
- const oldZoom = S.ui.zoom;
211190
+ const oldZoom = activeTab.value?.session.ui.zoom ?? 1;
211274
211191
  const delta = -e20.deltaY * 0.005;
211275
211192
  const newZoom = Math.min(5, Math.max(0.05, oldZoom * (1 + delta)));
211276
211193
  const ratio = newZoom / oldZoom;
211277
211194
  setPan(cursorX - (cursorX - panX) * ratio, cursorY - (cursorY - panY) * ratio);
211278
- setS({ ...S, ui: { ...S.ui, zoom: newZoom } });
211195
+ activeTab.value.session.ui.zoom = newZoom;
211279
211196
  } else if (e20.shiftKey) {
211280
211197
  setPan(panX - e20.deltaY, panY);
211281
211198
  } else {
@@ -211310,8 +211227,6 @@ function initShortcuts(getContext) {
211310
211227
  window.addEventListener("resize", () => getContext().positionZoomIndicator());
211311
211228
  document.addEventListener("keydown", (e20) => {
211312
211229
  const {
211313
- S,
211314
- setS,
211315
211230
  canvasMode,
211316
211231
  setPan,
211317
211232
  applyTransform: applyTransform2,
@@ -211320,6 +211235,7 @@ function initShortcuts(getContext) {
211320
211235
  openProject: openProject2,
211321
211236
  enterEditOnPath
211322
211237
  } = getContext();
211238
+ const tab = activeTab.value;
211323
211239
  const mod = e20.ctrlKey || e20.metaKey;
211324
211240
  if (e20.target instanceof HTMLElement && e20.target.matches("input, textarea, select, sp-textfield, sp-search, sp-number-field, sp-picker")) {
211325
211241
  if (mod && e20.key === "s") {
@@ -211356,9 +211272,9 @@ function initShortcuts(getContext) {
211356
211272
  case "w":
211357
211273
  e20.preventDefault();
211358
211274
  if (workspace.activeTabId && workspace.tabOrder.length > 1) {
211359
- const tab = workspace.tabs.get(workspace.activeTabId);
211360
- if (tab?.doc.dirty) {
211361
- const name = tab.documentPath?.split("/").pop() || "Untitled";
211275
+ const tab2 = workspace.tabs.get(workspace.activeTabId);
211276
+ if (tab2?.doc.dirty) {
211277
+ const name = tab2.documentPath?.split("/").pop() || "Untitled";
211362
211278
  if (!window.confirm(`"${name}" has unsaved changes. Close without saving?`))
211363
211279
  break;
211364
211280
  }
@@ -211382,28 +211298,28 @@ function initShortcuts(getContext) {
211382
211298
  break;
211383
211299
  case "d":
211384
211300
  e20.preventDefault();
211385
- if (S.selection) {
211386
- const sel = S.selection;
211387
- transactDoc(activeTab.value, (t15) => mutateDuplicateNode(t15, sel));
211301
+ if (tab?.session.selection) {
211302
+ const sel = tab.session.selection;
211303
+ transactDoc(tab, (t15) => mutateDuplicateNode(t15, sel));
211388
211304
  }
211389
211305
  break;
211390
211306
  case "c":
211391
211307
  e20.preventDefault();
211392
- copyNode(S);
211308
+ copyNode();
211393
211309
  break;
211394
211310
  case "x":
211395
211311
  e20.preventDefault();
211396
- cutNode(S);
211312
+ cutNode();
211397
211313
  break;
211398
211314
  case "v":
211399
211315
  e20.preventDefault();
211400
- pasteNode(S);
211316
+ pasteNode();
211401
211317
  break;
211402
211318
  case "0":
211403
211319
  if (canvasMode === "edit")
211404
211320
  break;
211405
211321
  e20.preventDefault();
211406
- setS({ ...S, ui: { ...S.ui, zoom: 1 } });
211322
+ activeTab.value.session.ui.zoom = 1;
211407
211323
  setPan(16, 16);
211408
211324
  applyTransform2();
211409
211325
  break;
@@ -211412,14 +211328,14 @@ function initShortcuts(getContext) {
211412
211328
  if (canvasMode === "edit")
211413
211329
  break;
211414
211330
  e20.preventDefault();
211415
- setS({ ...S, ui: { ...S.ui, zoom: Math.min(5, S.ui.zoom * 1.2) } });
211331
+ activeTab.value.session.ui.zoom = Math.min(5, (tab?.session.ui.zoom ?? 1) * 1.2);
211416
211332
  applyTransform2();
211417
211333
  break;
211418
211334
  case "-":
211419
211335
  if (canvasMode === "edit")
211420
211336
  break;
211421
211337
  e20.preventDefault();
211422
- setS({ ...S, ui: { ...S.ui, zoom: Math.max(0.05, S.ui.zoom / 1.2) } });
211338
+ activeTab.value.session.ui.zoom = Math.max(0.05, (tab?.session.ui.zoom ?? 1) / 1.2);
211423
211339
  applyTransform2();
211424
211340
  break;
211425
211341
  }
@@ -211428,22 +211344,22 @@ function initShortcuts(getContext) {
211428
211344
  switch (e20.key) {
211429
211345
  case "Delete":
211430
211346
  case "Backspace":
211431
- if (S.selection && S.selection.length >= 2) {
211347
+ if (tab?.session.selection && tab.session.selection.length >= 2) {
211432
211348
  e20.preventDefault();
211433
- const sel = S.selection;
211434
- transactDoc(activeTab.value, (t15) => mutateRemoveNode(t15, sel));
211349
+ const sel = tab.session.selection;
211350
+ transactDoc(tab, (t15) => mutateRemoveNode(t15, sel));
211435
211351
  }
211436
211352
  break;
211437
211353
  case "Escape":
211438
211354
  activeTab.value.session.selection = null;
211439
211355
  break;
211440
211356
  case "Enter":
211441
- if (S.selection && S.selection.length >= 2) {
211357
+ if (tab?.session.selection && tab.session.selection.length >= 2) {
211442
211358
  e20.preventDefault();
211443
- const pp = parentElementPath(S.selection);
211444
- const idx = childIndex(S.selection);
211359
+ const pp = parentElementPath(tab.session.selection);
211360
+ const idx = childIndex(tab.session.selection);
211445
211361
  const newPath = [...pp, "children", idx + 1];
211446
- transactDoc(activeTab.value, (t15) => {
211362
+ transactDoc(tab, (t15) => {
211447
211363
  mutateInsertNode(t15, pp, idx + 1, { tagName: "p", textContent: "" });
211448
211364
  t15.session.selection = newPath;
211449
211365
  });
@@ -211452,24 +211368,24 @@ function initShortcuts(getContext) {
211452
211368
  break;
211453
211369
  case "ArrowUp":
211454
211370
  e20.preventDefault();
211455
- navigateSelection(S);
211371
+ navigateSelection(-1);
211456
211372
  break;
211457
211373
  case "ArrowDown":
211458
211374
  e20.preventDefault();
211459
- navigateSelection(S, 1);
211375
+ navigateSelection(1);
211460
211376
  break;
211461
211377
  case "ArrowLeft":
211462
211378
  e20.preventDefault();
211463
- if (S.selection && S.selection.length >= 2) {
211464
- activeTab.value.session.selection = parentElementPath(S.selection);
211379
+ if (tab?.session.selection && tab.session.selection.length >= 2) {
211380
+ activeTab.value.session.selection = parentElementPath(tab.session.selection);
211465
211381
  }
211466
211382
  break;
211467
211383
  case "ArrowRight":
211468
211384
  e20.preventDefault();
211469
- if (S.selection) {
211470
- const node2 = getNodeAtPath(S.document, S.selection);
211385
+ if (tab?.session.selection) {
211386
+ const node2 = getNodeAtPath(tab.doc.document, tab.session.selection);
211471
211387
  if (node2?.children?.length > 0) {
211472
- activeTab.value.session.selection = [...S.selection, "children", 0];
211388
+ activeTab.value.session.selection = [...tab.session.selection, "children", 0];
211473
211389
  }
211474
211390
  }
211475
211391
  break;
@@ -211481,19 +211397,20 @@ function initShortcuts(getContext) {
211481
211397
  }
211482
211398
  }, { passive: false });
211483
211399
  }
211484
- function navigateSelection(S, direction = -1) {
211485
- if (!S.selection) {
211400
+ function navigateSelection(direction = -1) {
211401
+ const tab = activeTab.value;
211402
+ if (!tab?.session.selection) {
211486
211403
  activeTab.value.session.selection = [];
211487
211404
  return;
211488
211405
  }
211489
- if (S.selection.length < 2)
211406
+ if (tab.session.selection.length < 2)
211490
211407
  return;
211491
- const parent = getNodeAtPath(S.document, parentElementPath(S.selection));
211492
- const idx = childIndex(S.selection);
211408
+ const parent = getNodeAtPath(tab.doc.document, parentElementPath(tab.session.selection));
211409
+ const idx = childIndex(tab.session.selection);
211493
211410
  const newIdx = idx + direction;
211494
211411
  if (parent?.children && newIdx >= 0 && newIdx < parent.children.length) {
211495
211412
  activeTab.value.session.selection = [
211496
- ...parentElementPath(S.selection),
211413
+ ...parentElementPath(tab.session.selection),
211497
211414
  "children",
211498
211415
  newIdx
211499
211416
  ];
@@ -211512,7 +211429,6 @@ function mount4() {
211512
211429
  const tab = activeTab.value;
211513
211430
  if (!tab)
211514
211431
  return;
211515
- tab.session.ui.leftTab;
211516
211432
  renderActivityBar();
211517
211433
  });
211518
211434
  });
@@ -211558,7 +211474,7 @@ function renderActivityBar() {
211558
211474
  const tab = activeTab.value;
211559
211475
  if (!tab)
211560
211476
  return;
211561
- const leftTab = tab.session.ui.leftTab;
211477
+ const leftTab = view.leftTab;
211562
211478
  const tabs = [
211563
211479
  { value: "files", icon: "sp-icon-folder", label: "Files" },
211564
211480
  { value: "layers", icon: "sp-icon-layers", label: "Layers" },
@@ -211575,7 +211491,9 @@ function renderActivityBar() {
211575
211491
  direction="vertical"
211576
211492
  quiet
211577
211493
  @change=${(e20) => {
211578
- activeTab.value.session.ui.leftTab = e20.target.selected;
211494
+ view.leftTab = e20.target.selected;
211495
+ renderOnly("leftPanel");
211496
+ renderActivityBar();
211579
211497
  }}
211580
211498
  >
211581
211499
  ${tabs.map((t15) => html`
@@ -211632,9 +211550,9 @@ function mount5(rootEl, ctx) {
211632
211550
  tab.doc.dirty;
211633
211551
  tab.doc.mode;
211634
211552
  tab.session.selection;
211553
+ tab.session.ui.canvasMode;
211635
211554
  tab.session.ui.editingFunction;
211636
211555
  tab.session.ui.featureToggles;
211637
- tab.session.ui.leftTab;
211638
211556
  tab.session.ui.rightTab;
211639
211557
  render7();
211640
211558
  });
@@ -211747,7 +211665,7 @@ function toolbarTemplate() {
211747
211665
  if (m3.key === "settings")
211748
211666
  uiPatch.rightTab = "style";
211749
211667
  if (m3.key === "manage")
211750
- uiPatch.leftTab = "files";
211668
+ view.leftTab = "files";
211751
211669
  updateSession({ ui: uiPatch });
211752
211670
  _ctx11.renderCanvas();
211753
211671
  _ctx11.safeRenderRightPanel();
@@ -212028,6 +211946,7 @@ function computeInheritedStyle(style2, mediaNames, activeTab2, activeSelector =
212028
211946
 
212029
211947
  // src/panels/style-utils.js
212030
211948
  init_store();
211949
+ init_workspace();
212031
211950
  // data/css-meta.json
212032
211951
  var css_meta_default = {
212033
211952
  $schema: "../../packages/schema/schema.json",
@@ -213638,8 +213557,7 @@ function compressBorderSide(vals) {
213638
213557
  return vals.filter((v) => v && v.trim()).join(" ");
213639
213558
  }
213640
213559
  function getFontVars() {
213641
- const S = getState();
213642
- const style2 = S.document?.style;
213560
+ const style2 = activeTab.value?.doc.document?.style;
213643
213561
  if (!style2)
213644
213562
  return [];
213645
213563
  const vars = [];
@@ -213657,14 +213575,14 @@ var TYPO_PREVIEW_PROPS = new Set([
213657
213575
  "textDecoration"
213658
213576
  ]);
213659
213577
  function currentFontFamily() {
213660
- const S = getState();
213661
- const node2 = S.selection ? getNodeAtPath(S.document, S.selection) : null;
213578
+ const tab = activeTab.value;
213579
+ const node2 = tab?.session.selection ? getNodeAtPath(tab.doc.document, tab.session.selection) : null;
213662
213580
  const raw = node2?.style?.fontFamily;
213663
213581
  if (!raw)
213664
213582
  return "";
213665
213583
  const m3 = typeof raw === "string" && raw.match(/^var\((--[^)]+)\)$/);
213666
213584
  if (m3)
213667
- return S.document?.style?.[m3[1]] || "";
213585
+ return tab?.doc.document?.style?.[m3[1]] || "";
213668
213586
  return raw;
213669
213587
  }
213670
213588
 
@@ -213969,9 +213887,8 @@ function renderSelectInput(entry, prop, value2, onChange) {
213969
213887
  return renderKeywordInput(entry.enum || [], prop, value2, onChange);
213970
213888
  }
213971
213889
  function handleFontPresetSelection(preset, onChange) {
213972
- const S = getState();
213973
213890
  const varName = friendlyNameToVar(preset.title, "--font-");
213974
- if (!S.document?.style?.[varName]) {
213891
+ if (!activeTab.value?.doc.document?.style?.[varName]) {
213975
213892
  transactDoc(activeTab.value, (t15) => mutateUpdateStyle(t15, [], varName, preset.value));
213976
213893
  }
213977
213894
  onChange(`var(${varName})`);
@@ -216298,13 +216215,12 @@ init_workspace();
216298
216215
  init_store();
216299
216216
  init_workspace();
216300
216217
  function renderLayersTemplate(ctx) {
216301
- const S = getState();
216218
+ const tab = activeTab.value;
216302
216219
  for (const fn of view.dndCleanups)
216303
216220
  fn();
216304
216221
  view.dndCleanups = [];
216305
- const rows = flattenTree(S.document);
216306
- const _S = S;
216307
- const collapsed = _S._collapsed || (_S._collapsed = new Set);
216222
+ const rows = flattenTree(tab?.doc.document);
216223
+ const collapsed = view._layersCollapsed || (view._layersCollapsed = new Set);
216308
216224
  const layerRows = [];
216309
216225
  for (const { node: node2, path: path2, depth, nodeType } of rows) {
216310
216226
  let hidden = false;
@@ -216317,7 +216233,7 @@ function renderLayersTemplate(ctx) {
216317
216233
  }
216318
216234
  if (hidden)
216319
216235
  continue;
216320
- if (S.mode === "content" && path2.length === 0)
216236
+ if (tab?.doc.mode === "content" && path2.length === 0)
216321
216237
  continue;
216322
216238
  if (nodeType === "text") {
216323
216239
  const textPreview = String(node2).length > 40 ? String(node2).slice(0, 40) + "…" : String(node2);
@@ -216334,12 +216250,12 @@ function renderLayersTemplate(ctx) {
216334
216250
  }
216335
216251
  if (path2.length >= 2 && nodeType === "element") {
216336
216252
  const pPath = parentElementPath(path2);
216337
- const parentNode2 = pPath ? getNodeAtPath(S.document, pPath) : null;
216253
+ const parentNode2 = pPath ? getNodeAtPath(tab?.doc.document, pPath) : null;
216338
216254
  if (parentNode2 && isInlineElement(node2, parentNode2))
216339
216255
  continue;
216340
216256
  }
216341
216257
  const key = pathKey(path2);
216342
- const isSelected = pathsEqual(path2, S.selection);
216258
+ const isSelected = pathsEqual(path2, tab?.session.selection);
216343
216259
  const hasChildren = Array.isArray(node2.children) && node2.children.length > 0;
216344
216260
  const hasMapChildren = node2.children && typeof node2.children === "object" && node2.children.$prototype === "Array";
216345
216261
  const hasCases = node2.$switch && node2.cases && typeof node2.cases === "object" && Object.keys(node2.cases).length > 0;
@@ -216372,10 +216288,10 @@ function renderLayersTemplate(ctx) {
216372
216288
  labelItalic = false;
216373
216289
  }
216374
216290
  const isElement2 = nodeType === "element";
216375
- const isRoot = S.mode === "content" ? path2.length === 0 : path2.length < 2;
216291
+ const isRoot = tab?.doc.mode === "content" ? path2.length === 0 : path2.length < 2;
216376
216292
  const idx = isElement2 ? childIndex(path2) : 0;
216377
216293
  const parentPath = isElement2 && !isRoot ? parentElementPath(path2) : null;
216378
- const parentNode = parentPath ? getNodeAtPath(S.document, parentPath) : null;
216294
+ const parentNode = parentPath ? getNodeAtPath(tab?.doc.document, parentPath) : null;
216379
216295
  const siblingCount = parentNode?.children?.length || 0;
216380
216296
  const canMoveUp = isElement2 && !isRoot && idx > 0;
216381
216297
  const canMoveDown = isElement2 && !isRoot && idx < siblingCount - 1;
@@ -216390,8 +216306,9 @@ function renderLayersTemplate(ctx) {
216390
216306
  data-dnd-row=${isElement2 ? key : nothing}
216391
216307
  data-dnd-depth=${isElement2 ? depth : nothing}
216392
216308
  data-dnd-void=${isElement2 && isVoidEl ? "" : nothing}
216309
+ data-dnd-expanded=${isElement2 && isExpandable && !collapsed.has(key) ? "" : nothing}
216393
216310
  @click=${() => activeTab.value.session.selection = path2}
216394
- @contextmenu=${isElement2 ? (e20) => showContextMenu(e20, path2, getState(), {
216311
+ @contextmenu=${isElement2 ? (e20) => showContextMenu(e20, path2, {
216395
216312
  onEditComponent: ctx.navigateToComponent
216396
216313
  }) : nothing}
216397
216314
  >
@@ -216439,7 +216356,7 @@ function renderLayersTemplate(ctx) {
216439
216356
  e20.stopPropagation();
216440
216357
  e20.currentTarget.blur();
216441
216358
  const prevPath = [...parentPath, idx - 1];
216442
- const prev = getNodeAtPath(getState().document, prevPath);
216359
+ const prev = getNodeAtPath(activeTab.value?.doc.document, prevPath);
216443
216360
  const len = prev?.children?.length || 0;
216444
216361
  transactDoc(activeTab.value, (t15) => mutateMoveNode(t15, path2, prevPath, len));
216445
216362
  }}
@@ -216505,17 +216422,17 @@ function renderLayersTemplate(ctx) {
216505
216422
  }
216506
216423
 
216507
216424
  // src/panels/stylebook-layers-panel.js
216508
- init_store();
216425
+ init_workspace();
216509
216426
  init_components();
216510
216427
  function hasTagStyle2(rootStyle, tag7) {
216511
216428
  const s2 = rootStyle[`& ${tag7}`];
216512
216429
  return s2 && typeof s2 === "object" && Object.keys(s2).length > 0;
216513
216430
  }
216514
216431
  function renderStylebookLayersTemplate(ctx) {
216515
- const S = getState();
216516
- const rootStyle = S.document?.style || {};
216517
- const selectedTag = S.ui.stylebookSelection;
216518
- if (S.ui.stylebookTab === "elements") {
216432
+ const tab = activeTab.value;
216433
+ const rootStyle = tab?.doc.document?.style || {};
216434
+ const selectedTag = tab?.session.ui.stylebookSelection;
216435
+ if (tab?.session.ui.stylebookTab === "elements") {
216519
216436
  const renderEntryRow = (entry, depth = 0) => {
216520
216437
  const tag7 = entry.tag;
216521
216438
  const uniqueChildren = entry.children ? [...new Map(entry.children.map((c6) => [c6.tag, c6])).values()] : [];
@@ -216585,7 +216502,7 @@ init_store();
216585
216502
  init_workspace();
216586
216503
  init_components();
216587
216504
  function renderElementsTemplate(ctx) {
216588
- const S = getState();
216505
+ const tab = activeTab.value;
216589
216506
  const categories = Object.entries(ctx.webdata.elements).map(([category, elements]) => {
216590
216507
  const filtered = view.elementsFilter ? elements.filter((e20) => e20.tag.includes(view.elementsFilter)) : elements;
216591
216508
  if (filtered.length === 0)
@@ -216608,11 +216525,11 @@ function renderElementsTemplate(ctx) {
216608
216525
  class="element-card"
216609
216526
  data-block-tag=${tag7}
216610
216527
  @click=${() => {
216611
- const s2 = getState();
216612
- const parentPath = s2.selection || [];
216613
- const parent = getNodeAtPath(s2.document, parentPath);
216528
+ const t15 = activeTab.value;
216529
+ const parentPath = t15?.session.selection || [];
216530
+ const parent = getNodeAtPath(t15?.doc.document, parentPath);
216614
216531
  const idx = parent?.children ? parent.children.length : 0;
216615
- transactDoc(activeTab.value, (t15) => mutateInsertNode(t15, parentPath, idx, structuredClone(def3)));
216532
+ transactDoc(t15, (tr) => mutateInsertNode(tr, parentPath, idx, structuredClone(def3)));
216616
216533
  }}
216617
216534
  >
216618
216535
  <div class="element-card-preview"></div>
@@ -216623,7 +216540,7 @@ function renderElementsTemplate(ctx) {
216623
216540
  </sp-accordion-item>
216624
216541
  `;
216625
216542
  });
216626
- const effectiveEls = getEffectiveElements(S.document?.$elements);
216543
+ const effectiveEls = getEffectiveElements(tab?.doc.document?.$elements);
216627
216544
  const enabledTags = new Set;
216628
216545
  for (const entry of effectiveEls) {
216629
216546
  if (typeof entry !== "string")
@@ -216657,9 +216574,9 @@ function renderElementsTemplate(ctx) {
216657
216574
  data-component-tag=${comp.tagName}
216658
216575
  title=${comp.source === "npm" ? `${comp.package}: <${comp.tagName}>` : comp.path}
216659
216576
  @click=${() => {
216660
- const s2 = getState();
216661
- const parentPath = s2.selection || [];
216662
- const parent = getNodeAtPath(s2.document, parentPath);
216577
+ const t15 = activeTab.value;
216578
+ const parentPath = t15?.session.selection || [];
216579
+ const parent = getNodeAtPath(t15?.doc.document, parentPath);
216663
216580
  const idx = parent?.children ? parent.children.length : 0;
216664
216581
  const instanceDef = {
216665
216582
  tagName: comp.tagName,
@@ -216668,7 +216585,7 @@ function renderElementsTemplate(ctx) {
216668
216585
  p2.default !== undefined ? p2.default : ""
216669
216586
  ]))
216670
216587
  };
216671
- transactDoc(activeTab.value, (t15) => mutateInsertNode(t15, parentPath, idx, structuredClone(instanceDef)));
216588
+ transactDoc(t15, (tr) => mutateInsertNode(tr, parentPath, idx, structuredClone(instanceDef)));
216672
216589
  }}
216673
216590
  >
216674
216591
  <div class="element-card-preview">
@@ -216714,7 +216631,6 @@ function mount7(ctx) {
216714
216631
  tab.doc.document;
216715
216632
  tab.doc.mode;
216716
216633
  tab.session.selection;
216717
- tab.session.ui.leftTab;
216718
216634
  tab.session.ui.settingsTab;
216719
216635
  render9();
216720
216636
  });
@@ -216763,7 +216679,7 @@ function _render() {
216763
216679
  mode: aTab.doc.mode,
216764
216680
  selection: aTab.session.selection
216765
216681
  };
216766
- const tab = S.ui.leftTab;
216682
+ const tab = view.leftTab;
216767
216683
  let content3;
216768
216684
  if (tab === "layers")
216769
216685
  content3 = ctx.getCanvasMode() === "settings" ? renderStylebookLayersTemplate({
@@ -216925,16 +216841,23 @@ function requestClose(id) {
216925
216841
  }
216926
216842
 
216927
216843
  // src/studio.js
216928
- var S;
216929
- var doc = null;
216930
- var session = null;
216931
216844
  function createFloatingContainer() {
216932
216845
  const el = document.createElement("div");
216933
216846
  el.style.display = "contents";
216934
216847
  (document.querySelector("sp-theme") || document.body).appendChild(el);
216935
216848
  return el;
216936
216849
  }
216937
- var canvasMode = "design";
216850
+ function getCanvasMode() {
216851
+ return activeTab.value?.session.ui.canvasMode ?? "design";
216852
+ }
216853
+ function setCanvasMode(mode) {
216854
+ if (getCanvasMode() === "git-diff" && mode !== "git-diff") {
216855
+ gitDiffState = null;
216856
+ }
216857
+ const tab = activeTab.value;
216858
+ if (tab)
216859
+ tab.session.ui.canvasMode = mode;
216860
+ }
216938
216861
  var gitDiffState = null;
216939
216862
  async function navigateToComponent(componentPath) {
216940
216863
  try {
@@ -216943,9 +216866,27 @@ async function navigateToComponent(componentPath) {
216943
216866
  if (!content3)
216944
216867
  return;
216945
216868
  const parsed = JSON.parse(content3);
216946
- S = pushDocument(S, parsed, componentPath);
216947
- S.dirty = false;
216948
- ({ doc, session } = fromFlat(S));
216869
+ const tab = activeTab.value;
216870
+ if (!tab)
216871
+ return;
216872
+ const frame = {
216873
+ document: tab.doc.document,
216874
+ selection: tab.session.selection,
216875
+ documentPath: tab.documentPath,
216876
+ dirty: tab.doc.dirty,
216877
+ mode: tab.doc.mode
216878
+ };
216879
+ if (!tab.session.documentStack)
216880
+ tab.session.documentStack = [];
216881
+ tab.session.documentStack.push(frame);
216882
+ tab.doc.document = parsed;
216883
+ tab.doc.dirty = false;
216884
+ tab.doc.mode = null;
216885
+ tab.documentPath = componentPath;
216886
+ tab.session.selection = null;
216887
+ view.leftTab = "layers";
216888
+ tab.session.ui.activeMedia = null;
216889
+ tab.session.ui.activeSelector = null;
216949
216890
  render();
216950
216891
  statusMessage(`Editing component: ${parsed.tagName || componentPath}`);
216951
216892
  } catch (e20) {
@@ -216954,24 +216895,33 @@ async function navigateToComponent(componentPath) {
216954
216895
  }
216955
216896
  }
216956
216897
  async function navigateBack() {
216957
- if (!S.documentStack || S.documentStack.length === 0)
216898
+ const tab = activeTab.value;
216899
+ if (!tab?.session.documentStack || tab.session.documentStack.length === 0)
216958
216900
  return;
216959
- if (S.dirty && S.documentPath) {
216901
+ if (tab.doc.dirty && tab.documentPath) {
216960
216902
  try {
216961
216903
  const platform4 = getPlatform();
216962
- await platform4.writeFile(S.documentPath, JSON.stringify(S.document, null, 2));
216904
+ await platform4.writeFile(tab.documentPath, JSON.stringify(tab.doc.document, null, 2));
216963
216905
  } catch (e20) {
216964
216906
  const err = e20;
216965
216907
  statusMessage(`Save error: ${err.message}`);
216966
216908
  }
216967
216909
  }
216968
- S = popDocument(S);
216969
- ({ doc, session } = fromFlat(S));
216910
+ const frame = tab.session.documentStack.pop();
216911
+ if (!frame)
216912
+ return;
216913
+ tab.doc.document = frame.document;
216914
+ tab.doc.dirty = frame.dirty;
216915
+ tab.doc.mode = frame.mode;
216916
+ tab.documentPath = frame.documentPath;
216917
+ tab.session.selection = frame.selection;
216918
+ view.leftTab = "layers";
216970
216919
  render();
216971
216920
  statusMessage("Returned to parent document");
216972
216921
  }
216973
216922
  async function closeFunctionEditor() {
216974
- const editing = S.ui.editingFunction;
216923
+ const tab = activeTab.value;
216924
+ const editing = tab?.session.ui.editingFunction;
216975
216925
  if (!editing)
216976
216926
  return;
216977
216927
  if (view.functionEditor) {
@@ -216979,11 +216929,11 @@ async function closeFunctionEditor() {
216979
216929
  const minResult = await codeService("minify", { code: currentCode });
216980
216930
  const bodyToStore = minResult?.code ?? currentCode;
216981
216931
  if (editing.type === "def") {
216982
- transactDoc(activeTab.value, (t15) => mutateUpdateDef(t15, editing.defName, { body: bodyToStore }));
216932
+ transactDoc(tab, (t15) => mutateUpdateDef(t15, editing.defName, { body: bodyToStore }));
216983
216933
  } else if (editing.type === "event") {
216984
- const node2 = getNodeAtPath(S.document, editing.path);
216934
+ const node2 = getNodeAtPath(tab.doc.document, editing.path);
216985
216935
  const current = node2?.[editing.eventKey] || {};
216986
- transactDoc(activeTab.value, (t15) => mutateUpdateProperty(t15, editing.path, editing.eventKey, {
216936
+ transactDoc(tab, (t15) => mutateUpdateProperty(t15, editing.path, editing.eventKey, {
216987
216937
  ...current,
216988
216938
  $prototype: "Function",
216989
216939
  body: bodyToStore
@@ -217017,74 +216967,57 @@ var EMPTY_DOC = {
217017
216967
  { tagName: "p", textContent: "Open a Jx file or start editing." }
217018
216968
  ]
217019
216969
  };
217020
- S = createState(structuredClone(EMPTY_DOC));
217021
- ({ doc, session } = fromFlat(S));
217022
- setGetStateFn(() => S);
217023
216970
  openTab({ id: "initial", document: structuredClone(EMPTY_DOC) });
217024
216971
  mount5(toolbarEl, {
217025
216972
  navigateBack: () => navigateBack(),
217026
216973
  closeFunctionEditor: () => closeFunctionEditor(),
217027
216974
  openProject: () => openProject2(),
217028
- openFile: () => openFile2(),
217029
- saveFile: () => saveFile2(),
216975
+ openFile: () => openFile(),
216976
+ saveFile: () => saveFile(),
217030
216977
  parseMediaEntries,
217031
- getCanvasMode: () => canvasMode,
217032
- setCanvasMode: (m3) => {
217033
- if (canvasMode === "git-diff" && m3 !== "git-diff") {
217034
- gitDiffState = null;
217035
- }
217036
- canvasMode = m3;
217037
- },
216978
+ getCanvasMode,
216979
+ setCanvasMode,
217038
216980
  renderCanvas: () => renderCanvas(),
217039
216981
  safeRenderRightPanel: () => safeRenderRightPanel()
217040
216982
  });
217041
216983
  mount8(document.querySelector("#tab-strip"));
217042
216984
  mount3({
217043
- getCanvasMode: () => canvasMode,
216985
+ getCanvasMode,
217044
216986
  isEditing,
217045
216987
  renderBlockActionBar
217046
216988
  });
217047
216989
  initBlockActionBar({
217048
- getCanvasMode: () => canvasMode,
216990
+ getCanvasMode,
217049
216991
  navigateToComponent,
217050
216992
  createFloatingContainer
217051
216993
  });
217052
216994
  initComponentInlineEdit({ findCanvasElement });
217053
- initCanvasHelpers({ getCanvasMode: () => canvasMode, getZoom: () => S.ui.zoom });
216995
+ initCanvasHelpers({
216996
+ getCanvasMode,
216997
+ getZoom: () => activeTab.value?.session.ui.zoom ?? 1
216998
+ });
217054
216999
  initCanvasUtils({
217055
- getCanvasMode: () => canvasMode,
217056
- getZoom: () => S.ui.zoom,
217000
+ getCanvasMode,
217001
+ getZoom: () => activeTab.value?.session.ui.zoom ?? 1,
217057
217002
  setZoomDirect: (zoom) => {
217058
- session = { ...session, ui: { ...session.ui, zoom } };
217059
- S = toFlat(doc, session);
217060
217003
  if (activeTab.value)
217061
217004
  activeTab.value.session.ui.zoom = zoom;
217062
217005
  },
217063
217006
  renderStylebookOverlays
217064
217007
  });
217065
217008
  initPanelEvents({
217066
- getState: () => S,
217067
- setState: (s2) => {
217068
- S = s2;
217069
- ({ doc, session } = fromFlat(S));
217070
- },
217071
- getCanvasMode: () => canvasMode,
217009
+ getCanvasMode,
217072
217010
  enterInlineEdit,
217073
217011
  navigateToComponent
217074
217012
  });
217075
217013
  initCanvasLiveRender({
217076
- getCanvasMode: () => canvasMode
217014
+ getCanvasMode
217077
217015
  });
217078
217016
  initCanvasRender({
217079
- getCanvasMode: () => canvasMode,
217080
- setCanvasMode: (mode) => {
217081
- if (canvasMode === "git-diff" && mode !== "git-diff") {
217082
- gitDiffState = null;
217083
- }
217084
- canvasMode = mode;
217085
- },
217086
- openFileFromTree: openFileFromTree2,
217087
- exportFile: exportFile2,
217017
+ getCanvasMode,
217018
+ setCanvasMode,
217019
+ openFileFromTree,
217020
+ exportFile,
217088
217021
  gitDiffState,
217089
217022
  setGitDiffState: (state3) => {
217090
217023
  gitDiffState = state3;
@@ -217097,6 +217030,7 @@ effect(() => {
217097
217030
  return;
217098
217031
  tab.doc.document;
217099
217032
  tab.doc.mode;
217033
+ tab.session.ui.canvasMode;
217100
217034
  tab.session.ui.editingFunction;
217101
217035
  tab.session.ui.featureToggles;
217102
217036
  tab.session.ui.settingsTab;
@@ -217117,15 +217051,13 @@ effect(() => {
217117
217051
  });
217118
217052
  mount6({
217119
217053
  navigateToComponent,
217120
- getCanvasMode: () => canvasMode,
217054
+ getCanvasMode,
217121
217055
  renderCanvas: () => renderCanvas(),
217122
217056
  updateForcedPseudoPreview
217123
217057
  });
217124
217058
  mount7({
217125
- getCanvasMode: () => canvasMode,
217126
- setCanvasMode: (mode) => {
217127
- canvasMode = mode;
217128
- },
217059
+ getCanvasMode,
217060
+ setCanvasMode,
217129
217061
  renderImportsTemplate,
217130
217062
  renderFilesTemplate: renderFilesTemplate2,
217131
217063
  renderSignalsTemplate,
@@ -217147,76 +217079,19 @@ registerRenderer("leftPanel", () => render9());
217147
217079
  registerRenderer("canvas", () => renderCanvas());
217148
217080
  registerRenderer("rightPanel", () => render8());
217149
217081
  registerRenderer("overlays", () => render4());
217150
- setStatusbarRenderer(() => renderStatusbar(S));
217082
+ setStatusbarRenderer(() => renderStatusbar());
217151
217083
  mountStatusbar();
217152
217084
  mount4();
217153
217085
  canvasWrap.addEventListener("click", (e20) => {
217154
217086
  if (e20.target !== canvasWrap && e20.target !== view.panzoomWrap)
217155
217087
  return;
217156
- if (!S.selection)
217088
+ if (!activeTab.value?.session.selection)
217157
217089
  return;
217158
217090
  activeTab.value.session.selection = null;
217159
217091
  });
217160
217092
  function safeRenderRightPanel() {
217161
217093
  render8();
217162
217094
  }
217163
- setGetStateFn(() => S);
217164
- setUpdateFn(function _update(newState) {
217165
- const prevDoc = S.document;
217166
- const prevSel = S.selection;
217167
- const prevUi = S.ui;
217168
- S = newState;
217169
- ({ doc, session } = fromFlat(S));
217170
- const tab = activeTab.value;
217171
- if (tab) {
217172
- tab.doc.document = S.document;
217173
- tab.doc.dirty = S.dirty;
217174
- tab.doc.mode = S.mode;
217175
- tab.doc.handlersSource = S.handlersSource;
217176
- tab.doc.content.frontmatter = S.content?.frontmatter ?? {};
217177
- tab.session.selection = S.selection;
217178
- tab.session.hover = S.hover;
217179
- tab.session.clipboard = S.clipboard ?? null;
217180
- for (const [k, v] of Object.entries(S.ui || {})) {
217181
- tab.session.ui[k] = v;
217182
- }
217183
- tab.session.canvas.status = S.canvas?.status ?? "idle";
217184
- tab.session.canvas.scope = S.canvas?.scope ?? null;
217185
- tab.session.canvas.error = S.canvas?.error ?? null;
217186
- }
217187
- if (prevUi?.activeMedia !== S.ui?.activeMedia) {
217188
- updateActivePanelHeaders();
217189
- }
217190
- runPostRenderHooks(prevDoc, prevSel);
217191
- runUpdateMiddleware(S);
217192
- });
217193
- setUpdateSessionFn(function _updateSession(patch2) {
217194
- const prev = session;
217195
- session = { ...session, ...patch2 };
217196
- if (patch2.ui) {
217197
- session.ui = { ...prev.ui, ...patch2.ui };
217198
- }
217199
- if (patch2.canvas) {
217200
- session.canvas = { ...prev.canvas, ...patch2.canvas };
217201
- }
217202
- S = toFlat(doc, session);
217203
- if (prev.ui?.activeMedia !== session.ui?.activeMedia) {
217204
- updateActivePanelHeaders();
217205
- }
217206
- const canvasChanged = prev.canvas !== session.canvas;
217207
- if (canvasChanged && session.canvas.status === "ready" && session.ui?.pendingInlineEdit) {
217208
- const { path: path2, mediaName: mn } = session.ui.pendingInlineEdit;
217209
- updateUi("pendingInlineEdit", null);
217210
- const targetPanel = canvasPanels.find((p2) => p2.mediaName === mn) || canvasPanels[0];
217211
- if (targetPanel) {
217212
- const el = findCanvasElement(path2, targetPanel.canvas);
217213
- if (el)
217214
- enterComponentInlineEdit(el, path2);
217215
- }
217216
- }
217217
- runPostRenderHooks(doc.document, prev.selection);
217218
- });
217219
- addPostRenderHook(() => updateForcedPseudoPreview());
217220
217095
  registerFunctionCompletions();
217221
217096
  var _openParam = new URLSearchParams(location.search).get("open");
217222
217097
  if (_openParam) {
@@ -217275,29 +217150,27 @@ if (_openParam) {
217275
217150
  const fileRelPath = _fileParam || siteCtx.fileRelPath || _openParam;
217276
217151
  const content3 = await platform4.readFile(fileRelPath);
217277
217152
  if (content3) {
217278
- let parsedDoc;
217279
- if (fileRelPath.endsWith(".md")) {
217280
- const ns = await loadMarkdown(content3, null);
217281
- S = ns;
217282
- S.documentPath = fileRelPath;
217283
- parsedDoc = S.document;
217153
+ let parsedDoc, frontmatter2;
217154
+ const isMd = fileRelPath.endsWith(".md");
217155
+ if (isMd) {
217156
+ const result = await loadMarkdown(content3);
217157
+ parsedDoc = result.document;
217158
+ frontmatter2 = result.frontmatter;
217284
217159
  } else {
217285
217160
  parsedDoc = JSON.parse(content3);
217286
- S = createState(parsedDoc);
217287
- S.documentPath = fileRelPath;
217288
- }
217289
- S.dirty = false;
217290
- S.ui = { ...S.ui, leftTab: "files" };
217291
- ({ doc, session } = fromFlat(S));
217292
- const tab = activeTab.value;
217293
- if (tab) {
217294
- tab.doc.document = S.document;
217295
- tab.doc.mode = S.mode;
217296
- tab.doc.dirty = false;
217297
- tab.doc.content = S.content || { frontmatter: {} };
217298
- tab.documentPath = fileRelPath;
217299
- tab.session.ui.leftTab = "files";
217300
217161
  }
217162
+ const { closeTab: closeTab2 } = await Promise.resolve().then(() => (init_workspace(), exports_workspace));
217163
+ closeTab2("initial");
217164
+ openTab({
217165
+ id: fileRelPath,
217166
+ documentPath: fileRelPath,
217167
+ document: parsedDoc,
217168
+ frontmatter: frontmatter2,
217169
+ sourceFormat: isMd ? "md" : null
217170
+ });
217171
+ if (isMd && activeTab.value)
217172
+ activeTab.value.doc.mode = "content";
217173
+ view.leftTab = "files";
217301
217174
  render();
217302
217175
  statusMessage(`Opened ${_openParam}`);
217303
217176
  }
@@ -217313,56 +217186,23 @@ if (_openParam) {
217313
217186
  function renderLeftPanel() {
217314
217187
  render9();
217315
217188
  }
217316
- function renderToolbar() {
217317
- render7();
217318
- }
217319
- function fileOpsCtx() {
217320
- return {
217321
- S,
217322
- commit: (ns) => {
217323
- S = ns;
217324
- ({ doc, session } = fromFlat(S));
217325
- render();
217326
- },
217327
- renderToolbar
217328
- };
217329
- }
217330
- function openFile2() {
217331
- return openFile(fileOpsCtx());
217332
- }
217333
- function saveFile2() {
217334
- return saveFile(fileOpsCtx());
217335
- }
217336
- function exportFile2() {
217337
- return exportFile(fileOpsCtx());
217338
- }
217339
217189
  function loadProject2() {
217340
217190
  return loadProject();
217341
217191
  }
217342
217192
  function openProject2() {
217343
217193
  return openProject({
217344
- S,
217345
- commit: (ns) => {
217346
- S = ns;
217347
- ({ doc, session } = fromFlat(S));
217348
- },
217349
217194
  renderActivityBar: () => renderActivityBar(),
217350
217195
  renderLeftPanel
217351
217196
  });
217352
217197
  }
217353
217198
  function renderFilesTemplate2() {
217354
- return renderFilesTemplate({ openProject: openProject2, openFileFromTree: openFileFromTree2, renderLeftPanel });
217199
+ return renderFilesTemplate({ openProject: openProject2, openFileFromTree, renderLeftPanel });
217355
217200
  }
217356
- function openFileFromTree2(path2) {
217201
+ function openFileFromTree(path2) {
217357
217202
  return openFileInTab(path2);
217358
217203
  }
217359
217204
  initShortcuts(() => ({
217360
- S,
217361
- setS: (ns) => {
217362
- S = ns;
217363
- ({ doc, session } = fromFlat(S));
217364
- },
217365
- canvasMode,
217205
+ canvasMode: getCanvasMode(),
217366
217206
  panX: view.panX,
217367
217207
  panY: view.panY,
217368
217208
  setPan: (x, y) => {
@@ -217373,7 +217213,7 @@ initShortcuts(() => ({
217373
217213
  applyTransform,
217374
217214
  positionZoomIndicator,
217375
217215
  componentInlineEdit: view.componentInlineEdit,
217376
- saveFile: saveFile2,
217216
+ saveFile,
217377
217217
  openProject: openProject2,
217378
217218
  enterEditOnPath(path2) {
217379
217219
  requestAnimationFrame(() => {
@@ -217389,25 +217229,27 @@ initShortcuts(() => ({
217389
217229
  }));
217390
217230
  var AUTO_SAVE_DELAY = 2000;
217391
217231
  function scheduleAutosave() {
217392
- if (!S.fileHandle || !S.dirty)
217232
+ const tab = activeTab.value;
217233
+ if (!tab?.fileHandle || !tab.doc.dirty)
217393
217234
  return;
217394
217235
  clearTimeout(view.autosaveTimer);
217395
217236
  view.autosaveTimer = setTimeout(async () => {
217396
- if (S.fileHandle && S.dirty && "createWritable" in S.fileHandle) {
217237
+ const t15 = activeTab.value;
217238
+ if (t15?.fileHandle && t15.doc.dirty && "createWritable" in t15.fileHandle) {
217397
217239
  try {
217398
- const writable = await S.fileHandle.createWritable();
217399
- await writable.write(JSON.stringify(S.document, null, 2));
217240
+ const writable = await t15.fileHandle.createWritable();
217241
+ await writable.write(JSON.stringify(t15.doc.document, null, 2));
217400
217242
  await writable.close();
217401
- activeTab.value.doc.dirty = false;
217243
+ t15.doc.dirty = false;
217402
217244
  statusMessage("Auto-saved");
217403
217245
  } catch {}
217404
217246
  }
217405
217247
  }, AUTO_SAVE_DELAY);
217406
217248
  }
217407
- addUpdateMiddleware((state3) => {
217408
- if (state3.dirty)
217249
+ effect(() => {
217250
+ if (activeTab.value?.doc.dirty)
217409
217251
  scheduleAutosave();
217410
217252
  });
217411
217253
 
217412
- //# debugId=B17DF22CF984123464756E2164756E21
217254
+ //# debugId=CE5832A90C0FE77064756E2164756E21
217413
217255
  //# sourceMappingURL=studio.js.map