@jxsuite/studio 2.4.1 → 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.
- package/dist/chunks/{content-source-cn9te96r.js → content-source-k9724vcv.js} +8 -8
- package/dist/chunks/{convert-to-repeater-65gjgdst.js → convert-to-repeater-k10dvcc7.js} +7 -7
- package/dist/chunks/{extension-sections-mdgmqrry.js → extension-sections-0ada0xcw.js} +11 -11
- package/dist/chunks/{files-dsh5g8j4.js → files-yew27sn9.js} +12 -12
- package/dist/chunks/{format-host-m9h45a79.js → format-host-wewba4nq.js} +2 -2
- package/dist/chunks/{git-panel-rfs8pqjj.js → git-panel-2dh1aw50.js} +7 -7
- package/dist/chunks/{layers-panel-6cj5nf6a.js → layers-panel-qnsghwwd.js} +10 -10
- package/dist/chunks/{publish-panel-b0xh7b4y.js → publish-panel-a0pdft5w.js} +5 -5
- package/dist/chunks/{quick-search-a83fhnsh.js → quick-search-9avyc4pc.js} +13 -13
- package/dist/chunks/{right-panel-hrw5kdh5.js → right-panel-kcnhtc5j.js} +9 -9
- package/dist/chunks/{studio-4m39t01b.js → studio-0b6fs05q.js} +5 -5
- package/dist/chunks/{studio-gqpbrrj5.js → studio-26sv44br.js} +8 -8
- package/dist/chunks/{studio-d1f2rqxh.js → studio-28xc8ws4.js} +4 -4
- package/dist/chunks/{studio-675v08qj.js → studio-aweyr4v1.js} +2 -2
- package/dist/chunks/{studio-0svvdm0a.js → studio-cz2j1c0g.js} +4 -4
- package/dist/chunks/{studio-qymkxbcq.js → studio-dgj7gq44.js} +3 -3
- package/dist/chunks/{studio-rkr6pm84.js → studio-ef8t53vc.js} +3 -3
- package/dist/chunks/{studio-1d6bm7eg.js → studio-f5yyvqxv.js} +7 -7
- package/dist/chunks/{studio-rz861p2b.js → studio-hb7c46jy.js} +5 -5
- package/dist/chunks/{studio-gfzyxa0m.js → studio-hyn3vhwr.js} +5 -5
- package/dist/chunks/{studio-9sxn0eaa.js → studio-jnakhg87.js} +4 -4
- package/dist/chunks/{studio-tdt7b6qb.js → studio-ke244pam.js} +6 -6
- package/dist/chunks/{studio-h6553a8h.js → studio-pyrw8h0w.js} +9 -7
- package/dist/chunks/{studio-kfq9ed73.js → studio-ra7ar50f.js} +3 -3
- package/dist/chunks/{studio-40fm3hyr.js → studio-rqemfhz6.js} +2 -2
- package/dist/chunks/{studio-91sdrn9x.js → studio-sccge2wf.js} +2 -2
- package/dist/chunks/{studio-n9k15qdg.js → studio-v1va0vhd.js} +8 -8
- package/dist/chunks/{studio-jand3kjg.js → studio-v2c5pmjj.js} +10 -10
- package/dist/chunks/{studio-qb6vy1z1.js → studio-ws6ef6js.js} +8 -8
- package/dist/iframe-entry.js +35 -3
- package/dist/studio.js +31 -31
- package/package.json +2 -2
- package/src/canvas/canvas-origin.ts +14 -2
- package/src/canvas/iframe-inline-edit.ts +79 -25
- package/src/types.ts +3 -1
package/dist/studio.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
seedPublishConnected
|
|
3
|
-
} from "./chunks/studio-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
190
|
+
} from "./chunks/studio-v2c5pmjj.js";
|
|
191
191
|
import {
|
|
192
192
|
computeWindow
|
|
193
|
-
} from "./chunks/studio-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
319
|
+
} from "./chunks/studio-cz2j1c0g.js";
|
|
320
320
|
import {
|
|
321
321
|
beginActivity,
|
|
322
322
|
registerActivityPanel,
|
|
323
323
|
showProgressModal
|
|
324
|
-
} from "./chunks/studio-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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.
|
|
58595
|
-
var BUILD_DATE = "2026-08-
|
|
58596
|
-
var GIT_COMMIT = "
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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=
|
|
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.
|
|
3
|
+
"version": "2.4.2",
|
|
4
4
|
"description": "Jx Studio — visual builder for Jx documents",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
"@jxsuite/ai": "^0.36.2",
|
|
61
61
|
"@jxsuite/collab": "^0.8.1",
|
|
62
62
|
"@jxsuite/create": "^1.3.5",
|
|
63
|
-
"@jxsuite/formulas": "^0.0.
|
|
63
|
+
"@jxsuite/formulas": "^0.0.12",
|
|
64
64
|
"@jxsuite/markup": "^0.4.5",
|
|
65
65
|
"@jxsuite/protocol": "^1.2.0",
|
|
66
66
|
"@jxsuite/runtime": "^2.0.4",
|
|
@@ -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
|
-
|
|
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 `${
|
|
62
|
+
return `${origin}/${root ? `${root}/` : ""}`;
|
|
51
63
|
}
|
|
52
64
|
|
|
53
65
|
/**
|
|
@@ -38,6 +38,7 @@ import { adjacentBlock, blocksInOrder } from "./iframe-position";
|
|
|
38
38
|
import { isTagActiveInSelection, toggleInlineFormat } from "../editor/inline-format";
|
|
39
39
|
import { applyLink, insertTemplateToken, linkStateForSelection } from "../editor/inline-link";
|
|
40
40
|
import { restoreTemplateExpressions } from "../utils/edit-display";
|
|
41
|
+
import { RESERVED_KEYS, camelToKebab } from "@jxsuite/runtime";
|
|
41
42
|
import { rectOfRange } from "../utils/geometry";
|
|
42
43
|
import { getNodeAtPath } from "../state";
|
|
43
44
|
import { parseJxPath, serializeJxPath } from "./path-mapping";
|
|
@@ -86,6 +87,77 @@ function ownerInstanceOf(el: HTMLElement): HTMLElement | null {
|
|
|
86
87
|
return null;
|
|
87
88
|
}
|
|
88
89
|
|
|
90
|
+
/**
|
|
91
|
+
* Whether the instance node delivers `prop` by some route OTHER than `$props` — in which case
|
|
92
|
+
* "absent from `$props`" does not mean "unset", and a `$props` write would add a SECOND source for
|
|
93
|
+
* one rendered value.
|
|
94
|
+
*
|
|
95
|
+
* The routes are the property bridge's own, in the order `connectedCallback` reads them
|
|
96
|
+
* (`compiler.md` §4.4) — a `data-jx-props` payload, literal `props.*` attributes, then JS
|
|
97
|
+
* properties set before connection — plus the one that reaches that third source without going
|
|
98
|
+
* through `$props`. Each was reproduced against the real runtime:
|
|
99
|
+
*
|
|
100
|
+
* 1. `attributes: { "data-jx-props": "{…}" }` — the compiler's pre-rendered payload, parsed into state
|
|
101
|
+
* before anything else. Studio edits source documents rather than build output, so this is the
|
|
102
|
+
* rare one; it is here because the bridge reads it.
|
|
103
|
+
* 2. The JSON shorthand `attributes: { "props.text": "…" }`. `connectedCallback` collects `props.*`
|
|
104
|
+
* attributes into state, so the marker renders that text while `$props` is empty. This repo's
|
|
105
|
+
* own site carries 95 of them.
|
|
106
|
+
* 3. A prop whose name collides with a REFLECTED HTMLElement property — `title`, `role`, `id`, `lang`,
|
|
107
|
+
* `dir`, `slot`, `hidden`. `setAttribute("title", …)` writes the JS property as a side effect,
|
|
108
|
+
* and the merge `key in this && this[key] !== undefined` picks it up. 41 shipped starter
|
|
109
|
+
* components declare `title` or `role` as state. Matched the way `instanceSupplies` matches:
|
|
110
|
+
* attribute names are case-insensitive, and an ARIA-style reflection carries a kebab attribute
|
|
111
|
+
* for a camelCase property (`ariaLabel` → `aria-label`).
|
|
112
|
+
* 4. A TOP-LEVEL key on the instance node — `{ "tagName": "x-card", "heading": "Local" }`. This is the
|
|
113
|
+
* property-first interface written straight onto the node: `applyProperties` sets every
|
|
114
|
+
* non-reserved key as a JS property, which is exactly what the merge then reads back off `this`.
|
|
115
|
+
* Reserved keys (`name`, `items`, `children`, …) are skipped there and so are skipped here —
|
|
116
|
+
* refusing on one would block a prop that the key never delivered.
|
|
117
|
+
*
|
|
118
|
+
* Editing any of them writes `$props[prop]` and leaves the other value standing. For case 3 the
|
|
119
|
+
* attribute WINS (applyAttributes runs after the $props assignment) so the edit is simply
|
|
120
|
+
* invisible; for the rest `$props` wins and the stale value waits — clearing the prop later
|
|
121
|
+
* resurrects it. Refusing keeps the properties panel, which edits the real source, as the one way
|
|
122
|
+
* in.
|
|
123
|
+
*/
|
|
124
|
+
function deliveredOutsideProps(node: JxMutableNode | undefined, prop: string): boolean {
|
|
125
|
+
if (!node) {
|
|
126
|
+
return false;
|
|
127
|
+
}
|
|
128
|
+
const attrs = node.attributes as Record<string, unknown> | undefined;
|
|
129
|
+
if (attrs) {
|
|
130
|
+
if (attrs[`props.${prop}`] !== undefined) {
|
|
131
|
+
return true;
|
|
132
|
+
}
|
|
133
|
+
const reflected = new Set([prop.toLowerCase(), camelToKebab(prop).toLowerCase()]);
|
|
134
|
+
for (const [name, val] of Object.entries(attrs)) {
|
|
135
|
+
if (val !== undefined && reflected.has(name.toLowerCase())) {
|
|
136
|
+
return true;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
const payload = attrs["data-jx-props"];
|
|
140
|
+
if (typeof payload === "string" && payloadNames(payload, prop)) {
|
|
141
|
+
return true;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
// `$`-prefixed keys are scope bindings, never DOM properties — `applyProperties` skips them too.
|
|
145
|
+
if (RESERVED_KEYS.has(prop) || prop.startsWith("$")) {
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
return (node as Record<string, unknown>)[prop] !== undefined;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/** Whether a `data-jx-props` JSON payload carries `prop`. Unparseable payloads deliver nothing. */
|
|
152
|
+
function payloadNames(payload: string, prop: string): boolean {
|
|
153
|
+
try {
|
|
154
|
+
const parsed: unknown = JSON.parse(payload);
|
|
155
|
+
return typeof parsed === "object" && parsed !== null && prop in parsed;
|
|
156
|
+
} catch {
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
89
161
|
/** Locate the rendered element for a document path via its stamped `data-jx-path`. */
|
|
90
162
|
function elementForPath(container: HTMLElement, path: JxPath): HTMLElement | null {
|
|
91
163
|
const serialized = serializeJxPath(path);
|
|
@@ -267,10 +339,12 @@ export function startIframeInlineEdit(
|
|
|
267
339
|
};
|
|
268
340
|
|
|
269
341
|
/**
|
|
270
|
-
* Whether the instance's
|
|
271
|
-
*
|
|
272
|
-
*
|
|
273
|
-
*
|
|
342
|
+
* Whether the instance's prop (read from the pre-edit-display shadow doc) is plain-text editable
|
|
343
|
+
* AND `$props` is the place it lives — a value the instance delivers by another route belongs to
|
|
344
|
+
* that route, and `deliveredOutsideProps` refuses it. Template-valued (`"${...}"`) and
|
|
345
|
+
* `$ref`-valued props render display sugar, not the value — committing typed text would silently
|
|
346
|
+
* destroy the binding, so they're blocked. An absent getShadowDoc is permissive (tests, same
|
|
347
|
+
* contract as getMode).
|
|
274
348
|
*/
|
|
275
349
|
const propEditableAt = (hostPath: JxPath, prop: string): boolean => {
|
|
276
350
|
const shadowDoc = opts?.getShadowDoc?.();
|
|
@@ -278,27 +352,7 @@ export function startIframeInlineEdit(
|
|
|
278
352
|
return true;
|
|
279
353
|
}
|
|
280
354
|
const node = getNodeAtPath(shadowDoc, hostPath) as JxMutableNode | undefined;
|
|
281
|
-
|
|
282
|
-
* `$props` IS NOT THE ONLY PLACE A PROP VALUE LIVES, and "absent from $props" is therefore not
|
|
283
|
-
* the same question as "unset". Two shapes deliver one through `attributes` instead, and both
|
|
284
|
-
* were reproduced against the real runtime:
|
|
285
|
-
*
|
|
286
|
-
* 1. The JSON shorthand `attributes: { "props.text": "…" }`. `connectedCallback` collects
|
|
287
|
-
* `props.*` attributes into state, so the marker renders that text while `$props` is
|
|
288
|
-
* empty. This repo's own site carries 95 of them.
|
|
289
|
-
* 2. A prop whose name collides with a REFLECTED HTMLElement property — `title`, `role`,
|
|
290
|
-
* `id`, `lang`, `dir`, `slot`, `hidden`. `setAttribute("title", …)` writes the JS property
|
|
291
|
-
* as a side effect, and the merge `key in this && this[key] !== undefined` picks it up.
|
|
292
|
-
* 41 shipped starter components declare `title` or `role` as state.
|
|
293
|
-
*
|
|
294
|
-
* Editing either would write `$props[prop]` and leave the attribute standing — two sources for
|
|
295
|
-
* one rendered value. In case 2 the attribute WINS (applyAttributes runs after the $props
|
|
296
|
-
* assignment), so the edit is simply invisible; in case 1 clearing the prop later resurrects
|
|
297
|
-
* the old text. Refusing keeps the properties panel, which edits the attribute itself, as the
|
|
298
|
-
* one way in.
|
|
299
|
-
*/
|
|
300
|
-
const attrs = node?.attributes as Record<string, unknown> | undefined;
|
|
301
|
-
if (attrs?.[`props.${prop}`] !== undefined || attrs?.[prop] !== undefined) {
|
|
355
|
+
if (deliveredOutsideProps(node, prop)) {
|
|
302
356
|
return false;
|
|
303
357
|
}
|
|
304
358
|
const raw = (node?.$props as Record<string, unknown> | undefined)?.[prop];
|
package/src/types.ts
CHANGED
|
@@ -195,7 +195,9 @@ export interface StudioPlatform {
|
|
|
195
195
|
* every `$ref` fetch landed on the SPA fallback — which answers HTML at **200**, so `res.ok`
|
|
196
196
|
* passed and the parse died on `Unexpected token '<'`. Images failed the same way in silence.
|
|
197
197
|
*
|
|
198
|
-
*
|
|
198
|
+
* May be absolute or root-relative — a root-relative value is resolved against the canvas origin,
|
|
199
|
+
* because the canvas uses this as `new URL(path, base)` and a relative BASE throws. A missing
|
|
200
|
+
* trailing `/` is added: without it `new URL` drops the last segment.
|
|
199
201
|
*/
|
|
200
202
|
documentBaseUrl?: string;
|
|
201
203
|
activate: (root?: string) => Promise<void>;
|