@jxsuite/studio 2.4.0 → 2.4.2

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 (42) hide show
  1. package/dist/chunks/{content-source-jrvn5vwy.js → content-source-k9724vcv.js} +8 -8
  2. package/dist/chunks/{convert-to-repeater-109dwcj7.js → convert-to-repeater-k10dvcc7.js} +7 -7
  3. package/dist/chunks/{extension-sections-2zjyty05.js → extension-sections-0ada0xcw.js} +11 -11
  4. package/dist/chunks/{files-jdbtammm.js → files-yew27sn9.js} +12 -12
  5. package/dist/chunks/{format-host-kq6963gs.js → format-host-wewba4nq.js} +2 -2
  6. package/dist/chunks/{git-panel-2stf3ahg.js → git-panel-2dh1aw50.js} +7 -7
  7. package/dist/chunks/{layers-panel-a8wjsdjj.js → layers-panel-qnsghwwd.js} +10 -10
  8. package/dist/chunks/{publish-panel-2we6smjv.js → publish-panel-a0pdft5w.js} +5 -5
  9. package/dist/chunks/{quick-search-hmpwdgys.js → quick-search-9avyc4pc.js} +13 -13
  10. package/dist/chunks/{right-panel-hn3y3j0j.js → right-panel-kcnhtc5j.js} +9 -9
  11. package/dist/chunks/{studio-0evba9z5.js → studio-0b6fs05q.js} +5 -5
  12. package/dist/chunks/{studio-wxthr2x1.js → studio-26sv44br.js} +8 -8
  13. package/dist/chunks/{studio-eqyybj2e.js → studio-28xc8ws4.js} +4 -4
  14. package/dist/chunks/{studio-53dn6e6t.js → studio-aweyr4v1.js} +2 -2
  15. package/dist/chunks/{studio-102jzb8r.js → studio-cz2j1c0g.js} +4 -4
  16. package/dist/chunks/{studio-tqr02xaa.js → studio-dgj7gq44.js} +3 -3
  17. package/dist/chunks/{studio-ky68mp85.js → studio-ef8t53vc.js} +3 -3
  18. package/dist/chunks/{studio-8x4399kc.js → studio-f5yyvqxv.js} +7 -7
  19. package/dist/chunks/{studio-rdsvb808.js → studio-hb7c46jy.js} +5 -5
  20. package/dist/chunks/{studio-yz06mkfj.js → studio-hyn3vhwr.js} +5 -5
  21. package/dist/chunks/{studio-qd4k0h80.js → studio-jnakhg87.js} +4 -4
  22. package/dist/chunks/{studio-q07vj7p9.js → studio-ke244pam.js} +6 -6
  23. package/dist/chunks/{studio-yk7037sg.js → studio-pyrw8h0w.js} +60 -19
  24. package/dist/chunks/{studio-ddgrc5hs.js → studio-ra7ar50f.js} +3 -3
  25. package/dist/chunks/{studio-6gcrk1bq.js → studio-rqemfhz6.js} +2 -2
  26. package/dist/chunks/{studio-kwavhmdr.js → studio-sccge2wf.js} +2 -2
  27. package/dist/chunks/{studio-ng5gbxkj.js → studio-v1va0vhd.js} +8 -8
  28. package/dist/chunks/{studio-1dgqagzz.js → studio-v2c5pmjj.js} +10 -10
  29. package/dist/chunks/{studio-377bbhtz.js → studio-ws6ef6js.js} +8 -8
  30. package/dist/iframe-entry.js +99 -7
  31. package/dist/studio.js +31 -31
  32. package/package.json +4 -4
  33. package/src/canvas/canvas-origin.ts +14 -2
  34. package/src/canvas/editable-actions.ts +37 -1
  35. package/src/canvas/iframe-editable-root.ts +27 -0
  36. package/src/canvas/iframe-host.ts +118 -7
  37. package/src/canvas/iframe-inline-edit.ts +122 -6
  38. package/src/canvas/iframe-protocol.ts +7 -0
  39. package/src/canvas/iframe-render.ts +38 -1
  40. package/src/editor/inline-edit-apply.ts +10 -4
  41. package/src/editor/inline-edit.ts +62 -4
  42. package/src/types.ts +3 -1
@@ -3480,6 +3480,16 @@ function getPath2(obj, path) {
3480
3480
  return readPath(obj, path);
3481
3481
  }
3482
3482
  var _privatePropWarned = new Set;
