@jxsuite/studio 2.4.3 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (208) hide show
  1. package/README.md +5 -5
  2. package/dist/chunks/Overlay.dev-723bv0df.js +23 -0
  3. package/dist/chunks/cf-settings-45fgyx8y.js +21 -0
  4. package/dist/chunks/{components-z6cnkyy1.js → components-zmx10t62.js} +6 -5
  5. package/dist/chunks/content-source-t88vb2dr.js +49 -0
  6. package/dist/chunks/{convert-to-repeater-k10dvcc7.js → convert-to-repeater-dhw1nrz9.js} +27 -18
  7. package/dist/chunks/entry-commands-6z6bhxg0.js +46 -0
  8. package/dist/chunks/{extension-sections-0ada0xcw.js → extension-sections-3ydgjbdy.js} +32 -23
  9. package/dist/chunks/file-ops-rskmhxyq.js +48 -0
  10. package/dist/chunks/files-0cratsr0.js +92 -0
  11. package/dist/chunks/{format-host-wewba4nq.js → format-host-tgcmfs4p.js} +3 -3
  12. package/dist/chunks/fs-events-hrrg328g.js +21 -0
  13. package/dist/chunks/{git-panel-2dh1aw50.js → git-panel-0yywyk76.js} +21 -15
  14. package/dist/chunks/{jx-validate-jj9rnt4n.js → jx-validate-8q2g05x1.js} +6 -4
  15. package/dist/chunks/layers-panel-z3tqe5cd.js +67 -0
  16. package/dist/chunks/layers-q75tj2mf.js +45 -0
  17. package/dist/chunks/lit-html-j055mkh8.js +22 -0
  18. package/dist/chunks/monaco-buffer-dqw0xxpx.js +25 -0
  19. package/dist/chunks/{monaco-setup-a5wkmbgh.js → monaco-setup-rws7k4br.js} +4 -4
  20. package/dist/chunks/notify-yjm79n4s.js +30 -0
  21. package/dist/chunks/parse-24d0gay3.js +19 -0
  22. package/dist/chunks/platform-ba1j1gbh.js +16 -0
  23. package/dist/chunks/publish-panel-54jvhskq.js +28 -0
  24. package/dist/chunks/quick-search-cgfmjwq5.js +65 -0
  25. package/dist/chunks/references-3qx8fkyp.js +29 -0
  26. package/dist/chunks/right-panel-cqp8bc4v.js +46 -0
  27. package/dist/chunks/sp-overlay.dev-yztmtrs1.js +18 -0
  28. package/dist/chunks/sp-popover.dev-n2etmqdp.js +9 -0
  29. package/dist/chunks/sp-progress-circle.dev-hryhj0es.js +15 -0
  30. package/dist/chunks/sp-tooltip.dev-xcvpnqr0.js +15 -0
  31. package/dist/chunks/{sp-tray.dev-64v4pxw8.js → sp-tray.dev-tkbscm06.js} +5 -5
  32. package/dist/chunks/{studio-f5yyvqxv.js → studio-1hbqnt19.js} +37 -31
  33. package/dist/chunks/studio-2zkz8evn.js +3163 -0
  34. package/dist/chunks/{studio-0b6fs05q.js → studio-4pqqxbrt.js} +23 -15
  35. package/dist/chunks/{studio-kvfx9z2c.js → studio-4rzyrtjz.js} +70 -31
  36. package/dist/chunks/{studio-ef8t53vc.js → studio-5q69gfag.js} +60 -56
  37. package/dist/chunks/{studio-v2c5pmjj.js → studio-5rywrkxz.js} +565 -133
  38. package/dist/chunks/{studio-6kv3apa9.js → studio-68n67094.js} +25 -9
  39. package/dist/chunks/{studio-0rqqhrdy.js → studio-7nte2hj4.js} +2 -2
  40. package/dist/chunks/{studio-hyn3vhwr.js → studio-8swrjbny.js} +31 -18
  41. package/dist/chunks/studio-92ab2jqe.js +282 -0
  42. package/dist/chunks/{studio-sas7mt3k.js → studio-9ab8gqsm.js} +2 -2
  43. package/dist/chunks/{studio-28xc8ws4.js → studio-9c4k4vhv.js} +14 -10
  44. package/dist/chunks/studio-9n4mrmnb.js +184 -0
  45. package/dist/chunks/{studio-tr20m8g8.js → studio-9r6j5ev1.js} +37 -2
  46. package/dist/chunks/{studio-aweyr4v1.js → studio-ahjfdy6e.js} +9 -7
  47. package/dist/chunks/{studio-s519zc99.js → studio-bgxt93ga.js} +3 -3
  48. package/dist/chunks/{studio-26sv44br.js → studio-cb12yz9w.js} +8543 -8784
  49. package/dist/chunks/studio-cft9r4r7.js +119 -0
  50. package/dist/chunks/{studio-rqemfhz6.js → studio-eenfkt4e.js} +6 -6
  51. package/dist/chunks/studio-eesa1myv.js +498 -0
  52. package/dist/chunks/{studio-ay8wcr2b.js → studio-eqq1w638.js} +7 -9
  53. package/dist/chunks/{studio-ke244pam.js → studio-ex8zyp98.js} +51 -25
  54. package/dist/chunks/studio-gv8vvshc.js +118 -0
  55. package/dist/chunks/studio-hbtp0eh1.js +1315 -0
  56. package/dist/chunks/{studio-cz2j1c0g.js → studio-htsf0w6m.js} +75 -123
  57. package/dist/chunks/{studio-pyrw8h0w.js → studio-kjjsjak4.js} +528 -212
  58. package/dist/chunks/{studio-5x5774ak.js → studio-kryvsw6j.js} +3 -3
  59. package/dist/chunks/studio-kyh9avvv.js +28 -0
  60. package/dist/chunks/{studio-jnakhg87.js → studio-kyk2v92a.js} +14 -14
  61. package/dist/chunks/{studio-twsafr1e.js → studio-m3hwn7y1.js} +3 -3
  62. package/dist/chunks/{studio-tzx3xmvx.js → studio-mmv72a27.js} +8 -8
  63. package/dist/chunks/{studio-vhk5fmfv.js → studio-n8b9wqwy.js} +5 -5
  64. package/dist/chunks/{studio-cjm7fdjf.js → studio-p4ngc046.js} +8 -104
  65. package/dist/chunks/{studio-sccge2wf.js → studio-p8j86wag.js} +2 -2
  66. package/dist/chunks/{studio-v1va0vhd.js → studio-pdfjntbk.js} +75 -139
  67. package/dist/chunks/studio-q43a5y9h.js +394 -0
  68. package/dist/chunks/studio-q7v10cg2.js +106 -0
  69. package/dist/chunks/{studio-2w11grav.js → studio-qq6jym04.js} +3 -3
  70. package/dist/chunks/studio-r1r7afxx.js +1066 -0
  71. package/dist/chunks/{studio-3fk1ac9z.js → studio-rdx8jcxy.js} +4 -4
  72. package/dist/chunks/{studio-4mqb9a0j.js → studio-rn9ta2mz.js} +4 -4
  73. package/dist/chunks/studio-sf7zjgv1.js +91 -0
  74. package/dist/chunks/studio-t7qb9db8.js +665 -0
  75. package/dist/chunks/studio-tmc5emrw.js +8 -0
  76. package/dist/chunks/{studio-wdt9y92a.js → studio-txwkm6p9.js} +2 -2
  77. package/dist/chunks/{studio-dgj7gq44.js → studio-v38hmvx1.js} +7 -7
  78. package/dist/chunks/{studio-7ejemhej.js → studio-vadzgkrz.js} +6 -6
  79. package/dist/chunks/studio-wcy8trfx.js +263 -0
  80. package/dist/chunks/{studio-hb7c46jy.js → studio-wx5609xd.js} +21 -15
  81. package/dist/chunks/studio-xrapxwwc.js +1539 -0
  82. package/dist/chunks/{studio-ws6ef6js.js → studio-yxrkyv8a.js} +41 -29
  83. package/dist/chunks/{studio-xm3dbxvn.js → studio-zczps70q.js} +2 -2
  84. package/dist/chunks/workspace-xpres40v.js +89 -0
  85. package/dist/iframe-entry.js +450 -48
  86. package/dist/studio.js +2455 -1761
  87. package/package.json +16 -15
  88. package/src/browse/library-layouts.ts +3 -3
  89. package/src/browse/library-pane.ts +59 -5
  90. package/src/browse/library-preview.ts +19 -3
  91. package/src/canvas/asset-refs.ts +119 -0
  92. package/src/canvas/asset-resolve.ts +256 -0
  93. package/src/canvas/canvas-live-render.ts +23 -15
  94. package/src/canvas/canvas-render.ts +23 -1
  95. package/src/canvas/iframe-entry.ts +1 -0
  96. package/src/canvas/iframe-host.ts +4 -0
  97. package/src/canvas/iframe-patch.ts +47 -21
  98. package/src/canvas/iframe-protocol.ts +9 -0
  99. package/src/canvas/iframe-render.ts +50 -16
  100. package/src/commands/app-commands.ts +4 -0
  101. package/src/commands/command-args.ts +29 -0
  102. package/src/commands/context.ts +15 -1
  103. package/src/commands/defaults.ts +29 -1
  104. package/src/commands/levels.ts +16 -0
  105. package/src/commands/live-context.ts +3 -0
  106. package/src/content/collection-match.ts +267 -0
  107. package/src/content/entry-commands.ts +38 -29
  108. package/src/content/entry-fields.ts +6 -0
  109. package/src/editor/file-drop-action.ts +2 -1
  110. package/src/editor/shortcuts.ts +5 -0
  111. package/src/files/file-ops.ts +10 -42
  112. package/src/files/files.ts +487 -61
  113. package/src/files/fs-events.ts +9 -0
  114. package/src/files/gitignore.ts +432 -0
  115. package/src/files/media-paths.ts +37 -14
  116. package/src/files/media-upload.ts +47 -6
  117. package/src/files/serialize-document.ts +60 -0
  118. package/src/format/convert-file.ts +511 -0
  119. package/src/format/format-choices.ts +199 -0
  120. package/src/grid/grid-idle.ts +45 -0
  121. package/src/grid/grid-view.ts +35 -0
  122. package/src/grid/schema-columns.ts +2 -1
  123. package/src/grid/sources/content-source.ts +24 -3
  124. package/src/media/media-open.ts +58 -0
  125. package/src/media/media-pane.ts +310 -0
  126. package/src/new-project/import-tab.ts +333 -160
  127. package/src/new-project/new-project-modal.ts +27 -34
  128. package/src/packages/ensure-deps.ts +14 -7
  129. package/src/packages/jxsuite-update.ts +11 -11
  130. package/src/page-params.ts +10 -46
  131. package/src/panels/activity-bar.ts +45 -16
  132. package/src/panels/ai-chat/attached-context.ts +6 -1
  133. package/src/panels/ai-chat/chat-view.ts +255 -13
  134. package/src/panels/ai-chat/composer.ts +21 -80
  135. package/src/panels/ai-panel.ts +116 -2
  136. package/src/panels/frontmatter-fields.ts +6 -2
  137. package/src/panels/navigator-panels.ts +7 -6
  138. package/src/panels/properties-panel.ts +8 -2
  139. package/src/panels/seo-modal.ts +5 -6
  140. package/src/panels/settings-menu.ts +799 -0
  141. package/src/panels/signals-panel.ts +2 -1
  142. package/src/panels/toolbar.ts +135 -15
  143. package/src/platforms/cloud.ts +34 -3
  144. package/src/platforms/devserver.ts +72 -34
  145. package/src/preview/preview-overlay.ts +226 -0
  146. package/src/services/ai-ask.ts +247 -0
  147. package/src/services/ai-import-tools.ts +494 -0
  148. package/src/services/ai-models.ts +101 -11
  149. package/src/services/ai-project-tools.ts +20 -26
  150. package/src/services/ai-settings.ts +69 -71
  151. package/src/services/ai-system-prompt.ts +88 -2
  152. package/src/services/ai-turn-signal.ts +70 -0
  153. package/src/services/cf-settings.ts +19 -37
  154. package/src/services/connection.ts +74 -0
  155. package/src/services/context-manager.ts +89 -0
  156. package/src/services/document-assistant.ts +49 -7
  157. package/src/services/idle.ts +6 -1
  158. package/src/services/import-client.ts +33 -6
  159. package/src/services/import-run.ts +158 -0
  160. package/src/services/import-seed.ts +120 -0
  161. package/src/services/jx-validate.ts +58 -6
  162. package/src/services/project-adoption.ts +71 -0
  163. package/src/services/references.ts +0 -0
  164. package/src/services/settings/definitions.ts +181 -0
  165. package/src/services/settings/kernel.ts +335 -0
  166. package/src/services/settings/write-queue.ts +116 -0
  167. package/src/services/tool-executor.ts +34 -2
  168. package/src/settings/contexts-section.ts +45 -3
  169. package/src/settings/dependencies-editor.ts +43 -23
  170. package/src/settings/general-settings.ts +30 -12
  171. package/src/settings/preferences-accounts.ts +6 -19
  172. package/src/settings/preferences-dialog.ts +18 -41
  173. package/src/settings/preferences-keymap.ts +8 -18
  174. package/src/settings/preferences-sections.ts +57 -0
  175. package/src/settings/schema-field-ui.ts +2 -2
  176. package/src/settings/settings-document.ts +65 -9
  177. package/src/shell.ts +6 -14
  178. package/src/studio.ts +65 -7
  179. package/src/tabs/transact.ts +42 -19
  180. package/src/types.ts +164 -8
  181. package/src/ui/ai-credentials-form.ts +52 -27
  182. package/src/ui/ai-model-picker.ts +162 -0
  183. package/src/ui/choice-field.ts +72 -0
  184. package/src/ui/layers.ts +83 -5
  185. package/src/ui/media-picker.ts +6 -11
  186. package/src/utils/studio-utils.ts +43 -31
  187. package/styles/forced-colors.css +7 -0
  188. package/styles/overlays.css +256 -0
  189. package/styles/panels.css +13 -1
  190. package/styles/shell.css +111 -0
  191. package/dist/chunks/Overlay.dev-d2ajy6sj.js +0 -23
  192. package/dist/chunks/cf-settings-g5fbf2zp.js +0 -17
  193. package/dist/chunks/content-source-k9724vcv.js +0 -39
  194. package/dist/chunks/files-yew27sn9.js +0 -69
  195. package/dist/chunks/layers-panel-qnsghwwd.js +0 -59
  196. package/dist/chunks/publish-panel-a0pdft5w.js +0 -21
  197. package/dist/chunks/quick-search-9avyc4pc.js +0 -54
  198. package/dist/chunks/right-panel-kcnhtc5j.js +0 -38
  199. package/dist/chunks/sp-overlay.dev-s18hymn9.js +0 -18
  200. package/dist/chunks/sp-popover.dev-dj9x7e7a.js +0 -9
  201. package/dist/chunks/sp-progress-circle.dev-0zxcyz3r.js +0 -15
  202. package/dist/chunks/sp-tooltip.dev-5c6d1kjz.js +0 -15
  203. package/dist/chunks/studio-a7pzveq1.js +0 -110
  204. package/dist/chunks/studio-c31yb4t6.js +0 -55
  205. package/dist/chunks/studio-ra7ar50f.js +0 -6682
  206. package/src/canvas/content-assets.ts +0 -263
  207. package/src/canvas/site-style-css.ts +0 -121
  208. package/src/services/settings-store.ts +0 -99
