@jxsuite/studio 0.21.4 → 0.22.1

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.
package/dist/studio.js CHANGED
@@ -254721,6 +254721,8 @@ function mutateUpdateAttribute(tab, path, attr, value) {
254721
254721
  delete node.attributes;
254722
254722
  }
254723
254723
  function mutateUpdateMediaStyle(tab, path, mediaName, prop, value) {
254724
+ if (!mediaName)
254725
+ return mutateUpdateStyle(tab, path, prop, value);
254724
254726
  const node = getNodeAtPath(tab.doc.document, path);
254725
254727
  if (!node.style)
254726
254728
  node.style = {};
@@ -263034,6 +263036,12 @@ function collapsePropsToAttrMap(propsObj) {
263034
263036
  if (key.startsWith(":") && CSS_PSEUDO_NAMES2.has(key.slice(1))) {
263035
263037
  mdAttrKey = key.slice(1);
263036
263038
  }
263039
+ if (key === "@") {
263040
+ if (value2 && typeof value2 === "object" && !Array.isArray(value2)) {
263041
+ walk2(value2, prefix);
263042
+ }
263043
+ continue;
263044
+ }
263037
263045
  if (key.startsWith("@--")) {
263038
263046
  mdAttrKey = key.slice(1);
263039
263047
  }
@@ -263060,9 +263068,11 @@ async function codeService(action, payload) {
263060
263068
  }
263061
263069
  var pluginSchemaCache = new Map;
263062
263070
  async function fetchPluginSchema(def3, state) {
263063
- if (!def3.$src || !def3.$prototype)
263071
+ const importedPath = def3.$prototype ? projectState?.projectConfig?.imports?.[def3.$prototype] : null;
263072
+ const src = def3.$src || importedPath;
263073
+ if (!src || !def3.$prototype)
263064
263074
  return null;
263065
- const cacheKey = `${def3.$src}::${def3.$prototype}`;
263075
+ const cacheKey = `${src}::${def3.$prototype}`;
263066
263076
  if (pluginSchemaCache.has(cacheKey))
263067
263077
  return pluginSchemaCache.get(cacheKey);
263068
263078
  try {
@@ -263071,8 +263081,8 @@ async function fetchPluginSchema(def3, state) {
263071
263081
  pluginSchemaCache.set(cacheKey, null);
263072
263082
  return null;
263073
263083
  }
263074
- const base2 = state.documentPath ? `${location.origin}/${state.documentPath}` : undefined;
263075
- const schema4 = await platform3.fetchPluginSchema(def3.$src, def3.$prototype, base2);
263084
+ const base2 = !importedPath && state.documentPath ? `${location.origin}/${state.documentPath}` : undefined;
263085
+ const schema4 = await platform3.fetchPluginSchema(src, def3.$prototype, base2);
263076
263086
  pluginSchemaCache.set(cacheKey, schema4);
263077
263087
  return schema4;
263078
263088
  } catch {
@@ -264427,10 +264437,6 @@ var _serverFnConfig = { skip: false };
264427
264437
  function setSkipServerFunctions(v) {
264428
264438
  _serverFnConfig.skip = v;
264429
264439
  }
264430
- var _contentConfig = { skip: false };
264431
- function setSkipContentResolution(v) {
264432
- _contentConfig.skip = v;
264433
- }
264434
264440
  async function buildScope(doc, parentScope = {}, base2 = location.href) {
264435
264441
  const raw = {};
264436
264442
  for (const [key, val] of Object.entries(parentScope)) {
@@ -264503,6 +264509,8 @@ async function buildScope(doc, parentScope = {}, base2 = location.href) {
264503
264509
  }
264504
264510
  if (doc.$media) {
264505
264511
  state["$media"] = doc.$media;
264512
+ } else if (!state["$media"] && Object.keys(_rootMedia).length > 0) {
264513
+ state["$media"] = _rootMedia;
264506
264514
  }
264507
264515
  return state;
264508
264516
  }
@@ -264769,16 +264777,16 @@ function applyStyle2(el, styleDef, mediaQueries = {}, state = {}) {
264769
264777
  const resolved = sel.startsWith("&") ? sel.replace("&", parentSel) : sel.startsWith("[") ? `${parentSel}${sel}` : sel.startsWith(":") || sel.startsWith(".") ? `${parentSel}${sel}` : `${parentSel} ${sel}`;
264770
264778
  const props = toCSSText(sub);
264771
264779
  if (props)
264772
- css += `@media ${query} { ${resolved} { ${props} } }
264780
+ css += `${atRule} { ${resolved} { ${props} } }
264773
264781
  `;
264774
264782
  emitMediaNested(resolved, sub);
264775
264783
  }
264776
264784
  };
264777
264785
  if (key === "@--")
264778
264786
  continue;
264779
- const query = key.startsWith("@--") ? mediaQueries[key.slice(1)] ?? key.slice(1) : key.slice(1);
264787
+ const atRule = key.startsWith("@--") ? `@media ${mediaQueries[key.slice(1)] ?? key.slice(1)}` : key.startsWith("@(") ? `@media ${key.slice(1)}` : key;
264780
264788
  const scope = `[data-jx="${uid}"]`;
264781
- css += `@media ${query} { ${scope} { ${toCSSText(rules)} } }
264789
+ css += `${atRule} { ${scope} { ${toCSSText(rules)} } }
264782
264790
  `;
264783
264791
  emitMediaNested(scope, rules);
264784
264792
  }
@@ -264805,7 +264813,8 @@ function renderMappedArray(def3, state, options) {
264805
264813
  applyProperties(container, def3, state);
264806
264814
  applyStyle2(container, def3.style ?? {}, state["$media"] ?? {}, state);
264807
264815
  applyAttributes(container, def3.attributes ?? {}, state);
264808
- const { items: itemsSrc, map: mapDef, filter: filterRef, sort: sortRef } = def3.children;
264816
+ const { items: _items, of: _of, map: mapDef, filter: filterRef, sort: sortRef } = def3.children;
264817
+ const itemsSrc = _items ?? _of;
264809
264818
  effect(() => {
264810
264819
  container.innerHTML = "";
264811
264820
  let items;
@@ -265033,39 +265042,6 @@ async function resolvePrototype(def3, state, key, base2) {
265033
265042
  return new Blob(def3.parts ?? [], { type: def3.type ?? "text/plain" });
265034
265043
  case "ReadableStream":
265035
265044
  return null;
265036
- case "ContentCollection":
265037
- case "ContentEntry": {
265038
- if (_contentConfig.skip) {
265039
- return ref(def3.$prototype === "ContentCollection" ? [{ id: "", data: {} }] : null);
265040
- }
265041
- const s2 = ref(def3.$prototype === "ContentCollection" ? [] : null);
265042
- effect(() => {
265043
- let id = def3.id;
265044
- if (id && typeof id === "object" && id.$ref?.startsWith("#/$params/")) {
265045
- const paramName = id.$ref.replace("#/$params/", "");
265046
- id = state?.$params?.[paramName];
265047
- } else if (typeof id === "string" && id.includes("${")) {
265048
- id = evaluateTemplate(id, state);
265049
- }
265050
- if (def3.$prototype === "ContentEntry" && !id)
265051
- return;
265052
- fetch("/__studio/content", {
265053
- method: "POST",
265054
- headers: { "Content-Type": "application/json" },
265055
- body: JSON.stringify({
265056
- $prototype: def3.$prototype,
265057
- contentType: def3.contentType,
265058
- id,
265059
- filter: def3.filter,
265060
- sort: def3.sort,
265061
- limit: def3.limit
265062
- })
265063
- }).then((r) => r.ok ? r.json() : null).then((d2) => {
265064
- s2.value = d2;
265065
- }).catch(() => {});
265066
- });
265067
- return s2;
265068
- }
265069
265045
  default:
265070
265046
  console.warn(`Jx: unknown $prototype "${def3.$prototype}" for "${key}". Did you mean to add '$src'?`);
265071
265047
  return ref(null);
@@ -265149,6 +265125,10 @@ async function resolveClassJson(def3, state, key, base2) {
265149
265125
  return resolveViaDevProxy(def3, state, key, base2);
265150
265126
  }
265151
265127
  if (classDef.$implementation) {
265128
+ const schemaStr = JSON.stringify(classDef);
265129
+ if (schemaStr.includes('"#/$context/')) {
265130
+ return resolveViaDevProxy(def3, state, key, base2);
265131
+ }
265152
265132
  const schemaUrl = base2 ? new URL(src, base2).href : new URL(src, location.href).href;
265153
265133
  const implSrc = new URL(classDef.$implementation, schemaUrl).href;
265154
265134
  const exportName = def3.$export ?? classDef.title ?? def3.$prototype;
@@ -265414,6 +265394,7 @@ function camelToKebab(s2) {
265414
265394
  function toCSSText(rules) {
265415
265395
  return Object.entries(rules).filter(([k, v]) => !isNestedSelector2(k) && (v === null || typeof v !== "object")).map(([p, v]) => `${camelToKebab(p)}: ${v}`).join("; ");
265416
265396
  }
265397
+ var _rootMedia = {};
265417
265398
  var _elementDefs = new Map;
265418
265399
  async function registerElements(elements, base2) {
265419
265400
  for (const entry of elements) {
@@ -265690,7 +265671,6 @@ async function renderCanvasLive(gen, doc, canvasEl) {
265690
265671
  _failedElementsDocPath = S.documentPath ?? null;
265691
265672
  }
265692
265673
  setSkipServerFunctions(canvasMode !== "preview");
265693
- setSkipContentResolution(canvasMode !== "preview");
265694
265674
  let renderDoc = canvasMode === "preview" ? structuredClone(toRaw(doc)) : prepareForEditMode(stripEventHandlers(doc));
265695
265675
  let layoutWrapped = false;
265696
265676
  activeLayoutPath = null;
@@ -265741,7 +265721,7 @@ async function renderCanvasLive(gen, doc, canvasEl) {
265741
265721
  const docPrefix = root2 ? `${root2}/` : "";
265742
265722
  const docBase = S.documentPath ? `${location.origin}/${docPrefix}${S.documentPath}` : undefined;
265743
265723
  let effectiveElements = getEffectiveElements(renderDoc.$elements);
265744
- if (S.mode === "content" && componentRegistry.length > 0) {
265724
+ if ((S.mode === "content" || layoutWrapped) && componentRegistry.length > 0) {
265745
265725
  const existingRefs = new Set(effectiveElements.map((e) => typeof e === "string" ? e : e?.$ref));
265746
265726
  const collectTags = (node2) => {
265747
265727
  const tags2 = new Set;
@@ -265862,6 +265842,8 @@ async function renderCanvasLive(gen, doc, canvasEl) {
265862
265842
  if (!entry?.tagName)
265863
265843
  continue;
265864
265844
  const tag3 = entry.tagName.toLowerCase();
265845
+ if (tag3 === "script" && !entry.attributes?.src)
265846
+ continue;
265865
265847
  const attrs = { ...entry.attributes };
265866
265848
  for (const key of ["href", "src"]) {
265867
265849
  const val = attrs[key];
@@ -266340,6 +266322,24 @@ function renderSignalsTemplate(S, ctx) {
266340
266322
  const type = e.target.value;
266341
266323
  if (!type)
266342
266324
  return;
266325
+ if (type.startsWith("import:")) {
266326
+ const protoName = type.slice(7);
266327
+ const src = projectState?.projectConfig?.imports?.[protoName];
266328
+ let n3 = "$" + protoName.charAt(0).toLowerCase() + protoName.slice(1);
266329
+ let i3 = 1;
266330
+ const base2 = n3;
266331
+ while (S.document.state && S.document.state[n3]) {
266332
+ n3 = base2 + i3++;
266333
+ }
266334
+ transactDoc(activeTab.value, (t) => mutateAddDef(t, n3, { $prototype: protoName }));
266335
+ expandedSignal = n3;
266336
+ if (src) {
266337
+ fetchPluginSchema({ $prototype: protoName, $src: src }, S).then(() => ctx.renderLeftPanel());
266338
+ } else {
266339
+ ctx.renderLeftPanel();
266340
+ }
266341
+ return;
266342
+ }
266343
266343
  const template = DEF_TEMPLATES[type];
266344
266344
  if (!template)
266345
266345
  return;
@@ -266367,6 +266367,7 @@ function renderSignalsTemplate(S, ctx) {
266367
266367
  <sp-menu-item value="map">Map</sp-menu-item>
266368
266368
  <sp-menu-item value="formData">FormData</sp-menu-item>
266369
266369
  <sp-menu-item value="external">External Module…</sp-menu-item>
266370
+ ${projectState?.projectConfig?.imports ? html3`<sp-menu-divider></sp-menu-divider>${Object.keys(projectState.projectConfig.imports).map((k) => html3`<sp-menu-item value="import:${k}">${k}</sp-menu-item>`)}` : nothing}
266370
266371
  <sp-menu-divider></sp-menu-divider>
266371
266372
  <sp-menu-item value="function">Function</sp-menu-item>
266372
266373
  </sp-picker>
@@ -266554,9 +266555,6 @@ function renderDataSourceFields(S, name, def3, textareaRow, pickerRow, ctx) {
266554
266555
  ${signalFieldRow("Default", String(def3.default || ""), (v) => transactDoc(activeTab.value, (t) => mutateUpdateDef(t, name, { default: v })))}
266555
266556
  `;
266556
266557
  }
266557
- if (proto2 === "ContentEntry" || proto2 === "ContentCollection") {
266558
- return renderContentPrototypeFields(name, def3, proto2);
266559
- }
266560
266558
  if (proto2 === "Set" || proto2 === "Map" || proto2 === "FormData") {
266561
266559
  const fieldName = proto2 === "FormData" ? "fields" : "default";
266562
266560
  const fieldLabel = proto2 === "FormData" ? "Fields" : "Default";
@@ -266569,85 +266567,6 @@ function renderDataSourceFields(S, name, def3, textareaRow, pickerRow, ctx) {
266569
266567
  }
266570
266568
  return renderExternalPrototypeEditorTemplate(S, name, def3, ctx);
266571
266569
  }
266572
- function renderContentPrototypeFields(name, def3, proto2) {
266573
- const contentTypes = projectState?.projectConfig?.contentTypes;
266574
- const typeNames = contentTypes ? Object.keys(contentTypes) : [];
266575
- const currentType = def3.contentType || "";
266576
- const schema4 = currentType && contentTypes?.[currentType]?.schema || undefined;
266577
- const contentTypeField = typeNames.length ? html3`
266578
- <div class="signal-field-row">
266579
- <label class="signal-field-label">Content Type</label>
266580
- <sp-picker
266581
- size="s"
266582
- value=${currentType}
266583
- style="width:100%"
266584
- @change=${(e) => {
266585
- const v = e.target.value;
266586
- transactDoc(activeTab.value, (t) => mutateUpdateDef(t, name, { contentType: v }));
266587
- }}
266588
- >
266589
- ${typeNames.map((t) => html3`<sp-menu-item value=${t}>${t}</sp-menu-item>`)}
266590
- </sp-picker>
266591
- </div>
266592
- ` : signalFieldRow("Content Type", currentType, (v) => transactDoc(activeTab.value, (t) => mutateUpdateDef(t, name, { contentType: v })));
266593
- let entryFields = nothing;
266594
- if (proto2 === "ContentEntry") {
266595
- const idVal = def3.id;
266596
- const idStr = idVal && typeof idVal === "object" && idVal.$ref ? idVal.$ref : typeof idVal === "string" ? idVal : "";
266597
- entryFields = signalFieldRow("ID", idStr, (v) => {
266598
- const val = v.startsWith("#/") ? { $ref: v } : v;
266599
- transactDoc(activeTab.value, (t) => mutateUpdateDef(t, name, { id: val }));
266600
- });
266601
- } else {
266602
- const filterStr = def3.filter ? JSON.stringify(def3.filter) : "";
266603
- const sortStr = def3.sort ? `${def3.sort.field || ""} ${def3.sort.order || ""}`.trim() : "";
266604
- const limitStr = def3.limit != null ? String(def3.limit) : "";
266605
- entryFields = html3`
266606
- ${signalFieldRow("Filter", filterStr, (v) => {
266607
- try {
266608
- const parsed = v ? JSON.parse(v) : undefined;
266609
- transactDoc(activeTab.value, (t) => mutateUpdateDef(t, name, { filter: parsed }));
266610
- } catch {}
266611
- })}
266612
- ${signalFieldRow("Sort", sortStr, (v) => {
266613
- if (!v) {
266614
- transactDoc(activeTab.value, (t) => mutateUpdateDef(t, name, { sort: undefined }));
266615
- return;
266616
- }
266617
- const [field, order2] = v.split(" ");
266618
- transactDoc(activeTab.value, (t) => mutateUpdateDef(t, name, { sort: { field, order: order2 || "asc" } }));
266619
- })}
266620
- ${signalFieldRow("Limit", limitStr, (v) => {
266621
- const num = v ? parseInt(v, 10) : undefined;
266622
- transactDoc(activeTab.value, (t) => mutateUpdateDef(t, name, { limit: num }));
266623
- })}
266624
- `;
266625
- }
266626
- let schemaFields = nothing;
266627
- if (schema4?.properties) {
266628
- const fields = Object.entries(schema4.properties);
266629
- const required = new Set(schema4.required || []);
266630
- schemaFields = html3`
266631
- <div style="margin-top:8px;padding:6px 0;border-top:1px solid var(--border-dim)">
266632
- <div style="font-size:11px;color:var(--fg-dim);margin-bottom:4px;font-weight:500">
266633
- Fields (${currentType})
266634
- </div>
266635
- ${fields.map(([field, fieldDef]) => html3`
266636
- <div style="font-size:11px;padding:2px 0;display:flex;gap:4px;align-items:center">
266637
- <code style="color:var(--fg-default)">${field}</code>
266638
- <span style="color:var(--fg-dim)">${fieldDef.type || ""}</span>
266639
- ${required.has(field) ? html3`<span style="color:var(--color-accent,#f36f32);font-size:9px">req</span>` : nothing}
266640
- </div>
266641
- `)}
266642
- </div>
266643
- `;
266644
- }
266645
- return html3`
266646
- ${contentTypeField}
266647
- ${signalFieldRow("Prototype", proto2, (v) => transactDoc(activeTab.value, (t) => mutateUpdateDef(t, name, { $prototype: v })))}
266648
- ${entryFields} ${schemaFields}
266649
- `;
266650
- }
266651
266570
  function renderFunctionFields(S, name, def3, textareaRow, ctx) {
266652
266571
  const descriptionField = signalFieldRow("Description", def3.description || "", (v) => transactDoc(activeTab.value, (t) => mutateUpdateDef(t, name, { description: v || undefined })));
266653
266572
  const bodyField = def3.$src ? html3`
@@ -266894,15 +266813,86 @@ function renderEmitsEditorTemplate(S, name, def3) {
266894
266813
  </button>
266895
266814
  `;
266896
266815
  }
266897
- function renderSchemaFieldsTemplate(schema4, def3, name, _S) {
266816
+ function resolveSchemaEnum(enumDef, parentDef) {
266817
+ if (Array.isArray(enumDef))
266818
+ return enumDef;
266819
+ if (enumDef && typeof enumDef === "object") {
266820
+ const ref3 = enumDef.$ref;
266821
+ if (ref3 === "#/$context/contentTypes") {
266822
+ return Object.keys(projectState?.projectConfig?.contentTypes ?? {});
266823
+ }
266824
+ if (typeof ref3 === "string" && ref3.startsWith("#/$context/contentTypes/{@")) {
266825
+ const match2 = ref3.match(/#\/\$context\/contentTypes\/\{@(\w+)\}\/schema\/properties/);
266826
+ if (match2 && parentDef) {
266827
+ const paramName = match2[1];
266828
+ const typeName = parentDef[paramName];
266829
+ if (typeName) {
266830
+ const ct = projectState?.projectConfig?.contentTypes?.[typeName];
266831
+ const schema4 = ct?.schema;
266832
+ const props = schema4?.properties;
266833
+ if (props)
266834
+ return Object.keys(props);
266835
+ }
266836
+ }
266837
+ return;
266838
+ }
266839
+ }
266840
+ if (enumDef === "$contentTypes") {
266841
+ return Object.keys(projectState?.projectConfig?.contentTypes ?? {});
266842
+ }
266843
+ return;
266844
+ }
266845
+ function renderInlineField(key, schema4, value2, onChange, parentDef) {
266846
+ const enumValues = resolveSchemaEnum(schema4.enum, parentDef);
266847
+ if (enumValues) {
266848
+ return html3`<sp-picker
266849
+ size="s"
266850
+ label=${key}
266851
+ value=${value2 !== undefined ? String(value2) : "__none__"}
266852
+ @change=${(e) => onChange(e.target.value === "__none__" ? undefined : e.target.value)}
266853
+ >
266854
+ <sp-menu-item value="__none__">—</sp-menu-item>
266855
+ ${enumValues.map((v) => html3`<sp-menu-item value=${v}>${v}</sp-menu-item>`)}
266856
+ </sp-picker>`;
266857
+ }
266858
+ if (schema4.type === "boolean") {
266859
+ return html3`<sp-switch
266860
+ size="s"
266861
+ ?checked=${!!value2}
266862
+ @change=${(e) => onChange(e.target.checked)}
266863
+ >${key}</sp-switch
266864
+ >`;
266865
+ }
266866
+ if (schema4.type === "integer" || schema4.type === "number") {
266867
+ return html3`<sp-number-field
266868
+ size="s"
266869
+ label=${key}
266870
+ .value=${value2 !== undefined ? value2 : nothing}
266871
+ step=${schema4.type === "integer" ? "1" : nothing}
266872
+ @change=${(e) => {
266873
+ const parsed = schema4.type === "integer" ? parseInt(e.target.value, 10) : parseFloat(e.target.value);
266874
+ onChange(isNaN(parsed) ? undefined : parsed);
266875
+ }}
266876
+ ></sp-number-field>`;
266877
+ }
266878
+ return html3`<sp-textfield
266879
+ size="s"
266880
+ label=${key}
266881
+ placeholder=${key}
266882
+ .value=${value2 ?? ""}
266883
+ @input=${(e) => onChange(e.target.value || undefined)}
266884
+ ></sp-textfield>`;
266885
+ }
266886
+ function renderSchemaFieldsTemplate(schema4, def3, name, _S, ctx = null) {
266898
266887
  if (!schema4?.properties)
266899
266888
  return nothing;
266900
266889
  const required = new Set(schema4.required ?? []);
266901
- return Object.entries(schema4.properties).filter(([prop]) => !STUDIO_RESERVED_KEYS.has(prop)).map(([prop, ps]) => {
266890
+ const propertyFields = Object.entries(schema4.properties).filter(([prop]) => !STUDIO_RESERVED_KEYS.has(prop)).map(([prop, ps]) => {
266902
266891
  const currentValue = def3[prop];
266903
266892
  const labelText = prop + (required.has(prop) ? " *" : "");
266904
266893
  let control;
266905
- if (ps.enum) {
266894
+ const enumValues = resolveSchemaEnum(ps.enum, def3);
266895
+ if (enumValues) {
266906
266896
  control = html3`
266907
266897
  <sp-picker
266908
266898
  size="s"
@@ -266912,7 +266902,7 @@ function renderSchemaFieldsTemplate(schema4, def3, name, _S) {
266912
266902
  }))}
266913
266903
  >
266914
266904
  ${!required.has(prop) ? html3`<sp-menu-item value="__none__">—</sp-menu-item>` : nothing}
266915
- ${ps.enum.map((val) => html3`<sp-menu-item value=${val}>${val}</sp-menu-item>`)}
266905
+ ${enumValues.map((val) => html3`<sp-menu-item value=${val}>${val}</sp-menu-item>`)}
266916
266906
  </sp-picker>
266917
266907
  `;
266918
266908
  } else if (ps.type === "boolean") {
@@ -266930,7 +266920,7 @@ function renderSchemaFieldsTemplate(schema4, def3, name, _S) {
266930
266920
  max=${ifDefined(ps.maximum)}
266931
266921
  step=${ps.type === "integer" ? "1" : nothing}
266932
266922
  .value=${currentValue !== undefined ? currentValue : nothing}
266933
- placeholder=${ps.default !== undefined ? String(ps.default) : nothing}
266923
+ placeholder=${ps.default != null ? String(ps.default) : nothing}
266934
266924
  @change=${(e) => {
266935
266925
  clearTimeout(debounce);
266936
266926
  debounce = setTimeout(() => {
@@ -266979,6 +266969,51 @@ function renderSchemaFieldsTemplate(schema4, def3, name, _S) {
266979
266969
  ></sp-textfield>
266980
266970
  </div>
266981
266971
  `;
266972
+ } else if (ps.type === "array" && ps.items?.type === "object" && ps.items?.properties) {
266973
+ const rows = Array.isArray(currentValue) ? currentValue : [];
266974
+ const itemProps = ps.items.properties;
266975
+ control = html3`
266976
+ <div class="array-object-field">
266977
+ ${rows.map((row, idx) => html3`
266978
+ <div
266979
+ class="array-object-row"
266980
+ style="display:flex;gap:4px;align-items:center;margin-bottom:4px"
266981
+ >
266982
+ ${Object.entries(itemProps).map(([propKey, propSchema]) => renderInlineField(propKey, propSchema, row[propKey], (val) => {
266983
+ const updated = [...rows];
266984
+ updated[idx] = { ...updated[idx], [propKey]: val };
266985
+ transactDoc(activeTab.value, (t) => mutateUpdateDef(t, name, { [prop]: updated }));
266986
+ }, def3))}
266987
+ <sp-action-button
266988
+ quiet
266989
+ size="s"
266990
+ @click=${() => {
266991
+ const updated = rows.filter((_, i2) => i2 !== idx);
266992
+ transactDoc(activeTab.value, (t) => mutateUpdateDef(t, name, { [prop]: updated.length ? updated : undefined }));
266993
+ ctx?.renderLeftPanel();
266994
+ }}
266995
+ >
266996
+ <sp-icon-delete slot="icon"></sp-icon-delete>
266997
+ </sp-action-button>
266998
+ </div>
266999
+ `)}
267000
+ <sp-action-button
267001
+ quiet
267002
+ size="s"
267003
+ @click=${(e) => {
267004
+ e.stopPropagation();
267005
+ const newRow = {};
267006
+ for (const [k, v] of Object.entries(itemProps)) {
267007
+ if (v.default !== undefined)
267008
+ newRow[k] = v.default;
267009
+ }
267010
+ transactDoc(activeTab.value, (t) => mutateUpdateDef(t, name, { [prop]: [...rows, newRow] }));
267011
+ ctx?.renderLeftPanel();
267012
+ }}
267013
+ >+ Add</sp-action-button
267014
+ >
267015
+ </div>
267016
+ `;
266982
267017
  } else if (ps.type === "array" || ps.type === "object") {
266983
267018
  let debounce;
266984
267019
  control = html3`<sp-textfield
@@ -267021,17 +267056,20 @@ function renderSchemaFieldsTemplate(schema4, def3, name, _S) {
267021
267056
  widget: control
267022
267057
  });
267023
267058
  });
267059
+ return html3`${propertyFields}`;
267024
267060
  }
267025
267061
  function renderExternalPrototypeEditorTemplate(S, name, def3, ctx) {
267026
267062
  let schemaContent = nothing;
267027
- if (def3.$src && def3.$prototype) {
267028
- const cacheKey = `${def3.$src}::${def3.$prototype}`;
267063
+ const importedPath = def3.$prototype ? projectState?.projectConfig?.imports?.[def3.$prototype] : null;
267064
+ const resolvedSrc = def3.$src || importedPath;
267065
+ if (resolvedSrc && def3.$prototype) {
267066
+ const cacheKey = `${resolvedSrc}::${def3.$prototype}`;
267029
267067
  if (pluginSchemaCache.has(cacheKey)) {
267030
267068
  const schema4 = pluginSchemaCache.get(cacheKey);
267031
267069
  if (schema4) {
267032
267070
  schemaContent = html3`
267033
267071
  ${schema4.description ? html3`<div class="signal-hint" style="padding:4px 0 8px">${schema4.description}</div>` : nothing}
267034
- ${renderSchemaFieldsTemplate(schema4, def3, name, S)}
267072
+ ${renderSchemaFieldsTemplate(schema4, def3, name, S, ctx)}
267035
267073
  `;
267036
267074
  }
267037
267075
  } else {
@@ -267047,14 +267085,18 @@ function renderExternalPrototypeEditorTemplate(S, name, def3, ctx) {
267047
267085
  }
267048
267086
  }
267049
267087
  return html3`
267050
- ${signalFieldRow("Source", def3.$src || "", (v) => {
267088
+ ${importedPath ? html3`<div class="signal-hint" style="padding:4px 0 2px;font-size:11px;color:var(--fg-dim)">
267089
+ ${def3.$prototype}
267090
+ </div>` : html3`
267091
+ ${signalFieldRow("Source", def3.$src || "", (v) => {
267051
267092
  transactDoc(activeTab.value, (t) => mutateUpdateDef(t, name, { $src: v || undefined }));
267052
267093
  pluginSchemaCache.delete(`${v}::${def3.$prototype}`);
267053
267094
  })}
267054
- ${signalFieldRow("Prototype", def3.$prototype || "", (v) => {
267095
+ ${signalFieldRow("Prototype", def3.$prototype || "", (v) => {
267055
267096
  transactDoc(activeTab.value, (t) => mutateUpdateDef(t, name, { $prototype: v || undefined }));
267056
267097
  pluginSchemaCache.delete(`${def3.$src}::${v}`);
267057
267098
  })}
267099
+ `}
267058
267100
  ${def3.$export ? signalFieldRow("Export", def3.$export || "", (v) => transactDoc(activeTab.value, (t) => mutateUpdateDef(t, name, { $export: v || undefined }))) : nothing}
267059
267101
  ${schemaContent}
267060
267102
  `;
@@ -268516,6 +268558,7 @@ init_workspace2();
268516
268558
  // src/editor/context-menu.js
268517
268559
  init_lit_html();
268518
268560
  init_ref();
268561
+ init_html_to_jx();
268519
268562
  init_store();
268520
268563
  init_workspace2();
268521
268564
 
@@ -269016,17 +269059,93 @@ function renderLayersTemplate(ctx) {
269016
269059
  }
269017
269060
 
269018
269061
  // src/editor/context-menu.js
269019
- function copyNode() {
269062
+ var JX_MIME = "web application/jx+json";
269063
+ function nodeToHtml(node2) {
269064
+ if (typeof node2 === "string")
269065
+ return node2;
269066
+ const tag3 = node2.tagName || "div";
269067
+ let attrs = "";
269068
+ if (node2.attributes) {
269069
+ for (const [k, v] of Object.entries(node2.attributes)) {
269070
+ attrs += v === "" ? ` ${k}` : ` ${k}="${v.replace(/"/g, "&quot;")}"`;
269071
+ }
269072
+ }
269073
+ if (node2.style) {
269074
+ const css = Object.entries(node2.style).map(([k, v]) => `${k}:${v}`).join(";");
269075
+ if (css)
269076
+ attrs += ` style="${css.replace(/"/g, "&quot;")}"`;
269077
+ }
269078
+ let inner = "";
269079
+ if (node2.textContent) {
269080
+ inner = node2.textContent;
269081
+ } else if (node2.children) {
269082
+ inner = node2.children.map((c) => nodeToHtml(c)).join("");
269083
+ }
269084
+ return `<${tag3}${attrs}>${inner}</${tag3}>`;
269085
+ }
269086
+ async function writeToClipboard(json2) {
269087
+ workspace.clipboard = json2;
269088
+ try {
269089
+ await navigator.clipboard.write([
269090
+ new ClipboardItem({
269091
+ [JX_MIME]: new Blob([JSON.stringify(json2)], { type: JX_MIME }),
269092
+ "text/html": new Blob([nodeToHtml(json2)], { type: "text/html" })
269093
+ })
269094
+ ]);
269095
+ } catch {
269096
+ try {
269097
+ await navigator.clipboard.writeText(JSON.stringify(json2));
269098
+ } catch {}
269099
+ }
269100
+ }
269101
+ async function readFromClipboard() {
269102
+ try {
269103
+ const items = await navigator.clipboard.read();
269104
+ for (const item of items) {
269105
+ if (item.types.includes(JX_MIME)) {
269106
+ const blob = await item.getType(JX_MIME);
269107
+ const json2 = JSON.parse(await blob.text());
269108
+ return [json2];
269109
+ }
269110
+ if (item.types.includes("text/html")) {
269111
+ const blob = await item.getType("text/html");
269112
+ const htmlStr = await blob.text();
269113
+ const nodes = htmlToJx(htmlStr);
269114
+ const jxNodes = nodes.map((n2) => typeof n2 === "string" ? { tagName: "p", textContent: n2 } : n2);
269115
+ if (jxNodes.length > 0)
269116
+ return jxNodes;
269117
+ }
269118
+ if (item.types.includes("text/plain")) {
269119
+ const blob = await item.getType("text/plain");
269120
+ const text6 = await blob.text();
269121
+ try {
269122
+ const parsed = JSON.parse(text6);
269123
+ if (parsed && parsed.tagName)
269124
+ return [parsed];
269125
+ } catch {}
269126
+ if (text6.trim())
269127
+ return [{ tagName: "p", textContent: text6.trim() }];
269128
+ }
269129
+ }
269130
+ } catch {
269131
+ if (workspace.clipboard) {
269132
+ return [JSON.parse(JSON.stringify(workspace.clipboard))];
269133
+ }
269134
+ }
269135
+ return null;
269136
+ }
269137
+ async function copyNode() {
269020
269138
  const tab = activeTab.value;
269021
269139
  if (!tab?.session.selection)
269022
269140
  return;
269023
269141
  const node2 = getNodeAtPath(tab.doc.document, tab.session.selection);
269024
269142
  if (!node2)
269025
269143
  return;
269026
- workspace.clipboard = structuredClone(node2);
269144
+ const json2 = JSON.parse(JSON.stringify(node2));
269145
+ await writeToClipboard(json2);
269027
269146
  statusMessage("Copied");
269028
269147
  }
269029
- function cutNode() {
269148
+ async function cutNode() {
269030
269149
  const tab = activeTab.value;
269031
269150
  if (!tab?.session.selection || tab.session.selection.length < 2)
269032
269151
  return;
@@ -269034,17 +269153,18 @@ function cutNode() {
269034
269153
  const node2 = getNodeAtPath(tab.doc.document, sel);
269035
269154
  if (!node2)
269036
269155
  return;
269037
- workspace.clipboard = structuredClone(node2);
269156
+ const json2 = JSON.parse(JSON.stringify(node2));
269157
+ await writeToClipboard(json2);
269038
269158
  transactDoc(tab, (t) => mutateRemoveNode(t, sel));
269039
269159
  statusMessage("Cut");
269040
269160
  }
269041
- function pasteNode() {
269042
- if (!workspace.clipboard)
269043
- return;
269161
+ async function pasteNode() {
269044
269162
  const tab = activeTab.value;
269045
269163
  if (!tab)
269046
269164
  return;
269047
- const clip = workspace.clipboard;
269165
+ const nodes = await readFromClipboard();
269166
+ if (!nodes || nodes.length === 0)
269167
+ return;
269048
269168
  const pPath = tab.session.selection || [];
269049
269169
  const parent = getNodeAtPath(tab.doc.document, pPath);
269050
269170
  if (!parent)
@@ -269052,10 +269172,18 @@ function pasteNode() {
269052
269172
  if (tab.session.selection && tab.session.selection.length >= 2) {
269053
269173
  const pp = parentElementPath(tab.session.selection);
269054
269174
  const idx = childIndex(tab.session.selection);
269055
- transactDoc(tab, (t) => mutateInsertNode(t, pp, idx + 1, structuredClone(clip)));
269175
+ transactDoc(tab, (t) => {
269176
+ for (let i2 = 0;i2 < nodes.length; i2++) {
269177
+ mutateInsertNode(t, pp, idx + 1 + i2, nodes[i2]);
269178
+ }
269179
+ });
269056
269180
  } else {
269057
269181
  const idx = parent.children ? parent.children.length : 0;
269058
- transactDoc(tab, (t) => mutateInsertNode(t, pPath, idx, structuredClone(clip)));
269182
+ transactDoc(tab, (t) => {
269183
+ for (let i2 = 0;i2 < nodes.length; i2++) {
269184
+ mutateInsertNode(t, pPath, idx + i2, nodes[i2]);
269185
+ }
269186
+ });
269059
269187
  }
269060
269188
  statusMessage("Pasted");
269061
269189
  }
@@ -269159,26 +269287,39 @@ function showContextMenu(e, path2, opts = {}) {
269159
269287
  danger: true
269160
269288
  });
269161
269289
  }
269162
- if (workspace.clipboard) {
269163
- const clip = workspace.clipboard;
269290
+ if (path2.length >= 2) {
269164
269291
  items.push({ label: "—" });
269165
269292
  items.push({
269166
269293
  label: "Paste inside",
269167
- action: () => {
269294
+ action: async () => {
269295
+ const nodes = await readFromClipboard();
269296
+ if (!nodes || nodes.length === 0)
269297
+ return;
269168
269298
  const idx = node2.children ? node2.children.length : 0;
269169
- transactDoc(activeTab.value, (t) => mutateInsertNode(t, path2, idx, structuredClone(clip)));
269299
+ transactDoc(activeTab.value, (t) => {
269300
+ for (let i2 = 0;i2 < nodes.length; i2++) {
269301
+ mutateInsertNode(t, path2, idx + i2, nodes[i2]);
269302
+ }
269303
+ });
269304
+ statusMessage("Pasted");
269305
+ }
269306
+ });
269307
+ items.push({
269308
+ label: "Paste after",
269309
+ action: async () => {
269310
+ const nodes = await readFromClipboard();
269311
+ if (!nodes || nodes.length === 0)
269312
+ return;
269313
+ const pp = parentElementPath(path2);
269314
+ const idx = childIndex(path2);
269315
+ transactDoc(activeTab.value, (t) => {
269316
+ for (let i2 = 0;i2 < nodes.length; i2++) {
269317
+ mutateInsertNode(t, pp, idx + 1 + i2, nodes[i2]);
269318
+ }
269319
+ });
269320
+ statusMessage("Pasted");
269170
269321
  }
269171
269322
  });
269172
- if (path2.length >= 2) {
269173
- items.push({
269174
- label: "Paste after",
269175
- action: () => {
269176
- const pp = parentElementPath(path2);
269177
- const idx = childIndex(path2);
269178
- transactDoc(activeTab.value, (t) => mutateInsertNode(t, pp, idx + 1, structuredClone(clip)));
269179
- }
269180
- });
269181
- }
269182
269323
  }
269183
269324
  let { clientX: x, clientY: y } = e;
269184
269325
  _ctxHandle = renderPopover(html3`<sp-popover
@@ -269266,7 +269407,8 @@ function onMouseMove(e) {
269266
269407
  if (!_ctx8 || !_helper)
269267
269408
  return;
269268
269409
  const { getCanvasMode } = _ctx8;
269269
- if (getCanvasMode() !== "design") {
269410
+ const mode = getCanvasMode();
269411
+ if (mode !== "design" && mode !== "edit") {
269270
269412
  hide2();
269271
269413
  return;
269272
269414
  }
@@ -270414,12 +270556,12 @@ function applyCanvasMediaOverrides(canvasEl, activeBreakpoints) {
270414
270556
  if (!tab)
270415
270557
  return;
270416
270558
  const docMedia = getEffectiveMedia(tab.doc.document.$media || {});
270417
- const validBreakpoints = new Set;
270559
+ const activeConditions = new Set;
270418
270560
  for (const name of activeBreakpoints) {
270419
270561
  if (docMedia[name])
270420
- validBreakpoints.add(name);
270562
+ activeConditions.add(docMedia[name]);
270421
270563
  }
270422
- const overrides = collectMediaOverrides(document.styleSheets, validBreakpoints);
270564
+ const overrides = collectMediaOverrides(document.styleSheets, activeConditions);
270423
270565
  applyOverridesToCanvas(canvasEl, overrides);
270424
270566
  }
270425
270567
  function renderOverlays() {
@@ -271639,6 +271781,21 @@ function cleanupGoogleFontPreconnects(head) {
271639
271781
  }
271640
271782
 
271641
271783
  // src/panels/head-panel.js
271784
+ init_platform3();
271785
+ var layoutEntries = null;
271786
+ async function loadLayoutEntries() {
271787
+ try {
271788
+ const platform3 = getPlatform();
271789
+ const listing = await platform3.listDirectory("layouts");
271790
+ layoutEntries = listing.filter((f) => f.type === "file" && f.name.endsWith(".json")).map((f) => ({
271791
+ name: f.name.replace(/\.json$/, "").replace(/[-_]+/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()),
271792
+ path: `./layouts/${f.name}`
271793
+ }));
271794
+ } catch {
271795
+ layoutEntries = [];
271796
+ }
271797
+ renderOnly("leftPanel");
271798
+ }
271642
271799
  var PAGE_FIELDS = [
271643
271800
  { label: "Description", attr: "name", key: "description" },
271644
271801
  { label: "Viewport", attr: "name", key: "viewport" }
@@ -271776,9 +271933,63 @@ function renderHeadTemplate({ document: doc, applyMutation, renderLeftPanel }) {
271776
271933
  const tab = activeTab.value;
271777
271934
  const isContent = tab?.doc.mode === "content";
271778
271935
  const frontmatterSection = isContent ? renderFrontmatterSection() : nothing;
271936
+ const isPage = tab?.documentPath && projectState?.isSiteProject && (tab.documentPath.startsWith("pages/") || tab.documentPath.startsWith("./pages/"));
271937
+ let layoutSection = nothing;
271938
+ if (isPage) {
271939
+ if (layoutEntries === null) {
271940
+ loadLayoutEntries();
271941
+ } else {
271942
+ const currentLayout = doc.$layout;
271943
+ const defaultLayout = projectState?.projectConfig?.defaults?.layout;
271944
+ const displayValue = currentLayout === false ? "__none__" : currentLayout ? currentLayout : "__default__";
271945
+ const defaultLabel = defaultLayout ? defaultLayout.replace(/^\.\/layouts\//, "").replace(/\.json$/, "").replace(/[-_]+/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()) : "";
271946
+ layoutSection = html3`
271947
+ <div class="imports-section">
271948
+ <div class="imports-section-header">
271949
+ <span class="imports-section-title">Layout</span>
271950
+ </div>
271951
+ <div class="head-section-body">
271952
+ ${renderFieldRow({
271953
+ prop: "layout",
271954
+ label: "Layout",
271955
+ hasValue: currentLayout !== undefined,
271956
+ onClear: () => applyMutation((d2) => {
271957
+ delete d2.$layout;
271958
+ }),
271959
+ widget: html3`
271960
+ <sp-picker
271961
+ size="s"
271962
+ value=${displayValue}
271963
+ @change=${(e) => {
271964
+ const val = e.target.value;
271965
+ applyMutation((d2) => {
271966
+ if (val === "__default__")
271967
+ delete d2.$layout;
271968
+ else if (val === "__none__")
271969
+ d2.$layout = false;
271970
+ else
271971
+ d2.$layout = val;
271972
+ });
271973
+ invalidateLayoutCache();
271974
+ }}
271975
+ >
271976
+ <sp-menu-item value="__default__"
271977
+ >Default${defaultLabel ? ` (${defaultLabel})` : ""}</sp-menu-item
271978
+ >
271979
+ <sp-menu-item value="__none__">None</sp-menu-item>
271980
+ <sp-menu-divider></sp-menu-divider>
271981
+ ${layoutEntries.map((l) => html3`<sp-menu-item value=${l.path}>${l.name}</sp-menu-item>`)}
271982
+ </sp-picker>
271983
+ `
271984
+ })}
271985
+ </div>
271986
+ </div>
271987
+ `;
271988
+ }
271989
+ }
271779
271990
  return html3`
271780
271991
  <div class="imports-panel">
271781
- ${frontmatterSection}
271992
+ ${frontmatterSection} ${layoutSection}
271782
271993
 
271783
271994
  <!-- Page section -->
271784
271995
  <div class="imports-section">
@@ -299628,7 +299839,7 @@ function renderHeadEditor(container) {
299628
299839
  } else if (tag5 === "script") {
299629
299840
  attrs.src = "";
299630
299841
  } else if (tag5 === "style") {
299631
- entry.content = "";
299842
+ entry.textContent = "";
299632
299843
  }
299633
299844
  headEntries.push(entry);
299634
299845
  save();
@@ -299642,7 +299853,7 @@ function renderHeadEditor(container) {
299642
299853
  const updateEntry = (idx, key, val) => {
299643
299854
  const entry = headEntries[idx];
299644
299855
  if (key === "content" && (entry.tagName === "script" || entry.tagName === "style")) {
299645
- entry.content = val;
299856
+ entry.textContent = val;
299646
299857
  } else {
299647
299858
  if (!entry.attributes)
299648
299859
  entry.attributes = {};
@@ -299752,7 +299963,7 @@ function renderEntryFields(entry, idx, updateEntry) {
299752
299963
  <label class="settings-field-label">Script body</label>
299753
299964
  <textarea
299754
299965
  class="head-code-editor"
299755
- .value=${entry.content || ""}
299966
+ .value=${entry.textContent || ""}
299756
299967
  @input=${onFieldChange("content")}
299757
299968
  rows="6"
299758
299969
  spellcheck="false"
@@ -299766,7 +299977,7 @@ function renderEntryFields(entry, idx, updateEntry) {
299766
299977
  <label class="settings-field-label">Style body</label>
299767
299978
  <textarea
299768
299979
  class="head-code-editor"
299769
- .value=${entry.content || ""}
299980
+ .value=${entry.textContent || ""}
299770
299981
  @input=${onFieldChange("content")}
299771
299982
  rows="8"
299772
299983
  spellcheck="false"
@@ -304057,6 +304268,47 @@ function styleSidebarTemplate(node2, activeMediaTab, activeSelector, effectiveSt
304057
304268
  }
304058
304269
  return false;
304059
304270
  });
304271
+ const isOpen2 = isFiltering ? true : tab.session.ui.styleSections[sec.key] ?? false;
304272
+ if (!isOpen2) {
304273
+ return html3`
304274
+ <sp-accordion-item
304275
+ label=${sec.label}
304276
+ .open=${false}
304277
+ @sp-accordion-item-toggle=${(e20) => {
304278
+ activeTab.value.session.ui.styleSections = {
304279
+ ...activeTab.value.session.ui.styleSections,
304280
+ [sec.key]: e20.target.open
304281
+ };
304282
+ }}
304283
+ >
304284
+ ${sectionActiveProps.length > 0 ? html3`
304285
+ <span slot="heading" style="display:flex;align-items:center;gap:6px">
304286
+ ${sec.label}
304287
+ <span
304288
+ class="set-dot set-dot--section"
304289
+ title="Clear all ${sec.label.toLowerCase()} properties"
304290
+ @click=${(e20) => {
304291
+ e20.stopPropagation();
304292
+ e20.preventDefault();
304293
+ transactDoc(activeTab.value, (t15) => {
304294
+ for (const { prop, entry } of sectionActiveProps) {
304295
+ if (activeStyle[prop] !== undefined)
304296
+ commitMutate(t15, prop, undefined);
304297
+ if (inferInputType(entry) === "shorthand") {
304298
+ for (const l of getLonghands(prop)) {
304299
+ if (activeStyle[l.name] !== undefined)
304300
+ commitMutate(t15, l.name, undefined);
304301
+ }
304302
+ }
304303
+ }
304304
+ });
304305
+ }}
304306
+ ></span>
304307
+ </span>
304308
+ ` : nothing}
304309
+ </sp-accordion-item>
304310
+ `;
304311
+ }
304060
304312
  const rows = [];
304061
304313
  for (const { prop, entry } of entries2) {
304062
304314
  const val = activeStyle[prop];
@@ -304094,7 +304346,6 @@ function styleSidebarTemplate(node2, activeMediaTab, activeSelector, effectiveSt
304094
304346
  }
304095
304347
  if (isFiltering && rows.length === 0)
304096
304348
  return nothing;
304097
- const isOpen2 = isFiltering ? true : tab.session.ui.styleSections[sec.key] ?? false;
304098
304349
  return html3`
304099
304350
  <sp-accordion-item
304100
304351
  label=${sec.label}
@@ -305153,17 +305404,17 @@ function mediaBreakpointRowTemplate(name, query3) {
305153
305404
  </div>
305154
305405
  `;
305155
305406
  }
305156
- var layoutEntries = null;
305157
- async function loadLayoutEntries() {
305407
+ var layoutEntries2 = null;
305408
+ async function loadLayoutEntries2() {
305158
305409
  try {
305159
305410
  const platform4 = getPlatform();
305160
305411
  const listing = await platform4.listDirectory("layouts");
305161
- layoutEntries = listing.filter((f) => f.type === "file" && f.name.endsWith(".json")).map((f) => ({
305412
+ layoutEntries2 = listing.filter((f) => f.type === "file" && f.name.endsWith(".json")).map((f) => ({
305162
305413
  name: f.name.replace(/\.json$/, ""),
305163
305414
  path: `./layouts/${f.name}`
305164
305415
  }));
305165
305416
  } catch {
305166
- layoutEntries = [];
305417
+ layoutEntries2 = [];
305167
305418
  }
305168
305419
  renderOnly("rightPanel");
305169
305420
  }
@@ -305176,8 +305427,8 @@ function renderPageSection(node2) {
305176
305427
  const tab = activeTab.value;
305177
305428
  if (!isPageDocument(tab.documentPath))
305178
305429
  return nothing;
305179
- if (layoutEntries === null) {
305180
- loadLayoutEntries();
305430
+ if (layoutEntries2 === null) {
305431
+ loadLayoutEntries2();
305181
305432
  return nothing;
305182
305433
  }
305183
305434
  const currentLayout = node2.$layout;
@@ -305219,7 +305470,7 @@ function renderPageSection(node2) {
305219
305470
  >
305220
305471
  <sp-menu-item value="__none__">None</sp-menu-item>
305221
305472
  <sp-menu-divider></sp-menu-divider>
305222
- ${layoutEntries.map((l) => html3`<sp-menu-item value=${l.path}>${l.name}</sp-menu-item>`)}
305473
+ ${layoutEntries2.map((l) => html3`<sp-menu-item value=${l.path}>${l.name}</sp-menu-item>`)}
305223
305474
  </sp-picker>
305224
305475
  </div>
305225
305476
  ${effectivePath ? html3`<div style="font-size:10px;color:var(--fg-dim);padding:2px 0;font-style:italic">
@@ -306640,8 +306891,20 @@ var _scope5 = null;
306640
306891
  var _rendering = false;
306641
306892
  var _scheduled2 = false;
306642
306893
  var _hasFocus = false;
306643
- function _onFocusIn() {
306644
- _hasFocus = true;
306894
+ function _isTextInput(el) {
306895
+ if (!el)
306896
+ return false;
306897
+ const tag5 = el.tagName.toLowerCase();
306898
+ if (tag5 === "input" || tag5 === "textarea")
306899
+ return true;
306900
+ if (tag5 === "sp-textfield" || tag5 === "sp-number-field" || tag5 === "sp-search")
306901
+ return true;
306902
+ if (el.shadowRoot?.activeElement)
306903
+ return _isTextInput(el.shadowRoot.activeElement);
306904
+ return false;
306905
+ }
306906
+ function _onFocusIn(e21) {
306907
+ _hasFocus = _isTextInput(e21.target);
306645
306908
  }
306646
306909
  function _onFocusOut() {
306647
306910
  _hasFocus = false;
@@ -306685,6 +306948,24 @@ function render6() {
306685
306948
  queueMicrotask(_flush2);
306686
306949
  }
306687
306950
  }
306951
+ var _propsContainer = null;
306952
+ var _eventsContainer = null;
306953
+ var _styleContainer = null;
306954
+ var _assistantContainer = null;
306955
+ var _lastTab = null;
306956
+ function _ensureContainers() {
306957
+ if (_propsContainer)
306958
+ return;
306959
+ _propsContainer = document.createElement("div");
306960
+ _propsContainer.className = "panel-body";
306961
+ _eventsContainer = document.createElement("div");
306962
+ _eventsContainer.className = "panel-body";
306963
+ _styleContainer = document.createElement("div");
306964
+ _styleContainer.className = "panel-body";
306965
+ _assistantContainer = document.createElement("div");
306966
+ _assistantContainer.className = "panel-body";
306967
+ _assistantContainer.style.cssText = "display:flex;flex-direction:column;overflow:hidden";
306968
+ }
306688
306969
  function _flush2() {
306689
306970
  _scheduled2 = false;
306690
306971
  if (!_ctx13)
@@ -306693,86 +306974,88 @@ function _flush2() {
306693
306974
  return;
306694
306975
  _rendering = true;
306695
306976
  try {
306696
- render2(rightPanelTemplate(), rightPanel);
306697
- } catch (e21) {
306698
- console.error("right-panel render error:", e21);
306699
- try {
306977
+ const ctx = _ctx13;
306978
+ const aTab = activeTab.value;
306979
+ if (!aTab) {
306700
306980
  rightPanel.textContent = "";
306701
- delete rightPanel["_$litPart$"];
306702
- render2(rightPanelTemplate(), rightPanel);
306703
- } catch (e22) {
306704
- console.error("right-panel retry failed:", e22);
306981
+ return;
306982
+ }
306983
+ const S = {
306984
+ ui: aTab.session.ui,
306985
+ document: aTab.doc.document,
306986
+ mode: aTab.doc.mode,
306987
+ selection: aTab.session.selection
306988
+ };
306989
+ const tab = S.ui.rightTab;
306990
+ const panelTabs = [
306991
+ { value: "properties", icon: "sp-icon-properties", label: "Properties" },
306992
+ { value: "events", icon: "sp-icon-event", label: "Events" },
306993
+ { value: "style", icon: "sp-icon-brush", label: "Style" },
306994
+ { value: "assistant", icon: "sp-icon-chat", label: "Assistant" }
306995
+ ];
306996
+ const tabsT = html3`
306997
+ <div class="panel-tabs">
306998
+ <sp-tabs
306999
+ selected=${tab}
307000
+ quiet
307001
+ @change=${(e21) => {
307002
+ const sel = e21.target.selected;
307003
+ if (sel && sel !== tab) {
307004
+ updateUi("rightTab", sel);
307005
+ render6();
307006
+ }
307007
+ }}
307008
+ >
307009
+ ${panelTabs.map((t16) => html3`
307010
+ <sp-tab value=${t16.value} title=${t16.label} aria-label=${t16.label}>
307011
+ ${tabIcon(t16.icon, "xs")}
307012
+ </sp-tab>
307013
+ `)}
307014
+ </sp-tabs>
307015
+ </div>
307016
+ `;
307017
+ _ensureContainers();
307018
+ const containers = [
307019
+ _propsContainer,
307020
+ _eventsContainer,
307021
+ _styleContainer,
307022
+ _assistantContainer
307023
+ ];
307024
+ const tabKeys = ["properties", "events", "style", "assistant"];
307025
+ for (let i8 = 0;i8 < containers.length; i8++) {
307026
+ if (tabKeys[i8] === tab) {
307027
+ containers[i8].style.display = tabKeys[i8] === "assistant" ? "flex" : "";
307028
+ } else {
307029
+ containers[i8].style.display = "none";
307030
+ }
307031
+ }
307032
+ render2(tabsT, rightPanel);
307033
+ for (const c7 of containers) {
307034
+ if (!c7.parentNode)
307035
+ rightPanel.appendChild(c7);
306705
307036
  }
307037
+ if (tab === "properties") {
307038
+ render2(renderPropertiesPanelTemplate({ navigateToComponent: ctx.navigateToComponent }), _propsContainer);
307039
+ } else if (tab === "events") {
307040
+ render2(eventsSidebarTemplate({ isCustomElementDoc: () => isCustomElementDoc(S) }), _eventsContainer);
307041
+ } else if (tab === "style") {
307042
+ try {
307043
+ render2(renderStylePanelTemplate({ getCanvasMode: ctx.getCanvasMode }), _styleContainer);
307044
+ } catch (e21) {
307045
+ console.error("[renderStylePanelTemplate]", e21);
307046
+ }
307047
+ } else if (tab === "assistant") {
307048
+ render2(renderAiPanelTemplate(), _assistantContainer);
307049
+ }
307050
+ _lastTab = tab;
307051
+ } catch (e21) {
307052
+ console.error("right-panel render error:", e21);
306706
307053
  } finally {
306707
307054
  _rendering = false;
306708
307055
  }
306709
307056
  requestAnimationFrame(() => mountQuikChat());
306710
307057
  _ctx13.updateForcedPseudoPreview();
306711
307058
  }
306712
- function rightPanelTemplate() {
306713
- const ctx = _ctx13;
306714
- const aTab = activeTab.value;
306715
- if (!aTab)
306716
- return nothing;
306717
- const S = {
306718
- ui: aTab.session.ui,
306719
- document: aTab.doc.document,
306720
- mode: aTab.doc.mode,
306721
- selection: aTab.session.selection
306722
- };
306723
- const tab = S.ui.rightTab;
306724
- const panelTabs = [
306725
- { value: "properties", icon: "sp-icon-properties", label: "Properties" },
306726
- { value: "events", icon: "sp-icon-event", label: "Events" },
306727
- { value: "style", icon: "sp-icon-brush", label: "Style" },
306728
- { value: "assistant", icon: "sp-icon-chat", label: "Assistant" }
306729
- ];
306730
- const tabsT = html3`
306731
- <div class="panel-tabs">
306732
- <sp-tabs
306733
- selected=${tab}
306734
- quiet
306735
- @change=${(e21) => {
306736
- const sel = e21.target.selected;
306737
- if (sel && sel !== tab) {
306738
- updateUi("rightTab", sel);
306739
- }
306740
- }}
306741
- >
306742
- ${panelTabs.map((t16) => html3`
306743
- <sp-tab value=${t16.value} title=${t16.label} aria-label=${t16.label}>
306744
- ${tabIcon(t16.icon, "xs")}
306745
- </sp-tab>
306746
- `)}
306747
- </sp-tabs>
306748
- </div>
306749
- `;
306750
- let bodyT = nothing;
306751
- if (tab === "properties") {
306752
- bodyT = renderPropertiesPanelTemplate({ navigateToComponent: ctx.navigateToComponent });
306753
- } else if (tab === "events") {
306754
- bodyT = eventsSidebarTemplate({
306755
- isCustomElementDoc: () => isCustomElementDoc(S)
306756
- });
306757
- } else if (tab === "style") {
306758
- try {
306759
- bodyT = renderStylePanelTemplate({ getCanvasMode: ctx.getCanvasMode });
306760
- } catch (e21) {
306761
- console.error("[renderStylePanelTemplate]", e21);
306762
- }
306763
- } else if (tab === "assistant") {
306764
- bodyT = renderAiPanelTemplate();
306765
- }
306766
- return html3`
306767
- ${tabsT}
306768
- <div
306769
- class="panel-body"
306770
- style=${tab === "assistant" ? "display:flex;flex-direction:column;overflow:hidden" : ""}
306771
- >
306772
- ${bodyT}
306773
- </div>
306774
- `;
306775
- }
306776
307059
 
306777
307060
  // src/panels/left-panel.js
306778
307061
  init_lit_html();
@@ -307749,5 +308032,5 @@ effect(() => {
307749
308032
  scheduleAutosave();
307750
308033
  });
307751
308034
 
307752
- //# debugId=F13607B73179696E64756E2164756E21
308035
+ //# debugId=1F44B1DD74CD205664756E2164756E21
307753
308036
  //# sourceMappingURL=studio.js.map