3483
+ function instanceSupplies(el, key) {
3484
+ if (Object.hasOwn(el, key)) {
3485
+ return true;
3486
+ }
3487
+ if (el.hasAttribute(key)) {
3488
+ return true;
3489
+ }
3490
+ const kebab = key.replaceAll(/[A-Z]/gu, (c) => `-${c.toLowerCase()}`);
3491
+ return kebab !== key && el.hasAttribute(kebab);
3492
+ }
3483
3493
  function refusePrivateProp(key, where) {
3484
3494
  if (!isPrivateStateKey(key)) {
3485
3495
  return false;
@@ -3630,7 +3640,7 @@ async function defineElement(source, baseUrl) {
3630
3640
  if (isPrivateStateKey(key)) {
3631
3641
  continue;
3632
3642
  }
3633
- if (key in this && this[key] !== undefined) {
3643
+ if (key in this && this[key] !== undefined && instanceSupplies(this, key)) {
3634
3644
  state[key] = this[key];
3635
3645
  }
3636
3646
  }
@@ -4199,6 +4209,8 @@ function injectHead(doc) {
4199
4209
  }
4200
4210
  }
4201
4211
  function makeStamper(ctx) {
4212
+ const islands = [];
4213
+ const insideIsland = (path) => islands.some((island) => path.length > island.length && island.every((seg, i) => path[i] === seg));
4202
4214
  return (created, path, def2) => {
4203
4215
  if (!(created instanceof HTMLElement)) {
4204
4216
  return;
@@ -4207,8 +4219,10 @@ function makeStamper(ctx) {
4207
4219
  if (isDefinitionRoot) {
4208
4220
  created.dataset.jxDefinitionRoot = "";
4209
4221
  }
4210
- if (!isDefinitionRoot && created.tagName.includes("-") && isEditableMode(ctx.canvasMode)) {
4222
+ const isIsland = !isDefinitionRoot && created.tagName.includes("-") && isEditableMode(ctx.canvasMode);
4223
+ if (isIsland) {
4211
4224
  created.contentEditable = "false";
4225
+ islands.push(path);
4212
4226
  }
4213
4227
  const classified = classifyRenderNode(path, def2, ctx);
4214
4228
  if (classified.kind === "layout") {
@@ -4225,6 +4239,9 @@ function makeStamper(ctx) {
4225
4239
  return;
4226
4240
  }
4227
4241
  created.dataset.jxPath = serializeJxPath(classified.path);
4242
+ if (!isIsland && isEditableMode(ctx.canvasMode) && insideIsland(path)) {
4243
+ created.contentEditable = "true";
4244
+ }
4228
4245
  };
4229
4246
  }
4230
4247
  function installCanvasImageRetry(root, maxAttempts = 3) {
@@ -5188,6 +5205,7 @@ var insertFn = null;
5188
5205
  var endFn = null;
5189
5206
  var _plainMode = false;
5190
5207
  var _plainOriginal = "";
5208
+ var _plainPosted = false;
5191
5209
  var editableVerdicts = null;
5192
5210
  function setEditableVerdicts(verdicts) {
5193
5211
  editableVerdicts = verdicts;
@@ -5207,6 +5225,7 @@ function startEditing(el, path, callbacks, opts) {
5207
5225
  endFn = callbacks.onEnd;
5208
5226
  _plainMode = opts?.plainText === true;
5209
5227
  _plainOriginal = _plainMode ? el.textContent ?? "" : "";
5228
+ _plainPosted = false;
5210
5229
  el.dataset.jxActiveBlock = "";
5211
5230
  if (_plainMode) {
5212
5231
  try {
@@ -5240,6 +5259,7 @@ function stopEditing(silent = false) {
5240
5259
  insertFn = null;
5241
5260
  _plainMode = false;
5242
5261
  _plainOriginal = "";
5262
+ _plainPosted = false;
5243
5263
  if (silent) {
5244
5264
  endFn = null;
5245
5265
  } else if (endFn) {
@@ -5251,6 +5271,9 @@ function stopEditing(silent = false) {
5251
5271
  function commitActiveBlock() {
5252
5272
  commitChanges(true);
5253
5273
  }
5274
+ function isPlainSession() {
5275
+ return _plainMode && activeEl !== null;
5276
+ }
5254
5277
  function isEditing() {
5255
5278
  return activeEl !== null;
5256
5279
  }
@@ -5336,6 +5359,7 @@ function releaseWithoutCommit() {
5336
5359
  insertFn = null;
5337
5360
  _plainMode = false;
5338
5361
  _plainOriginal = "";
5362
+ _plainPosted = false;
5339
5363
  if (endFn) {
5340
5364
  const fn = endFn;
5341
5365
  endFn = null;
@@ -5347,8 +5371,14 @@ function commitChanges(inPlace) {
5347
5371
  return;
5348
5372
  }
5349
5373
  if (_plainMode) {
5350
- commitFn(activePath, null, (activeEl.textContent ?? "").replaceAll(`
5351
- `, " "), inPlace);
5374
+ const text = (activeEl.textContent ?? "").replaceAll(`
5375
+ `, " ");
5376
+ if (text === _plainOriginal.replaceAll(`
5377
+ `, " ") && !_plainPosted) {
5378
+ return;
5379
+ }
5380
+ _plainPosted = true;
5381
+ commitFn(activePath, null, text, inPlace);
5352
5382
  return;
5353
5383
  }
5354
5384
  normalizeInlineContent(activeEl);
@@ -5471,6 +5501,9 @@ function openSlashMenu(opts) {
5471
5501
  if (!activeEl || !insertFn || !activePath) {
5472
5502
  return;
5473
5503
  }
5504
+ if (_plainMode) {
5505
+ return;
5506
+ }
5474
5507
  const sel = window.getSelection();
5475
5508
  if (!sel || !sel.rangeCount) {
5476
5509
  return;
@@ -5986,6 +6019,7 @@ var INSERT_TEXT = new Set([
5986
6019
  "insertTranspose"
5987
6020
  ]);
5988
6021
  var NATIVE_FORMAT_PREFIX = "format";
6022
+ var PROP_STRUCTURAL = new Set(["insertParagraph", "insertLineBreak"]);
5989
6023
  function classifyBeforeInput(ctx) {
5990
6024
  const { inputType, from, to } = ctx;
5991
6025
  if (inputType === "insertCompositionText" || inputType === "insertFromComposition") {
@@ -6000,6 +6034,9 @@ function classifyBeforeInput(ctx) {
6000
6034
  if (inputType === "insertFromDrop" || inputType === "deleteByDrag") {
6001
6035
  return { kind: "reject" };
6002
6036
  }
6037
+ if (ctx.inPropHost) {
6038
+ return PROP_STRUCTURAL.has(inputType) ? { kind: "reject" } : { kind: "native" };
6039
+ }
6003
6040
  if (!from || !to) {
6004
6041
  return { kind: "reject" };
6005
6042
  }
@@ -6107,6 +6144,11 @@ function startEditableRoot(container, deps) {
6107
6144
  composing = false;
6108
6145
  scheduleCommitTick();
6109
6146
  };
6147
+ const forget = () => {
6148
+ cancelCommitTick();
6149
+ activeEl2 = null;
6150
+ activeKey = null;
6151
+ };
6110
6152
  const deactivate = () => {
6111
6153
  if (!activeEl2) {
6112
6154
  return;
@@ -6181,6 +6223,7 @@ function startEditableRoot(container, deps) {
6181
6223
  atBlockStart: resolved ? isAtBlockStart(resolved.from) : false,
6182
6224
  data: e.data ?? "",
6183
6225
  from: resolved?.from ?? null,
6226
+ inPropHost: activeEl2 !== null && activeKey === null,
6184
6227
  inputType: e.inputType,
6185
6228
  to: resolved?.to ?? null
6186
6229
  });
@@ -6251,6 +6294,7 @@ function startEditableRoot(container, deps) {
6251
6294
  doc.addEventListener("pointerdown", onPointerDownCapture, true);
6252
6295
  return {
6253
6296
  capture: () => captureDocSelection(container, deps.isEditableBlock),
6297
+ forget,
6254
6298
  isComposing: () => composing,
6255
6299
  flush: () => {
6256
6300
  if (composing) {
@@ -6620,6 +6664,39 @@ function ownerInstanceOf(el) {
6620
6664
  }
6621
6665
  return null;
6622
6666
  }
6667
+ function deliveredOutsideProps(node, prop) {
6668
+ if (!node) {
6669
+ return false;
6670
+ }
6671
+ const attrs = node.attributes;
6672
+ if (attrs) {
6673
+ if (attrs[`props.${prop}`] !== undefined) {
6674
+ return true;
6675
+ }
6676
+ const reflected = new Set([prop.toLowerCase(), camelToKebab(prop).toLowerCase()]);
6677
+ for (const [name, val] of Object.entries(attrs)) {
6678
+ if (val !== undefined && reflected.has(name.toLowerCase())) {
6679
+ return true;
6680
+ }
6681
+ }
6682
+ const payload = attrs["data-jx-props"];
6683
+ if (typeof payload === "string" && payloadNames(payload, prop)) {
6684
+ return true;
6685
+ }
6686
+ }
6687
+ if (RESERVED_KEYS.has(prop) || prop.startsWith("$")) {
6688
+ return false;
6689
+ }
6690
+ return node[prop] !== undefined;
6691
+ }
6692
+ function payloadNames(payload, prop) {
6693
+ try {
6694
+ const parsed = JSON.parse(payload);
6695
+ return typeof parsed === "object" && parsed !== null && prop in parsed;
6696
+ } catch {
6697
+ return false;
6698
+ }
6699
+ }
6623
6700
  function elementForPath2(container, path) {
6624
6701
  const serialized = serializeJxPath(path);
6625
6702
  const esc = serialized.replaceAll("\\", String.raw`\\`).replaceAll("'", String.raw`\'`);
@@ -6746,14 +6823,17 @@ function startIframeInlineEdit(channel, container, opts) {
6746
6823
  return true;
6747
6824
  }
6748
6825
  const node = getNodeAtPath(shadowDoc, hostPath);
6826
+ if (deliveredOutsideProps(node, prop)) {
6827
+ return false;
6828
+ }
6749
6829
  const raw = node?.$props?.[prop];
6750
6830
  if (raw == null) {
6751
6831
  return true;
6752
6832
  }
6753
- if (typeof raw === "object") {
6833
+ if (typeof raw !== "string") {
6754
6834
  return false;
6755
6835
  }
6756
- return !(typeof raw === "string" && raw.includes("${"));
6836
+ return !raw.includes("${");
6757
6837
  };
6758
6838
  const enterPropEditAt = (el, hostPath, prop) => {
6759
6839
  channel.post({ kind: "editStart", path: hostPath, prop });
@@ -6768,6 +6848,7 @@ function startIframeInlineEdit(channel, container, opts) {
6768
6848
  onEnd: () => {
6769
6849
  clearHighlight();
6770
6850
  lastNonEmptyRange = null;
6851
+ root.forget();
6771
6852
  channel.post({ kind: "editEnd" });
6772
6853
  },
6773
6854
  onInsert: () => {},
@@ -6779,6 +6860,9 @@ function startIframeInlineEdit(channel, container, opts) {
6779
6860
  if (!el) {
6780
6861
  return;
6781
6862
  }
6863
+ if (isPlainSession()) {
6864
+ return;
6865
+ }
6782
6866
  if (lastNonEmptyRange && lastNonEmptyRange.startContainer.isConnected && el.contains(lastNonEmptyRange.commonAncestorContainer)) {
6783
6867
  const sel = window.getSelection();
6784
6868
  el.focus();
@@ -6893,6 +6977,14 @@ function startIframeInlineEdit(channel, container, opts) {
6893
6977
  if (!editingAllowed()) {
6894
6978
  return;
6895
6979
  }
6980
+ if (msg.prop !== undefined) {
6981
+ const instance = elementForPath2(container, msg.path);
6982
+ const marker = instance?.querySelector(`[data-jx-bound-prop="${CSS.escape(msg.prop)}"]`);
6983
+ if (marker) {
6984
+ activateProp(marker);
6985
+ }
6986
+ return;
6987
+ }
6896
6988
  const el = elementForPath2(container, msg.path);
6897
6989
  if (el && isEditableBlock(el)) {
6898
6990
  root.placeCaret({ offset: msg.offset ?? 0, path: msg.path });
@@ -8170,5 +8262,5 @@ export {
8170
8262
  startCanvasIframe
8171
8263
  };
8172
8264
 
8173
- //# debugId=4B2F6890CDE12A9464756E2164756E21
8265
+ //# debugId=2229405F4A023FB664756E2164756E21
8174
8266
  //# sourceMappingURL=iframe-entry.js.map
package/dist/studio.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  seedPublishConnected
3
- } from "./chunks/studio-tqr02xaa.js";
3
+ } from "./chunks/studio-dgj7gq44.js";
4
4
  import"./chunks/studio-wdt9y92a.js";
5
5
  import {
6
6
  ElementResolutionController,
@@ -67,7 +67,7 @@ import {
67
67
  registerStyleCommands,
68
68
  render as render3,
69
69
  setInspectorTab
70
- } from "./chunks/studio-8x4399kc.js";
70
+ } from "./chunks/studio-f5yyvqxv.js";
71
71
  import {
72
72
  CommandUnavailableError,
73
73
  chordFromEvent,
@@ -99,7 +99,7 @@ import {
99
99
  showSlashMenu,
100
100
  showSlashMenuAtRect,
101
101
  suppressBlockActionBar
102
- } from "./chunks/studio-377bbhtz.js";
102
+ } from "./chunks/studio-ws6ef6js.js";
103
103
  import {
104
104
  addableContexts,
105
105
  defaultCommands,
@@ -111,7 +111,7 @@ import {
111
111
  tokenLabel,
112
112
  transposeStylebookStyle,
113
113
  writeTokenOverride
114
- } from "./chunks/studio-yz06mkfj.js";
114
+ } from "./chunks/studio-hyn3vhwr.js";
115
115
  import {
116
116
  DERIVE_PRESETS,
117
117
  JxColorPopover,
@@ -144,7 +144,7 @@ import {
144
144
  spNumberField,
145
145
  spTextArea,
146
146
  spTextField
147
- } from "./chunks/studio-ng5gbxkj.js";
147
+ } from "./chunks/studio-v1va0vhd.js";
148
148
  import {
149
149
  LitElement,
150
150
  css
@@ -160,7 +160,7 @@ import {
160
160
  import {
161
161
  initQuickSearch,
162
162
  openQuickSearch
163
- } from "./chunks/studio-qd4k0h80.js";
163
+ } from "./chunks/studio-jnakhg87.js";
164
164
  import {
165
165
  addRecentProject,
166
166
  clearRecentProjects,
@@ -187,10 +187,10 @@ import {
187
187
  removeRecentProject,
188
188
  renderFilesTemplate,
189
189
  seedEntry
190
- } from "./chunks/studio-1dgqagzz.js";
190
+ } from "./chunks/studio-v2c5pmjj.js";
191
191
  import {
192
192
  computeWindow
193
- } from "./chunks/studio-kwavhmdr.js";
193
+ } from "./chunks/studio-sccge2wf.js";
194
194
  import {
195
195
  autoSyncProjectOnOpen,
196
196
  cleanupGitPanel,
@@ -211,13 +211,13 @@ import {
211
211
  registerSourceControlCommands,
212
212
  renderGitPanel,
213
213
  stripRange
214
- } from "./chunks/studio-q07vj7p9.js";
214
+ } from "./chunks/studio-ke244pam.js";
215
215
  import {
216
216
  createPanelScheduler
217
217
  } from "./chunks/studio-8jpzqk5s.js";
218
218
  import {
219
219
  selectContributedEntry
220
- } from "./chunks/studio-rdsvb808.js";
220
+ } from "./chunks/studio-hb7c46jy.js";
221
221
  import {
222
222
  notifySettingsDocument,
223
223
  onSettingsDocumentChanged,
@@ -238,7 +238,7 @@ import {
238
238
  renderForm,
239
239
  renderProvenanceChip,
240
240
  resolveContextPointer
241
- } from "./chunks/studio-ky68mp85.js";
241
+ } from "./chunks/studio-ef8t53vc.js";
242
242
  import {
243
243
  validateDoc,
244
244
  validateProjectConfig
@@ -263,7 +263,7 @@ import {
263
263
  registerGridCommands,
264
264
  scanLibrary,
265
265
  uploadDirForCategory
266
- } from "./chunks/studio-0evba9z5.js";
266
+ } from "./chunks/studio-0b6fs05q.js";
267
267
  import {
268
268
  live
269
269
  } from "./chunks/studio-c31yb4t6.js";
@@ -307,7 +307,7 @@ import {
307
307
  setIncludeDrafts,
308
308
  startFsSync,
309
309
  writesForTurn
310
- } from "./chunks/studio-wxthr2x1.js";
310
+ } from "./chunks/studio-26sv44br.js";
311
311
  import"./chunks/studio-2w11grav.js";
312
312
  import"./chunks/studio-bw5dz6km.js";
313
313
  import {
@@ -316,18 +316,18 @@ import {
316
316
  isImage,
317
317
  setMediaChangedHandler,
318
318
  uploadAssets
319
- } from "./chunks/studio-102jzb8r.js";
319
+ } from "./chunks/studio-cz2j1c0g.js";
320
320
  import {
321
321
  beginActivity,
322
322
  registerActivityPanel,
323
323
  showProgressModal
324
- } from "./chunks/studio-eqyybj2e.js";
324
+ } from "./chunks/studio-28xc8ws4.js";
325
325
  import {
326
326
  currentDeploy,
327
327
  latestDeployment,
328
328
  noteDeployment,
329
329
  platformSupportsPublish
330
- } from "./chunks/studio-53dn6e6t.js";
330
+ } from "./chunks/studio-aweyr4v1.js";
331
331
  import {
332
332
  CATEGORIES,
333
333
  SPAN_FULL_RENDER,
@@ -439,7 +439,7 @@ import {
439
439
  timeSpan,
440
440
  timeSpanAsync,
441
441
  updateActivePanelHeaders
442
- } from "./chunks/studio-yk7037sg.js";
442
+ } from "./chunks/studio-pyrw8h0w.js";
443
443
  import {
444
444
  BOTTOM_TAB_IDS,
445
445
  BUFFER_COMMIT,
@@ -606,7 +606,7 @@ import {
606
606
  updateSiteConfig,
607
607
  view,
608
608
  workspace
609
- } from "./chunks/studio-ddgrc5hs.js";
609
+ } from "./chunks/studio-ra7ar50f.js";
610
610
  import {
611
611
  classMap,
612
612
  ifDefined,
@@ -669,7 +669,7 @@ import {
669
669
  mountStillWanted,
670
670
  refreshExtensionUi,
671
671
  refreshFormats
672
- } from "./chunks/studio-6gcrk1bq.js";
672
+ } from "./chunks/studio-rqemfhz6.js";
673
673
  import {
674
674
  getPlatform,
675
675
  hasPlatform,
@@ -16807,7 +16807,7 @@ function entryIdsFor(collection) {
16807
16807
  if (known) {
16808
16808
  return known;
16809
16809
  }
16810
- const pending = import("./chunks/content-source-jrvn5vwy.js").then((m) => m.listCollectionEntryIds(collection));
16810
+ const pending = import("./chunks/content-source-k9724vcv.js").then((m) => m.listCollectionEntryIds(collection));
16811
16811
  entryIdCache.set(collection, pending);
16812
16812
  return pending;
16813
16813
  }
@@ -17068,7 +17068,7 @@ function renderEntryMode(surface, tab) {
17068
17068
  });
17069
17069
  }
17070
17070
  async function openEntryEditor(path) {
17071
- const { openFileInTab: openFileInTab2 } = await import("./chunks/files-jdbtammm.js");
17071
+ const { openFileInTab: openFileInTab2 } = await import("./chunks/files-yew27sn9.js");
17072
17072
  await openFileInTab2(path);
17073
17073
  for (const [id, tab] of workspace.tabs.entries()) {
17074
17074
  if (tab.documentPath !== path) {
@@ -17129,7 +17129,7 @@ async function createEntry(collectionName) {
17129
17129
  });
17130
17130
  return null;
17131
17131
  }
17132
- const { createFileIn: createFileIn2 } = await import("./chunks/files-jdbtammm.js");
17132
+ const { createFileIn: createFileIn2 } = await import("./chunks/files-yew27sn9.js");
17133
17133
  const created = await createFileIn2({
17134
17134
  content,
17135
17135
  dir: collection.dir,
@@ -22349,7 +22349,7 @@ function groupProblems(records = problems) {
22349
22349
  return groups;
22350
22350
  }
22351
22351
  function openProblemPath(path) {
22352
- import("./chunks/files-jdbtammm.js").then((m) => m.openFileInTab(path));
22352
+ import("./chunks/files-yew27sn9.js").then((m) => m.openFileInTab(path));
22353
22353
  }
22354
22354
  function actionTpl(record) {
22355
22355
  const id = record.action;
@@ -58591,9 +58591,9 @@ function renderDefsEditor(container) {
58591
58591
 
58592
58592
  // src/version.ts
58593
58593
  var APP_NAME = "Jx Studio";
58594
- var VERSION = "2.4.0";
58595
- var BUILD_DATE = "2026-08-23T19:11:17.175Z";
58596
- var GIT_COMMIT = "bea72f9";
58594
+ var VERSION = "2.4.2";
58595
+ var BUILD_DATE = "2026-08-24T19:48:51.173Z";
58596
+ var GIT_COMMIT = "7859073";
58597
58597
  var LINKS = {
58598
58598
  github: "https://github.com/jxsuite/jx",
58599
58599
  docs: "https://github.com/jxsuite/jx#readme",
@@ -59670,7 +59670,7 @@ async function openProjectSettings(section) {
59670
59670
  showSettingsDocument();
59671
59671
  notifySettingsDocument();
59672
59672
  try {
59673
- const { syncExtensionSettingsSections } = await import("./chunks/extension-sections-2zjyty05.js");
59673
+ const { syncExtensionSettingsSections } = await import("./chunks/extension-sections-0ada0xcw.js");
59674
59674
  await syncExtensionSettingsSections();
59675
59675
  } catch (error) {
59676
59676
  notify.warn(`Could not load extension settings sections — ${errorMessage(error)}`, {
@@ -60294,7 +60294,7 @@ function publishCommands() {
60294
60294
  name: "set_up_publishing"
60295
60295
  },
60296
60296
  run: async () => {
60297
- const { openPublishPanel } = await import("./chunks/publish-panel-2we6smjv.js");
60297
+ const { openPublishPanel } = await import("./chunks/publish-panel-a0pdft5w.js");
60298
60298
  openPublishPanel();
60299
60299
  },
60300
60300
  title: "Set Up Publishing"
@@ -61016,7 +61016,7 @@ function i18nCommands() {
61016
61016
  });
61017
61017
  return;
61018
61018
  }
61019
- const { openFileInTab: openFileInTab2 } = await import("./chunks/files-jdbtammm.js");
61019
+ const { openFileInTab: openFileInTab2 } = await import("./chunks/files-yew27sn9.js");
61020
61020
  await openFileInTab2(path);
61021
61021
  },
61022
61022
  title: "Open Translation"
@@ -61063,7 +61063,7 @@ function i18nCommands() {
61063
61063
  return;
61064
61064
  }
61065
61065
  const cut = path.lastIndexOf("/");
61066
- const { createFileIn: createFileIn2, openFileInTab: openFileInTab2 } = await import("./chunks/files-jdbtammm.js");
61066
+ const { createFileIn: createFileIn2, openFileInTab: openFileInTab2 } = await import("./chunks/files-yew27sn9.js");
61067
61067
  const created = await createFileIn2({
61068
61068
  content,
61069
61069
  dir: cut === -1 ? "." : path.slice(0, cut),
@@ -61876,5 +61876,5 @@ export {
61876
61876
  hasUnsavedTabs
61877
61877
  };
61878
61878
 
61879
- //# debugId=5769FAD30A86F19564756E2164756E21
61879
+ //# debugId=31462A53C2ABCD7964756E2164756E21
61880
61880
  //# sourceMappingURL=studio.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jxsuite/studio",
3
- "version": "2.4.0",
3
+ "version": "2.4.2",
4
4
  "description": "Jx Studio — visual builder for Jx documents",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -59,11 +59,11 @@
59
59
  "@atlaskit/pragmatic-drag-and-drop-hitbox": "^2.1.0",
60
60
  "@jxsuite/ai": "^0.36.2",
61
61
  "@jxsuite/collab": "^0.8.1",
62
- "@jxsuite/create": "^1.3.4",
63
- "@jxsuite/formulas": "^0.0.10",
62
+ "@jxsuite/create": "^1.3.5",
63
+ "@jxsuite/formulas": "^0.0.12",
64
64
  "@jxsuite/markup": "^0.4.5",
65
65
  "@jxsuite/protocol": "^1.2.0",
66
- "@jxsuite/runtime": "^2.0.3",
66
+ "@jxsuite/runtime": "^2.0.4",
67
67
  "@jxsuite/schema": "^1.8.0",
68
68
  "@spectrum-web-components/accordion": "^1.12.2",
69
69
  "@spectrum-web-components/action-bar": "1.12.2",
@@ -42,12 +42,24 @@ export function canvasBaseOrigin(): string {
42
42
  * @returns {string} A base URL ending in `/`, safe to pass to `new URL(relativePath, base)`.
43
43
  */
44
44
  export function documentBase(projectRoot?: string): string {
45
+ const origin = canvasBaseOrigin();
45
46
  const declared = hasPlatform() ? getPlatform().documentBaseUrl : undefined;
46
47
  if (declared) {
47
- return declared.endsWith("/") ? declared : `${declared}/`;
48
+ const withSlash = declared.endsWith("/") ? declared : `${declared}/`;
49
+ /*
50
+ * ABSOLUTE, ALWAYS. The caller uses this as `new URL(documentPath, base)`, and a `base` that is
51
+ * itself relative throws `Failed to construct 'URL': Invalid base URL` — which is not a failed
52
+ * fetch but a failed MOUNT: the canvas renders nothing at all.
53
+ *
54
+ * Jx Cloud declares a root-relative base (`/api/v1/p/:owner/:repo/:branch/studio/raw/`), which
55
+ * is the natural thing to declare and must keep working. Resolving it against the canvas origin
56
+ * here means a platform may declare either shape. An already-absolute URL passes through
57
+ * unchanged, because that is what `new URL` does with one.
58
+ */
59
+ return new URL(withSlash, `${origin}/`).href;
48
60
  }
49
61
  const root = projectRoot || "";
50
- return `${canvasBaseOrigin()}/${root ? `${root}/` : ""}`;
62
+ return `${origin}/${root ? `${root}/` : ""}`;
51
63
  }
52
64
 
53
65
  /**
@@ -49,6 +49,15 @@ export interface BeforeInputContext {
49
49
  atBlockEnd: boolean;
50
50
  /** The event's `data` (inserted text), empty for deletions. */
51
51
  data: string;
52
+ /**
53
+ * The caret is in a PROP-BOUND nested host — text inside a component instance that is an
54
+ * invertible prop binding (studio.md §8.2.5), not a block of the page document.
55
+ *
56
+ * Such a host has no `data-jx-path` of its own, so `from`/`to` are always null for it. Without
57
+ * this flag the null-position rule below rejected every keystroke: the caret appeared, and
58
+ * nothing typed into a component slot ever landed.
59
+ */
60
+ inPropHost: boolean;
52
61
  }
53
62
 
54
63
  /**
@@ -91,6 +100,20 @@ const INSERT_TEXT = new Set([
91
100
  */
92
101
  const NATIVE_FORMAT_PREFIX = "format";
93
102
 
103
+ /**
104
+ * Intents refused inside a prop-bound host, because a prop value is ONE PLAIN STRING.
105
+ *
106
+ * `docs/studio/editing/writing.md` puts it as "paragraph splits are off": there is no second block
107
+ * for Enter to make, and a `<br>` would serialise into a value that is supposed to be a single
108
+ * line. Everything else — insert, delete, paste, cut, IME — is ordinary text editing on a
109
+ * `plaintext-only` host and runs natively.
110
+ *
111
+ * These are refused rather than re-expressed: Enter FINISHES the edit (handled as a keystroke by
112
+ * the editing engine, not as an input event), so the chokepoint's only job is to stop the browser
113
+ * inserting the newline first.
114
+ */
115
+ const PROP_STRUCTURAL = new Set(["insertParagraph", "insertLineBreak"]);
116
+
94
117
  /**
95
118
  * Classify one `beforeinput`.
96
119
  *
@@ -124,7 +147,20 @@ export function classifyBeforeInput(ctx: BeforeInputContext): EditAction {
124
147
  return { kind: "reject" };
125
148
  }
126
149
 
127
- // 2. No resolvable position means the caret is in canvas chrome or a `contenteditable="false"`
150
+ /*
151
+ * 2. A prop-bound host is editable text with NO document path — it is written back as a prop
152
+ * value by its own commit (`editCommitProp`), not as a block. It must be judged before the
153
+ * null-position rule below, which would otherwise reject it for having no path: that is exactly
154
+ * what made a component slot show a caret and swallow every keystroke.
155
+ *
156
+ * Nothing here is structural, so nothing is re-expressed; the browser edits the text and the
157
+ * session commits the string.
158
+ */
159
+ if (ctx.inPropHost) {
160
+ return PROP_STRUCTURAL.has(inputType) ? { kind: "reject" } : { kind: "native" };
161
+ }
162
+
163
+ // 3. No resolvable position means the caret is in canvas chrome or a `contenteditable="false"`
128
164
  // Island — there is no document node to write to.
129
165
  if (!from || !to) {
130
166
  return { kind: "reject" };
@@ -134,6 +134,12 @@ export function restoreDocSelection(container: HTMLElement, range: DocRange): bo
134
134
  export interface EditableRootHandle {
135
135
  /** Tear the host down. */
136
136
  stop: () => void;
137
+ /**
138
+ * Forget the active element without committing or notifying — for when the EDITING ENGINE has
139
+ * already ended the session itself (Enter / Escape in a prop host) and only this module's
140
+ * bookkeeping is left pointing at it.
141
+ */
142
+ forget: () => void;
137
143
  /**
138
144
  * Re-derive the active block from the live selection. Call after moving the selection
139
145
  * programmatically — `selectionchange` is dispatched as a task, so a caller that needs the
@@ -242,6 +248,23 @@ export function startEditableRoot(
242
248
  scheduleCommitTick();
243
249
  };
244
250
 
251
+ /**
252
+ * Drop the active element WITHOUT telling the engine — the engine is what asked.
253
+ *
254
+ * `deactivate()` exists for the caret leaving, and calls `onDeactivate` to commit on the way out.
255
+ * When the ENGINE ends a session itself (Enter or Escape in a prop host), that has already
256
+ * happened, and calling back into it would re-enter a teardown that is mid-flight. What was
257
+ * missing was the other half: nothing cleared `activeEl`/`activeKey`, so the host kept pointing
258
+ * at a dead session — `onPointerDownCapture`'s `el !== activeEl` guard was then false and the
259
+ * next click on the same text did nothing at all, and `inPropHost` stayed true with no session
260
+ * behind it.
261
+ */
262
+ const forget = () => {
263
+ cancelCommitTick();
264
+ activeEl = null;
265
+ activeKey = null;
266
+ };
267
+
245
268
  /** Release whatever is active, if anything. */
246
269
  const deactivate = () => {
247
270
  if (!activeEl) {
@@ -358,6 +381,9 @@ export function startEditableRoot(
358
381
  atBlockStart: resolved ? isAtBlockStart(resolved.from) : false,
359
382
  data: e.data ?? "",
360
383
  from: resolved?.from ?? null,
384
+ /* An adopted element with no path IS the prop-bound host — `onPointerDownCapture` sets
385
+ exactly that pair, and `syncActiveBlock` preserves it while the caret stays inside. */
386
+ inPropHost: activeEl !== null && activeKey === null,
361
387
  inputType: e.inputType,
362
388
  to: resolved?.to ?? null,
363
389
  });
@@ -456,6 +482,7 @@ export function startEditableRoot(
456
482
 
457
483
  return {
458
484
  capture: () => captureDocSelection(container, deps.isEditableBlock),
485
+ forget,
459
486
  /** Whether an IME composition is in flight — callers must not rewrite the DOM under it. */
460
487
  isComposing: () => composing,
461
488
  flush: () => {