@@ -2084,6 +2084,188 @@ function evaluateOperand(operand, state, event, iterCtx) {
2084
2084
  return resolveOperand(operand, state, event, iterCtx);
2085
2085
  }
2086
2086
 
2087
+ // ../schema/src/asset-paths.ts
2088
+ function normalizeDir(value) {
2089
+ const forward = value.replaceAll("\\", "/");
2090
+ return forward.length > 1 && forward.endsWith("/") ? forward.slice(0, -1) : forward;
2091
+ }
2092
+ function normalizeAssetPrefix(prefix) {
2093
+ const forward = normalizeDir(prefix);
2094
+ return forward.startsWith("/") ? forward : `/${forward}`;
2095
+ }
2096
+ function bySpecificity(mounts, key) {
2097
+ return [...mounts].toSorted((a, b) => b[key].length - a[key].length);
2098
+ }
2099
+ function isUnder(path, dir) {
2100
+ return path === dir || path.startsWith(`${dir}/`);
2101
+ }
2102
+ function assetUrlFor(mounts, absolutePath) {
2103
+ const path = normalizeDir(absolutePath);
2104
+ for (const mount of bySpecificity(mounts, "dir")) {
2105
+ const dir = normalizeDir(mount.dir);
2106
+ if (!isUnder(path, dir) || path === dir) {
2107
+ continue;
2108
+ }
2109
+ const rest = path.slice(dir.length + 1);
2110
+ const encoded = rest.split("/").map((segment) => encodeURIComponent(segment)).join("/");
2111
+ return `${normalizeAssetPrefix(mount.urlPrefix)}/${encoded}`;
2112
+ }
2113
+ return null;
2114
+ }
2115
+ function resolveAssetUrl(mounts, url) {
2116
+ const pathname = url.split("#")[0].split("?")[0];
2117
+ if (!pathname.startsWith("/")) {
2118
+ return null;
2119
+ }
2120
+ let decoded;
2121
+ try {
2122
+ decoded = decodeURIComponent(pathname);
2123
+ } catch {
2124
+ return null;
2125
+ }
2126
+ if (/%2e|%2f/i.test(decoded)) {
2127
+ return null;
2128
+ }
2129
+ for (const mount of bySpecificity(mounts, "urlPrefix")) {
2130
+ const prefix = normalizeAssetPrefix(mount.urlPrefix);
2131
+ if (!decoded.startsWith(`${prefix}/`)) {
2132
+ continue;
2133
+ }
2134
+ const rest = decoded.slice(prefix.length + 1);
2135
+ const segments = rest.split("/");
2136
+ if (segments.some((s) => s === "" || s === "." || s === "..")) {
2137
+ return null;
2138
+ }
2139
+ return `${normalizeDir(mount.dir)}/${segments.join("/")}`;
2140
+ }
2141
+ return null;
2142
+ }
2143
+ var URL_TERMINATOR = String.raw`"'\s<>()\\`;
2144
+ var PUBLIC_DIR = "public";
2145
+ function splitRefSuffix(value) {
2146
+ const cut = value.search(/[#?]/);
2147
+ return cut === -1 ? { path: value, suffix: "" } : { path: value.slice(0, cut), suffix: value.slice(cut) };
2148
+ }
2149
+ function isNonRelativeRef(value) {
2150
+ return value === "" || value.startsWith("/") || value.startsWith("#") || value.includes("${") || /^[a-z][\w+.-]*:/i.test(value);
2151
+ }
2152
+ function normalizeProjectPath(path) {
2153
+ return path.replaceAll("\\", "/").replace(/^\.\//, "").replace(/^\/+/, "").replace(/\/+$/, "");
2154
+ }
2155
+ function joinProjectPath(dir, ref2) {
2156
+ const base = dir === "" || dir === "." ? [] : normalizeProjectPath(dir).split("/");
2157
+ const out = base.filter((segment) => segment !== "" && segment !== ".");
2158
+ for (const segment of ref2.replaceAll("\\", "/").split("/")) {
2159
+ if (segment === "" || segment === ".") {
2160
+ continue;
2161
+ }
2162
+ if (segment === "..") {
2163
+ if (out.length === 0) {
2164
+ return null;
2165
+ }
2166
+ out.pop();
2167
+ continue;
2168
+ }
2169
+ out.push(segment);
2170
+ }
2171
+ return out.join("/");
2172
+ }
2173
+ function projectPathsForSiteUrl(url, mounts, lanes) {
2174
+ const { path } = splitRefSuffix(url);
2175
+ if (!path.startsWith("/")) {
2176
+ return [];
2177
+ }
2178
+ let decoded;
2179
+ try {
2180
+ decoded = decodeURIComponent(path);
2181
+ } catch {
2182
+ return [];
2183
+ }
2184
+ if (/%2e|%2f/i.test(decoded)) {
2185
+ return [];
2186
+ }
2187
+ const rest = decoded.replace(/^\/+/, "");
2188
+ if (rest === "" || rest.split("/").some((segment) => segment === "." || segment === "..")) {
2189
+ return [];
2190
+ }
2191
+ const out = [];
2192
+ for (const lane of lanes) {
2193
+ if (lane === "mounts") {
2194
+ const mounted = resolveAssetUrl(mounts, path);
2195
+ if (mounted !== null && !mounted.startsWith("/") && !/^[A-Za-z]:/.test(mounted)) {
2196
+ out.push(normalizeProjectPath(mounted));
2197
+ }
2198
+ continue;
2199
+ }
2200
+ out.push(lane === "root" ? rest : `${PUBLIC_DIR}/${rest}`);
2201
+ }
2202
+ return [...new Set(out)];
2203
+ }
2204
+ function projectPathForRef(value, documentDir, mounts, lanes) {
2205
+ const { path } = splitRefSuffix(value);
2206
+ if (path === "") {
2207
+ return null;
2208
+ }
2209
+ if (isNonRelativeRef(value)) {
2210
+ return path.startsWith("/") ? projectPathsForSiteUrl(path, mounts, lanes)[0] ?? null : null;
2211
+ }
2212
+ if (path.endsWith("/")) {
2213
+ return null;
2214
+ }
2215
+ let decoded;
2216
+ try {
2217
+ decoded = decodeURIComponent(path);
2218
+ } catch {
2219
+ return null;
2220
+ }
2221
+ const joined = joinProjectPath(documentDir, decoded);
2222
+ if (joined === null || joined === "" || joined === normalizeProjectPath(documentDir)) {
2223
+ return null;
2224
+ }
2225
+ return joined;
2226
+ }
2227
+ function encodeProjectPath(path) {
2228
+ return path.split("/").map((segment) => encodeURIComponent(segment)).join("/");
2229
+ }
2230
+ function parseSrcset(value) {
2231
+ const out = [];
2232
+ let i = 0;
2233
+ while (i < value.length) {
2234
+ while (i < value.length && (/\s/.test(value[i]) || value[i] === ",")) {
2235
+ i += 1;
2236
+ }
2237
+ if (i >= value.length) {
2238
+ break;
2239
+ }
2240
+ const start = i;
2241
+ while (i < value.length && !/\s/.test(value[i])) {
2242
+ i += 1;
2243
+ }
2244
+ let url = value.slice(start, i);
2245
+ if (url.endsWith(",")) {
2246
+ out.push({ descriptor: "", url: url.replace(/,+$/, "") });
2247
+ continue;
2248
+ }
2249
+ while (i < value.length && /\s/.test(value[i])) {
2250
+ i += 1;
2251
+ }
2252
+ const descStart = i;
2253
+ while (i < value.length && value[i] !== ",") {
2254
+ i += 1;
2255
+ }
2256
+ const descriptor = value.slice(descStart, i).trim();
2257
+ i += 1;
2258
+ url = url.replace(/,+$/, "");
2259
+ if (url !== "") {
2260
+ out.push({ descriptor, url });
2261
+ }
2262
+ }
2263
+ return out;
2264
+ }
2265
+ function formatSrcset(candidates) {
2266
+ return candidates.map(({ url, descriptor }) => descriptor ? `${url} ${descriptor}` : url).join(", ");
2267
+ }
2268
+
2087
2269
  // ../runtime/src/statements.ts
2088
2270
  function statementKind(statement) {
2089
2271
  if ("operator" in statement) {
@@ -2287,6 +2469,52 @@ function canvasAttrName(el, key) {
2287
2469
  }
2288
2470
  return key;
2289
2471
  }
2472
+ var _canvasAssetResolver = null;
2473
+ function setCanvasAssetResolver(fn) {
2474
+ _canvasAssetResolver = fn;
2475
+ }
2476
+ var ASSET_KEYS = new Set(["src", "poster", "data"]);
2477
+ var SRCSET_KEYS = new Set(["srcset", "imagesrcset"]);
2478
+ var HREF_ASSET_TAGS = new Set(["LINK"]);
2479
+ function isAssetKey(tagName, key) {
2480
+ return ASSET_KEYS.has(key) || key === "href" && HREF_ASSET_TAGS.has(tagName);
2481
+ }
2482
+ function canvasAssetValue(tagName, key, value) {
2483
+ if (!_canvasAssetResolver || value === "") {
2484
+ return value;
2485
+ }
2486
+ const name = key.toLowerCase();
2487
+ if (SRCSET_KEYS.has(name)) {
2488
+ const candidates = parseSrcset(value);
2489
+ let changed = false;
2490
+ const next = candidates.map((candidate) => {
2491
+ const resolved = _canvasAssetResolver?.(candidate.url, name) ?? null;
2492
+ if (resolved === null || resolved === candidate.url) {
2493
+ return candidate;
2494
+ }
2495
+ changed = true;
2496
+ return { descriptor: candidate.descriptor, url: resolved };
2497
+ });
2498
+ return changed ? formatSrcset(next) : value;
2499
+ }
2500
+ if (!isAssetKey(tagName, name)) {
2501
+ return value;
2502
+ }
2503
+ return _canvasAssetResolver(value, name) ?? value;
2504
+ }
2505
+ var CSS_URL_RE = /\burl\(\s*(?<q>["']?)(?<u>[^"')]*)\k<q>\s*\)/g;
2506
+ function canvasStyleUrls(value) {
2507
+ if (!_canvasAssetResolver || !value.includes("url(")) {
2508
+ return value;
2509
+ }
2510
+ return value.replaceAll(CSS_URL_RE, (whole, quote, url) => {
2511
+ const resolved = url === "" ? null : _canvasAssetResolver?.(url, "url()") ?? null;
2512
+ return resolved === null ? whole : `url(${quote}${resolved}${quote})`;
2513
+ });
2514
+ }
2515
+ function canvasStyleValue(value) {
2516
+ return transposeCanvasUnits(canvasStyleUrls(value));
2517
+ }
2290
2518
  var _stampPropBindings = false;
2291
2519
  function setStampPropBindings(on) {
2292
2520
  _stampPropBindings = on;
@@ -2460,6 +2688,32 @@ function evaluateTemplate(str, state) {
2460
2688
  const fn = new Function("state", "$map", "item", "index", `return \`${str}\``);
2461
2689
  return fn(state, $map, $map?.item, $map?.index);
2462
2690
  }
2691
+ function isSingleExpression(str) {
2692
+ if (!str.startsWith("${") || !str.endsWith("}")) {
2693
+ return false;
2694
+ }
2695
+ let depth = 0;
2696
+ for (let i = 1;i < str.length; i += 1) {
2697
+ const c = str[i];
2698
+ if (c === "{") {
2699
+ depth += 1;
2700
+ } else if (c === "}") {
2701
+ depth -= 1;
2702
+ if (depth === 0) {
2703
+ return i === str.length - 1;
2704
+ }
2705
+ }
2706
+ }
2707
+ return false;
2708
+ }
2709
+ function evaluateAttrTemplate(str, state) {
2710
+ if (!isSingleExpression(str)) {
2711
+ return evaluateTemplate(str, state);
2712
+ }
2713
+ const $map = state?.$map;
2714
+ const fn = new Function("state", "$map", "item", "index", `return (${str.slice(2, -1)})`);
2715
+ return fn(state, $map, $map?.item, $map?.index);
2716
+ }
2463
2717
  var _moduleCache = new Map;
2464
2718
  async function resolveFunction(def2, state, key, base) {
2465
2719
  if (def2.body && def2.$src) {
@@ -2563,6 +2817,17 @@ var RESERVED_KEYS = new Set([
2563
2817
  "observedAttributes"
2564
2818
  ]);
2565
2819
  var warnedRefChildren = new Set;
2820
+ var SVG_NS = "http://www.w3.org/2000/svg";
2821
+ var MATHML_NS = "http://www.w3.org/1998/Math/MathML";
2822
+ function elementNamespace(tagName, inherited) {
2823
+ if (tagName === "svg") {
2824
+ return SVG_NS;
2825
+ }
2826
+ if (tagName === "math") {
2827
+ return MATHML_NS;
2828
+ }
2829
+ return inherited ?? null;
2830
+ }
2566
2831
  function renderNode(def2, state, options) {
2567
2832
  const path = options?._path ?? [];
2568
2833
  if (typeof def2 === "string" || typeof def2 === "number" || typeof def2 === "boolean") {
@@ -2605,7 +2870,9 @@ function renderNode(def2, state, options) {
2605
2870
  if (def2.$switch) {
2606
2871
  return renderSwitch(def2, localState, options);
2607
2872
  }
2608
- const el = document.createElement(tagName);
2873
+ const ns = elementNamespace(tagName, options?._ns);
2874
+ const childNs = tagName === "foreignObject" ? null : ns;
2875
+ const el = ns ? document.createElementNS(ns, tagName) : document.createElement(tagName);
2609
2876
  if (options?.onNodeCreated) {
2610
2877
  options.onNodeCreated(el, path, def2, localState);
2611
2878
  }
@@ -2614,12 +2881,12 @@ function renderNode(def2, state, options) {
2614
2881
  applyAttributes(el, def2.attributes ?? {}, localState);
2615
2882
  const kids = def2.children;
2616
2883
  if (isMappedArray(kids)) {
2617
- const arrOpts = options ? { ...options, _path: [...path, "children"] } : undefined;
2884
+ const arrOpts = options || childNs ? { ...options, _ns: childNs, _path: [...path, "children"] } : undefined;
2618
2885
  renderMappedArrayInto(el, kids, localState, arrOpts);
2619
2886
  } else if (Array.isArray(kids)) {
2620
2887
  for (let i = 0;i < kids.length; i++) {
2621
2888
  const child = kids[i];
2622
- const childOpts = options ? { ...options, _path: [...path, "children", i] } : undefined;
2889
+ const childOpts = options || childNs ? { ...options, _ns: childNs, _path: [...path, "children", i] } : undefined;
2623
2890
  if (isMappedArray(child)) {
2624
2891
  renderMappedArrayInto(el, child, localState, childOpts);
2625
2892
  } else {
@@ -2678,6 +2945,15 @@ function bindProperty(el, key, val, state) {
2678
2945
  if (boundProp) {
2679
2946
  el.dataset.jxBoundProp = boundProp;
2680
2947
  }
2948
+ const asAsset = (resolved) => typeof resolved === "string" ? canvasAssetValue(el.tagName, key, resolved) : resolved;
2949
+ const write = (resolved) => {
2950
+ const node = el;
2951
+ if (key === "className" && !(node instanceof HTMLElement)) {
2952
+ node.setAttribute("class", resolved == null ? "" : String(resolved));
2953
+ return;
2954
+ }
2955
+ target[key] = resolved;
2956
+ };
2681
2957
  if (isRefObj(val)) {
2682
2958
  const refVal = val;
2683
2959
  if (key === "id") {
@@ -2685,17 +2961,17 @@ function bindProperty(el, key, val, state) {
2685
2961
  return;
2686
2962
  }
2687
2963
  effect(() => {
2688
- target[key] = resolveRef(refVal.$ref, state);
2964
+ write(asAsset(resolveRef(refVal.$ref, state)));
2689
2965
  });
2690
2966
  return;
2691
2967
  }
2692
2968
  if (isTemplateString(val)) {
2693
2969
  effect(() => {
2694
- target[key] = evaluateTemplate(val, state);
2970
+ write(asAsset(evaluateTemplate(val, state)));
2695
2971
  });
2696
2972
  return;
2697
2973
  }
2698
- target[key] = val;
2974
+ write(asAsset(val));
2699
2975
  }
2700
2976
  function applyStyle(el, styleDef, mediaQueries = {}, state = {}) {
2701
2977
  const nested = {};
@@ -2730,19 +3006,19 @@ function applyStyle(el, styleDef, mediaQueries = {}, state = {}) {
2730
3006
  if (prop.startsWith("--")) {
2731
3007
  if (isTemplateString(val)) {
2732
3008
  effect(() => {
2733
- el.style.setProperty(prop, transposeCanvasUnits(evaluateTemplate(val, state)));
3009
+ el.style.setProperty(prop, canvasStyleValue(evaluateTemplate(val, state)));
2734
3010
  });
2735
3011
  } else {
2736
- el.style.setProperty(prop, transposeCanvasUnits(scalar));
3012
+ el.style.setProperty(prop, canvasStyleValue(scalar));
2737
3013
  }
2738
3014
  } else if (isTemplateString(val)) {
2739
3015
  effect(() => {
2740
- el.style[prop] = transposeCanvasUnits(evaluateTemplate(val, state));
3016
+ el.style[prop] = canvasStyleValue(evaluateTemplate(val, state));
2741
3017
  });
2742
3018
  } else if (mediaOverriddenProps.has(prop)) {
2743
3019
  baseDecls[prop] = scalar;
2744
3020
  } else {
2745
- el.style[prop] = transposeCanvasUnits(scalar);
3021
+ el.style[prop] = canvasStyleValue(scalar);
2746
3022
  }
2747
3023
  }
2748
3024
  const hasNested = Object.keys(nested).length > 0;
@@ -2801,7 +3077,7 @@ function applyStyle(el, styleDef, mediaQueries = {}, state = {}) {
2801
3077
  if (key === "@--") {
2802
3078
  continue;
2803
3079
  }
2804
- const query = key.startsWith("@--") ? mediaQueries[key.slice(1)] ?? key.slice(1) : key.startsWith("@(") ? key.slice(1) : null;
3080
+ const query = resolveAtQuery(key, mediaQueries);
2805
3081
  const atRule = query === null ? key : `@media ${query}`;
2806
3082
  const scope = `[data-jx="${uid}"]`;
2807
3083
  const scheme = query === null ? null : pureSchemeOf(query);
@@ -2834,15 +3110,35 @@ function reapplyStyle(el, styleDef, mediaQueries = {}, state = {}) {
2834
3110
  delete el.dataset.jx;
2835
3111
  applyStyle(el, styleDef ?? {}, mediaQueries, state);
2836
3112
  }
3113
+ var ENUMERATED_ATTRS = new Set(["contenteditable", "draggable", "spellcheck"]);
3114
+ function booleanAttrValue(name, value) {
3115
+ const lower = name.toLowerCase();
3116
+ if (lower.startsWith("aria-") || ENUMERATED_ATTRS.has(lower)) {
3117
+ return String(value);
3118
+ }
3119
+ return value ? "" : null;
3120
+ }
2837
3121
  function applyAttributes(el, attrs, state) {
2838
3122
  for (const [k, v] of Object.entries(attrs)) {
2839
3123
  const attr = canvasAttrName(el, k);
3124
+ const write = (resolved) => {
3125
+ if (typeof resolved === "boolean") {
3126
+ const text = booleanAttrValue(attr, resolved);
3127
+ if (text === null) {
3128
+ el.removeAttribute(attr);
3129
+ } else {
3130
+ el.setAttribute(attr, text);
3131
+ }
3132
+ return;
3133
+ }
3134
+ el.setAttribute(attr, canvasAssetValue(el.tagName, k, String(resolved ?? "")));
3135
+ };
2840
3136
  if (isRefObj(v)) {
2841
- effect(() => el.setAttribute(attr, String(resolveRef(v.$ref, state) ?? "")));
3137
+ effect(() => write(resolveRef(v.$ref, state)));
2842
3138
  } else if (isTemplateString(v)) {
2843
- effect(() => el.setAttribute(attr, String(evaluateTemplate(v, state))));
3139
+ effect(() => write(evaluateAttrTemplate(v, state)));
2844
3140
  } else {
2845
- el.setAttribute(attr, String(v));
3141
+ write(v);
2846
3142
  }
2847
3143
  }
2848
3144
  }
@@ -3515,7 +3811,18 @@ function camelToKebab(s) {
3515
3811
  return s.replaceAll(/[A-Z]/g, (c) => `-${c.toLowerCase()}`);
3516
3812
  }
3517
3813
  function toCSSText(rules) {
3518
- return Object.entries(rules).filter(([k, v]) => !isNestedSelector(k) && (v === null || typeof v !== "object")).map(([p, v]) => `${camelToKebab(p)}: ${transposeCanvasUnits(String(v))}`).join("; ");
3814
+ return Object.entries(rules).filter(([k, v]) => !isNestedSelector(k) && (v === null || typeof v !== "object")).map(([p, v]) => `${camelToKebab(p)}: ${canvasStyleValue(String(v))}`).join("; ");
3815
+ }
3816
+ var MEDIA_TYPES = new Set(["all", "print", "screen", "speech"]);
3817
+ function resolveAtQuery(atKey, mediaQueries) {
3818
+ if (atKey.startsWith("@--")) {
3819
+ return mediaQueries[atKey.slice(1)] ?? atKey.slice(1);
3820
+ }
3821
+ if (atKey.startsWith("@(")) {
3822
+ const inner = atKey.slice(2, -1).trim();
3823
+ return MEDIA_TYPES.has(inner.toLowerCase()) ? inner : atKey.slice(1);
3824
+ }
3825
+ return null;
3519
3826
  }
3520
3827
  var COLOR_SCHEME_ATTR = "data-color-scheme";
3521
3828
  function pureSchemeOf(query) {
@@ -3666,6 +3973,9 @@ async function defineElement(source, baseUrl) {
3666
3973
  }
3667
3974
  applyStyle(this, def2.style ?? {}, state["$media"] ?? {}, state);
3668
3975
  applyAttributes(this, def2.attributes ?? {}, state);
3976
+ if (!Array.isArray(def2.children) && def2.textContent !== undefined) {
3977
+ bindProperty(this, "textContent", def2.textContent, state);
3978
+ }
3669
3979
  const children = Array.isArray(def2.children) ? def2.children : [];
3670
3980
  for (const childDef of children) {
3671
3981
  this.append(renderNode(childDef, state));
@@ -3712,19 +4022,21 @@ function renderCustomElementWithProps(def2, state, options, path) {
3712
4022
  if (refusePrivateProp(key, "$props")) {
3713
4023
  continue;
3714
4024
  }
4025
+ const write = (resolved) => {
4026
+ el[key] = typeof resolved === "string" ? canvasAssetValue(el.tagName, key, resolved) : resolved;
4027
+ };
3715
4028
  if (isRefObj(val)) {
3716
4029
  const refVal = val;
3717
- const resolved = resolveRef(refVal.$ref, state);
3718
- el[key] = resolved;
4030
+ write(resolveRef(refVal.$ref, state));
3719
4031
  effect(() => {
3720
- el[key] = resolveRef(refVal.$ref, state);
4032
+ write(resolveRef(refVal.$ref, state));
3721
4033
  });
3722
4034
  } else if (isTemplateString(val)) {
3723
4035
  effect(() => {
3724
- el[key] = evaluateTemplate(val, state);
4036
+ write(evaluateTemplate(val, state));
3725
4037
  });
3726
4038
  } else {
3727
- el[key] = val;
4039
+ write(val);
3728
4040
  }
3729
4041
  }
3730
4042
  applyStyle(el, def2.style ?? {}, state["$media"] ?? {}, state);
@@ -3769,6 +4081,47 @@ function distributeSlots(host, slottedChildren) {
3769
4081
  }
3770
4082
  }
3771
4083
 
4084
+ // src/canvas/asset-resolve.ts
4085
+ function resolveAssetRef(value, ctx) {
4086
+ if (!ctx || value === "") {
4087
+ return null;
4088
+ }
4089
+ if (ctx.space === "repo") {
4090
+ if (!ctx.fileBaseUrl) {
4091
+ return null;
4092
+ }
4093
+ const path = projectPathForRef(value, ctx.documentDir, ctx.mounts, ctx.lanes);
4094
+ if (path === null) {
4095
+ return null;
4096
+ }
4097
+ const { suffix } = splitRefSuffix(value);
4098
+ return `${ctx.fileBaseUrl}${encodeProjectPath(path)}${suffix}`;
4099
+ }
4100
+ const [mount] = ctx.mounts;
4101
+ return mount ? mountedRefFor(value, ctx.documentDir, mount) : null;
4102
+ }
4103
+ function mountedRefFor(value, entryDir, mount) {
4104
+ if (isNonRelativeRef(value)) {
4105
+ return null;
4106
+ }
4107
+ const { path: bare, suffix } = splitRefSuffix(value);
4108
+ let decoded;
4109
+ try {
4110
+ decoded = decodeURIComponent(bare);
4111
+ } catch {
4112
+ return null;
4113
+ }
4114
+ const target = joinProjectPath(entryDir, decoded);
4115
+ if (target === null) {
4116
+ return null;
4117
+ }
4118
+ const url = assetUrlFor([mount], target);
4119
+ if (!url) {
4120
+ return null;
4121
+ }
4122
+ return `${url}${suffix}`;
4123
+ }
4124
+
3772
4125
  // src/canvas/path-mapping.ts
3773
4126
  function wrapsPageContent(path, prefix) {
3774
4127
  if (!prefix) {
@@ -3818,7 +4171,7 @@ function parseJxPath(serialized) {
3818
4171
  return JSON.parse(serialized);
3819
4172
  }
3820
4173
 
3821
- // src/canvas/site-style-css.ts
4174
+ // ../site/src/site-style.ts
3822
4175
  var SITE_STYLE_ID = "jx-site-style";
3823
4176
  function toDecls(props, transpose) {
3824
4177
  return Object.entries(props).map(([key, value]) => `${camelToKebab(key)}: ${transpose(String(value))}`).join("; ");
@@ -3853,7 +4206,7 @@ function buildSiteStyleCSS(siteStyle, mediaQueries, transpose) {
3853
4206
  rules.push(`body { ${bodyCSS} }`);
3854
4207
  }
3855
4208
  for (const [atKey, block] of condBlocks) {
3856
- const query = atKey.startsWith("@--") ? mediaQueries[atKey.slice(1)] ?? atKey.slice(1) : atKey.startsWith("@(") ? atKey.slice(1) : null;
4209
+ const query = resolveAtQuery(atKey, mediaQueries);
3857
4210
  if (query === null) {
3858
4211
  continue;
3859
4212
  }
@@ -3942,7 +4295,7 @@ var EDIT_PLACEHOLDER_CSS = `
3942
4295
  font-size: 0;
3943
4296
  overflow: hidden;
3944
4297
  }
3945
- .empty-text-placeholder:not([contenteditable])::after {
4298
+ .empty-text-placeholder:is(:empty, :has(> br:only-child)):not([data-jx-active-block]):not([contenteditable])::after {
3946
4299
  content: "Click here to add text...";
3947
4300
  color: color-mix(in srgb, #808080 40%, transparent);
3948
4301
  font-style: italic;
@@ -3968,7 +4321,7 @@ var EDIT_PLACEHOLDER_CSS = `
3968
4321
  pointer-events: none;
3969
4322
  white-space: nowrap;
3970
4323
  }
3971
- [data-jx-active-block]:empty::after {
4324
+ [data-jx-active-block]:is(:empty, :has(> br:only-child))::after {
3972
4325
  content: "Type / for commands";
3973
4326
  color: color-mix(in srgb, #808080 40%, transparent);
3974
4327
  font-style: italic;
@@ -4157,7 +4510,7 @@ function applySiteStyle(siteStyle, mediaQueries = {}) {
4157
4510
  existing?.remove();
4158
4511
  return;
4159
4512
  }
4160
- const css = buildSiteStyleCSS(siteStyle, mediaQueries, transposeCanvasUnits);
4513
+ const css = buildSiteStyleCSS(siteStyle, mediaQueries, canvasStyleValue);
4161
4514
  if (existing) {
4162
4515
  existing.textContent = css;
4163
4516
  return;
@@ -4174,7 +4527,7 @@ function applyPreviewColorScheme(doc, scheme) {
4174
4527
  delete doc.documentElement.dataset.colorScheme;
4175
4528
  }
4176
4529
  }
4177
- function injectHead(doc) {
4530
+ function injectHead(doc, assets = null) {
4178
4531
  const head = doc.$head;
4179
4532
  if (!Array.isArray(head)) {
4180
4533
  return;
@@ -4190,9 +4543,14 @@ function injectHead(doc) {
4190
4543
  const attrs = { ...entry.attributes };
4191
4544
  for (const key of ["href", "src"]) {
4192
4545
  const val = attrs[key];
4193
- if (typeof val === "string" && !val.startsWith("/") && !val.startsWith(".") && !val.startsWith("http")) {
4546
+ if (typeof val !== "string" || val === "") {
4547
+ continue;
4548
+ }
4549
+ if (!val.startsWith("/") && !val.startsWith(".") && !val.startsWith("http")) {
4194
4550
  attrs[key] = `/node_modules/${val}`;
4551
+ continue;
4195
4552
  }
4553
+ attrs[key] = resolveAssetRef(val, assets) ?? val;
4196
4554
  }
4197
4555
  const sel = `${tag}${attrs.href ? `[href="${String(attrs.href)}"]` : ""}${attrs.src ? `[src="${String(attrs.src)}"]` : ""}`;
4198
4556
  if (sel !== tag && document.head.querySelector(sel)) {
@@ -4269,13 +4627,15 @@ function installCanvasImageRetry(root, maxAttempts = 3) {
4269
4627
  return () => root.removeEventListener("error", onError, true);
4270
4628
  }
4271
4629
  async function renderResolvedDocument(opts) {
4630
+ const assets = opts.assets ?? null;
4631
+ setCanvasAssetResolver(assets ? (value) => resolveAssetRef(value, assets) : null);
4272
4632
  setSkipServerFunctions(opts.mode !== "preview");
4273
4633
  setSkipAutoRequests(opts.mode !== "preview" && !opts.allowAutoRequests);
4274
4634
  setCanvasViewportTranspose(true);
4275
4635
  setCanvasDelinkAnchors(opts.mode !== "preview");
4276
4636
  setStampPropBindings(opts.mode === "design" || opts.mode === "edit");
4277
4637
  applySiteStyle(opts.siteStyle, opts.doc.$media ?? {});
4278
- injectHead(opts.doc);
4638
+ injectHead(opts.doc, assets);
4279
4639
  syncEditModeCss(opts.container.ownerDocument, opts.mode);
4280
4640
  syncPreviewShell(opts.container.ownerDocument, opts.mode);
4281
4641
  syncStylebookCss(opts.container.ownerDocument, opts.mode);
@@ -7080,6 +7440,42 @@ function startIframeSlashBridge(channel, doc) {
7080
7440
 
7081
7441
  // src/commands/levels.ts
7082
7442
  var KEY_SCOPES = ["global", "canvas", "caret", "grid", "code", "dock", "palette"];
7443
+ var PLACEMENT_MATRIX = {
7444
+ "commandbar/primary": {
7445
+ admits: ["application", "document"],
7446
+ note: "document only for Save / Undo / Redo / Open in Browser, by frequency; ≤5 total"
7447
+ },
7448
+ "commandbar/overflow": {
7449
+ admits: ["application", "project", "document"],
7450
+ note: "never selection — the Command Bar is not a selection surface"
7451
+ },
7452
+ "statusbar/project": { admits: ["project"], note: "the status bar's left field" },
7453
+ "statusbar/document": { admits: ["document"], note: "the status bar's centre field" },
7454
+ "statusbar/selection": { admits: ["selection"], note: "the status bar's right field" },
7455
+ "context/element": { admits: ["selection"], note: "the canvas element menu acts on a selection" },
7456
+ "context/file": { admits: ["project"], note: "a file row addresses the project's file set" },
7457
+ "context/layer": { admits: ["selection"], note: "an outline row IS a selection" },
7458
+ "context/tab": { admits: ["document"], note: "a tab addresses one document" },
7459
+ "context/pane": { admits: ["document"], note: "a pane hosts one document" },
7460
+ blockbar: { admits: ["selection"], note: "the floating bar owns selection-scoped verbs" },
7461
+ "blockbar/format": {
7462
+ admits: ["selection"],
7463
+ note: "the bar's inline-format cluster — a range inside the selection is still the selection"
7464
+ },
7465
+ "outline/row": { admits: ["selection"], note: "row actions act on the row's node" },
7466
+ "settings/menu": {
7467
+ admits: ["application", "project"],
7468
+ note: "the rail foot's gear menu — the settings family. It is a MENU, not a pinned slot, so it " + "may host two levels for the reason commandbar/overflow does: a menu prints each row's own " + "name, chord and gate, and draws a divider where the level changes. The rail's PINNED " + "groups stay single-level, in PANEL_PLACEMENT_MATRIX"
7469
+ },
7470
+ palette: {
7471
+ admits: ["application", "project", "document", "selection"],
7472
+ note: "the level-agnostic surface; it groups rows by level"
7473
+ },
7474
+ never: {
7475
+ admits: ["application", "project", "document", "selection"],
7476
+ note: "keyboard- and API-only; no rendered surface to be misplaced in"
7477
+ }
7478
+ };
7083
7479
 
7084
7480
  // src/commands/keymap.ts
7085
7481
  var MODIFIER_ORDER = ["mod", "ctrl", "alt", "shift"];
@@ -7366,11 +7762,18 @@ function applyIframePatch(shadowDoc, forwardOps, container, ctx, echoPaths) {
7366
7762
  const echoed = new Set(echoPaths.map((p) => serializeJxPath(p)));
7367
7763
  for (const op of forwardOps) {
7368
7764
  if (op.op === "set-key" && echoed.has(serializeJxPath(op.path))) {
7765
+ syncEchoedPlaceholder(shadowDoc, op.path, container);
7369
7766
  continue;
7370
7767
  }
7371
7768
  applyOpToDom(shadowDoc, op, container, ctx);
7372
7769
  }
7373
7770
  }
7771
+ function syncEchoedPlaceholder(doc, path, container) {
7772
+ const el = findElement(container, path);
7773
+ if (el) {
7774
+ syncPlaceholderClass(el, getNodeAtPath(doc, path));
7775
+ }
7776
+ }
7374
7777
  function requireCtx(ctx) {
7375
7778
  if (!ctx) {
7376
7779
  throw new Error("iframe-patch-no-render-ctx");
@@ -7421,7 +7824,7 @@ function insertChild(doc, parentPath, index, container, ctx) {
7421
7824
  const newEl = renderSubtreeIframe(doc, [...parentPath, "children", index], ctx);
7422
7825
  remapChildPaths(container, parentPath, index, 1);
7423
7826
  insertAt(parentEl, newEl, domChildReference(parentEl, parentNode, index));
7424
- syncContainerPlaceholder(parentEl, parentNode);
7827
+ syncPlaceholderClass(parentEl, parentNode);
7425
7828
  }
7426
7829
  function replaceSubtree(doc, docPath, container, ctx) {
7427
7830
  const oldEl = requireElement(container, docPath);
@@ -7436,7 +7839,7 @@ function removeChild(doc, parentPath, index, container) {
7436
7839
  el.remove();
7437
7840
  remapChildPaths(container, parentPath, index + 1, -1);
7438
7841
  if (parentEl) {
7439
- syncContainerPlaceholder(parentEl, getNodeAtPath(doc, parentPath));
7842
+ syncPlaceholderClass(parentEl, getNodeAtPath(doc, parentPath));
7440
7843
  }
7441
7844
  }
7442
7845
  function moveChild(doc, fromParentPath, fromIndex, toParentPath, toIndex, container) {
@@ -7452,17 +7855,17 @@ function moveChild(doc, fromParentPath, fromIndex, toParentPath, toIndex, contai
7452
7855
  const toParentNode = getNodeAtPath(doc, toPrefix);
7453
7856
  insertAt(toParentEl, el, domChildReference(toParentEl, toParentNode, toIndex));
7454
7857
  if (fromParentEl) {
7455
- syncContainerPlaceholder(fromParentEl, getNodeAtPath(doc, elementPath(fromParentEl) ?? fromParentPath));
7858
+ syncPlaceholderClass(fromParentEl, getNodeAtPath(doc, elementPath(fromParentEl) ?? fromParentPath));
7456
7859
  }
7457
- syncContainerPlaceholder(toParentEl, toParentNode);
7860
+ syncPlaceholderClass(toParentEl, toParentNode);
7458
7861
  }
7459
- function syncContainerPlaceholder(parentEl, parentNode) {
7862
+ function syncPlaceholderClass(el, node) {
7460
7863
  for (const cls of EMPTY_PLACEHOLDER_CLASSES) {
7461
- parentEl.classList.remove(cls);
7864
+ el.classList.remove(cls);
7462
7865
  }
7463
- const placeholder = parentNode ? computeEmptyPlaceholderClass(parentNode) : null;
7866
+ const placeholder = node ? computeEmptyPlaceholderClass(node) : null;
7464
7867
  if (placeholder) {
7465
- parentEl.classList.add(placeholder);
7868
+ el.classList.add(placeholder);
7466
7869
  }
7467
7870
  }
7468
7871
  function insertAt(parentEl, node, ref2) {
@@ -7520,22 +7923,20 @@ function patchText(doc, path, container) {
7520
7923
  const el = requireElement(container, path);
7521
7924
  const node = getNodeAtPath(doc, path);
7522
7925
  el.textContent = textDisplayValue(node);
7523
- for (const cls of EMPTY_PLACEHOLDER_CLASSES) {
7524
- el.classList.remove(cls);
7525
- }
7526
- const placeholder = node ? computeEmptyPlaceholderClass(node) : null;
7527
- if (placeholder) {
7528
- el.classList.add(placeholder);
7529
- }
7926
+ syncPlaceholderClass(el, node);
7530
7927
  }
7531
7928
  function requireElement(container, path) {
7929
+ const el = findElement(container, path);
7930
+ if (!el) {
7931
+ throw new Error(`iframe-patch-element-not-found:${serializeJxPath(path)}`);
7932
+ }
7933
+ return el;
7934
+ }
7935
+ function findElement(container, path) {
7532
7936
  const serialized = serializeJxPath(path);
7533
7937
  const esc = serialized.replaceAll("\\", String.raw`\\`).replaceAll("'", String.raw`\'`);
7534
7938
  const el = container.querySelector(`[data-jx-path='${esc}']`);
7535
- if (!(el instanceof HTMLElement)) {
7536
- throw new Error(`iframe-patch-element-not-found:${serialized}`);
7537
- }
7538
- return el;
7939
+ return el instanceof HTMLElement ? el : null;
7539
7940
  }
7540
7941
  function editModeStyle(style) {
7541
7942
  if (!style || typeof style !== "object") {
@@ -8165,6 +8566,7 @@ function startCanvasIframe(opts) {
8165
8566
  stopDataScopeWatch = null;
8166
8567
  handle?.dispose();
8167
8568
  handle = await renderResolvedDocument({
8569
+ assets: msg.assets ?? null,
8168
8570
  container,
8169
8571
  doc: msg.doc,
8170
8572
  docBase: msg.docBase,
@@ -8262,5 +8664,5 @@ export {
8262
8664
  startCanvasIframe
8263
8665
  };
8264
8666
 
8265
- //# debugId=2229405F4A023FB664756E2164756E21
8667
+ //# debugId=1D7D4718E26F550F64756E2164756E21
8266
8668
  //# sourceMappingURL=iframe-entry.js.map