@jxsuite/studio 2.0.0 → 2.1.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 (419) hide show
  1. package/canvas.html +21 -0
  2. package/dist/chunks/Overlay.dev-qfjzkms8.js +23 -0
  3. package/dist/chunks/{Overlay.dev-cszx7yn5.js.map → Overlay.dev-qfjzkms8.js.map} +1 -1
  4. package/dist/chunks/{adapter-z8fkmbmv.js → adapter-qfnesn5v.js} +2 -2
  5. package/dist/chunks/{cf-settings-mmyv436d.js → cf-settings-73sh5z4p.js} +3 -3
  6. package/dist/chunks/{components-0hzentwq.js → components-2bpqp4mq.js} +5 -7
  7. package/dist/chunks/{components-0hzentwq.js.map → components-2bpqp4mq.js.map} +1 -1
  8. package/dist/chunks/content-source-34gnkbnn.js +38 -0
  9. package/dist/chunks/content-source-34gnkbnn.js.map +9 -0
  10. package/dist/chunks/convert-to-repeater-t8kxre5s.js +258 -0
  11. package/dist/chunks/convert-to-repeater-t8kxre5s.js.map +10 -0
  12. package/dist/chunks/extension-sections-6mqydz35.js +301 -0
  13. package/dist/chunks/extension-sections-6mqydz35.js.map +11 -0
  14. package/dist/chunks/files-8kn321h1.js +70 -0
  15. package/dist/chunks/files-8kn321h1.js.map +9 -0
  16. package/dist/chunks/{format-host-2c188n41.js → format-host-mkwsmt8d.js} +3 -3
  17. package/dist/chunks/git-panel-tjbr191d.js +51 -0
  18. package/dist/chunks/git-panel-tjbr191d.js.map +9 -0
  19. package/dist/chunks/{index-baeg2x3y.js → index-7pxchcce.js} +4 -3
  20. package/dist/chunks/{index-baeg2x3y.js.map → index-7pxchcce.js.map} +3 -3
  21. package/dist/chunks/{jx-validate-qx8degj7.js → jx-validate-v6qgknrs.js} +3 -3
  22. package/dist/chunks/layers-panel-87pb25km.js +58 -0
  23. package/dist/chunks/layers-panel-87pb25km.js.map +9 -0
  24. package/dist/chunks/{monaco-setup-zjdp9zdw.js → monaco-setup-e0yh25j4.js} +4 -75
  25. package/dist/chunks/{monaco-setup-zjdp9zdw.js.map → monaco-setup-e0yh25j4.js.map} +3 -4
  26. package/dist/chunks/publish-panel-m0fcgdrt.js +21 -0
  27. package/dist/chunks/publish-panel-m0fcgdrt.js.map +9 -0
  28. package/dist/chunks/quick-search-rhs6cbsb.js +53 -0
  29. package/dist/chunks/quick-search-rhs6cbsb.js.map +9 -0
  30. package/dist/chunks/right-panel-zpdxd98t.js +37 -0
  31. package/dist/chunks/right-panel-zpdxd98t.js.map +9 -0
  32. package/dist/chunks/section-registry-zmckaxf4.js +30 -0
  33. package/dist/chunks/section-registry-zmckaxf4.js.map +9 -0
  34. package/dist/chunks/sp-overlay.dev-9ran0sfq.js +18 -0
  35. package/dist/chunks/sp-overlay.dev-9ran0sfq.js.map +9 -0
  36. package/dist/chunks/sp-popover.dev-rebmdncr.js +9 -0
  37. package/dist/chunks/sp-popover.dev-rebmdncr.js.map +9 -0
  38. package/dist/chunks/sp-progress-circle.dev-5vkppfkt.js +15 -0
  39. package/dist/chunks/sp-progress-circle.dev-5vkppfkt.js.map +9 -0
  40. package/dist/chunks/sp-tooltip.dev-ykqrg8h5.js +15 -0
  41. package/dist/chunks/sp-tooltip.dev-ykqrg8h5.js.map +9 -0
  42. package/dist/chunks/{sp-tray.dev-pb2fsd65.js → sp-tray.dev-ey59vfh7.js} +10 -8
  43. package/dist/chunks/{sp-tray.dev-pb2fsd65.js.map → sp-tray.dev-ey59vfh7.js.map} +2 -2
  44. package/dist/chunks/{studio-9qr6fq7p.js → studio-0rqqhrdy.js} +10 -378
  45. package/dist/chunks/{studio-9qr6fq7p.js.map → studio-0rqqhrdy.js.map} +4 -14
  46. package/dist/chunks/{studio-5x6tqqec.js → studio-2sj1jj3v.js} +10 -8
  47. package/dist/chunks/{studio-5x6tqqec.js.map → studio-2sj1jj3v.js.map} +2 -2
  48. package/dist/chunks/{studio-h3fw0wn8.js → studio-3fk1ac9z.js} +6 -4
  49. package/dist/chunks/{studio-h3fw0wn8.js.map → studio-3fk1ac9z.js.map} +2 -2
  50. package/dist/chunks/studio-41exgzhq.js +6690 -0
  51. package/dist/chunks/studio-41exgzhq.js.map +38 -0
  52. package/dist/chunks/{studio-7v2bpweq.js → studio-4mqb9a0j.js} +6 -4
  53. package/dist/chunks/{studio-7v2bpweq.js.map → studio-4mqb9a0j.js.map} +2 -2
  54. package/dist/chunks/studio-50k77cca.js +1787 -0
  55. package/dist/chunks/studio-50k77cca.js.map +18 -0
  56. package/dist/chunks/{studio-56v3zwk6.js → studio-6a0h7sne.js} +10 -8
  57. package/dist/chunks/{studio-56v3zwk6.js.map → studio-6a0h7sne.js.map} +2 -2
  58. package/dist/chunks/studio-6f0am8f2.js +53 -0
  59. package/dist/chunks/studio-6f0am8f2.js.map +10 -0
  60. package/dist/chunks/studio-6nmjc5qk.js +7002 -0
  61. package/dist/chunks/studio-6nmjc5qk.js.map +32 -0
  62. package/dist/chunks/studio-7afd21nw.js +67 -0
  63. package/dist/chunks/studio-7afd21nw.js.map +10 -0
  64. package/dist/chunks/{studio-xze4zrs1.js → studio-7bmdvzne.js} +2 -2
  65. package/dist/chunks/studio-7bmdvzne.js.map +10 -0
  66. package/dist/chunks/studio-8jpzqk5s.js +128 -0
  67. package/dist/chunks/studio-8jpzqk5s.js.map +10 -0
  68. package/dist/chunks/studio-94m77tgk.js +1482 -0
  69. package/dist/chunks/studio-94m77tgk.js.map +13 -0
  70. package/dist/chunks/studio-9je6w5sq.js +1000 -0
  71. package/dist/chunks/studio-9je6w5sq.js.map +16 -0
  72. package/dist/chunks/{studio-w7c6kvk7.js → studio-a7pzveq1.js} +2 -2
  73. package/dist/chunks/{studio-97qcgvdn.js → studio-adn0z1k3.js} +14 -8
  74. package/dist/chunks/{studio-97qcgvdn.js.map → studio-adn0z1k3.js.map} +3 -3
  75. package/dist/chunks/{studio-kr88ryr7.js → studio-bsxxx3x2.js} +17 -15
  76. package/dist/chunks/{studio-kr88ryr7.js.map → studio-bsxxx3x2.js.map} +7 -6
  77. package/dist/chunks/studio-bw5dz6km.js +608 -0
  78. package/dist/chunks/studio-bw5dz6km.js.map +21 -0
  79. package/dist/chunks/studio-c6s5getc.js +4232 -0
  80. package/dist/chunks/studio-c6s5getc.js.map +39 -0
  81. package/dist/chunks/studio-cjm7fdjf.js +451 -0
  82. package/dist/chunks/studio-cjm7fdjf.js.map +14 -0
  83. package/dist/chunks/{studio-cx91mnh8.js → studio-ckwz4d3x.js} +53 -53
  84. package/dist/chunks/{studio-cx91mnh8.js.map → studio-ckwz4d3x.js.map} +5 -5
  85. package/dist/chunks/studio-dg7zyt5x.js +28 -0
  86. package/dist/chunks/studio-dg7zyt5x.js.map +10 -0
  87. package/dist/chunks/studio-dzcyvvvz.js +166 -0
  88. package/dist/chunks/studio-dzcyvvvz.js.map +10 -0
  89. package/dist/chunks/{studio-7kqe41sa.js → studio-evkr5d3b.js} +15 -7
  90. package/dist/chunks/{studio-7kqe41sa.js.map → studio-evkr5d3b.js.map} +5 -4
  91. package/dist/chunks/studio-f12h6177.js +411 -0
  92. package/dist/chunks/studio-f12h6177.js.map +13 -0
  93. package/dist/chunks/studio-hbp1s8m9.js +1817 -0
  94. package/dist/chunks/studio-hbp1s8m9.js.map +14 -0
  95. package/dist/chunks/studio-k2sxjph5.js +15295 -0
  96. package/dist/chunks/studio-k2sxjph5.js.map +235 -0
  97. package/dist/chunks/studio-myje51t3.js +385 -0
  98. package/dist/chunks/studio-myje51t3.js.map +12 -0
  99. package/dist/chunks/studio-na6rwzhw.js +353 -0
  100. package/dist/chunks/studio-na6rwzhw.js.map +11 -0
  101. package/dist/chunks/{studio-4k0m4fmp.js → studio-p471gg5n.js} +126 -3
  102. package/dist/chunks/studio-p471gg5n.js.map +14 -0
  103. package/dist/chunks/{studio-9bjwf0d4.js → studio-rgwcpast.js} +5 -46
  104. package/dist/chunks/{studio-9bjwf0d4.js.map → studio-rgwcpast.js.map} +4 -5
  105. package/dist/chunks/{studio-hhj92gcr.js → studio-s519zc99.js} +3 -3
  106. package/dist/chunks/studio-sas7mt3k.js +75 -0
  107. package/dist/chunks/studio-sas7mt3k.js.map +10 -0
  108. package/dist/chunks/studio-t5wqwzdh.js +74 -0
  109. package/dist/chunks/studio-t5wqwzdh.js.map +10 -0
  110. package/dist/chunks/{studio-9qpspwfn.js → studio-temv5bw4.js} +5 -5
  111. package/dist/chunks/{studio-9qpspwfn.js.map → studio-temv5bw4.js.map} +1 -1
  112. package/dist/chunks/{studio-qkvt37c0.js → studio-tr20m8g8.js} +669 -18
  113. package/dist/chunks/studio-tr20m8g8.js.map +9 -0
  114. package/dist/chunks/{studio-ygzav7z5.js → studio-v96qfnww.js} +5514 -4291
  115. package/dist/chunks/studio-v96qfnww.js.map +54 -0
  116. package/dist/chunks/studio-vcetc91k.js +659 -0
  117. package/dist/chunks/studio-vcetc91k.js.map +10 -0
  118. package/dist/chunks/{studio-n9zmpxjg.js → studio-vmymjefp.js} +2972 -4937
  119. package/dist/chunks/studio-vmymjefp.js.map +98 -0
  120. package/dist/chunks/{studio-bghqfmea.js → studio-wwseqyw0.js} +2 -2
  121. package/dist/chunks/studio-x9hmy5kr.js +390 -0
  122. package/dist/chunks/studio-x9hmy5kr.js.map +10 -0
  123. package/dist/chunks/studio-xa0ce0n7.js +335 -0
  124. package/dist/chunks/studio-xa0ce0n7.js.map +10 -0
  125. package/dist/chunks/studio-xm3dbxvn.js +378 -0
  126. package/dist/chunks/studio-xm3dbxvn.js.map +19 -0
  127. package/dist/chunks/{studio-nqw433kv.js → studio-xmvbhg31.js} +2 -2
  128. package/dist/chunks/studio-zv37yrfa.js +1054 -0
  129. package/dist/chunks/studio-zv37yrfa.js.map +13 -0
  130. package/dist/chunks/{ws-client-q7hkz3j9.js → ws-client-654ts66j.js} +6 -4
  131. package/dist/chunks/ws-client-654ts66j.js.map +11 -0
  132. package/dist/iframe-entry.js +668 -230
  133. package/dist/studio.js +23711 -35185
  134. package/index.html +35 -4685
  135. package/package.json +17 -15
  136. package/src/about/about-modal.ts +42 -11
  137. package/src/browse/library-commands.ts +240 -0
  138. package/src/browse/library-layouts.ts +369 -0
  139. package/src/browse/library-model.ts +396 -0
  140. package/src/browse/library-pane.ts +1049 -0
  141. package/src/browse/library-preview.ts +252 -0
  142. package/src/browse/library-source.ts +150 -0
  143. package/src/canvas/canvas-helpers.ts +33 -9
  144. package/src/canvas/canvas-live-render.ts +45 -29
  145. package/src/canvas/canvas-patcher.ts +151 -31
  146. package/src/canvas/canvas-perf.ts +46 -7
  147. package/src/canvas/canvas-render.ts +1092 -220
  148. package/src/canvas/canvas-surface.ts +240 -0
  149. package/src/canvas/canvas-utils.ts +1166 -124
  150. package/src/canvas/iframe-drop.ts +2 -1
  151. package/src/canvas/iframe-entry.ts +241 -4
  152. package/src/canvas/iframe-host.ts +1226 -143
  153. package/src/canvas/iframe-inline-edit.ts +31 -0
  154. package/src/canvas/iframe-interaction.ts +76 -16
  155. package/src/canvas/iframe-keys.ts +107 -45
  156. package/src/canvas/iframe-overlay.ts +38 -1
  157. package/src/canvas/iframe-protocol.ts +137 -2
  158. package/src/canvas/iframe-render.ts +70 -2
  159. package/src/canvas/iframe-slash.ts +6 -0
  160. package/src/canvas/path-mapping.ts +56 -6
  161. package/src/canvas/surface-registry.ts +292 -0
  162. package/src/collab/collab-commands.ts +244 -0
  163. package/src/collab/collab-session.ts +198 -32
  164. package/src/collab/collab-state.ts +28 -2
  165. package/src/collab/presence-chips.ts +100 -9
  166. package/src/commands/active-registry.ts +42 -0
  167. package/src/commands/app-commands.ts +176 -0
  168. package/src/commands/budget.ts +158 -0
  169. package/src/commands/command-args.ts +253 -0
  170. package/src/commands/context.ts +387 -0
  171. package/src/commands/defaults.ts +580 -0
  172. package/src/commands/keymap.ts +571 -0
  173. package/src/commands/levels.ts +345 -0
  174. package/src/commands/live-context.ts +193 -0
  175. package/src/commands/reference.ts +227 -0
  176. package/src/commands/registry.ts +293 -0
  177. package/src/component-props.ts +3 -2
  178. package/src/content/draft-state.ts +106 -0
  179. package/src/content/entry-commands.ts +282 -0
  180. package/src/content/entry-editor.ts +327 -0
  181. package/src/content/entry-fields.ts +116 -0
  182. package/src/content/entry-model.ts +175 -0
  183. package/src/editor/canvas-context-menu.ts +17 -11
  184. package/src/editor/canvas-slash-bridge.ts +11 -4
  185. package/src/editor/context-menu.ts +804 -204
  186. package/src/editor/convert-to-component.ts +31 -10
  187. package/src/editor/convert-to-repeater.ts +3 -2
  188. package/src/editor/inline-edit-apply.ts +5 -5
  189. package/src/editor/inline-edit.ts +86 -50
  190. package/src/editor/insert-zone-action.ts +1 -1
  191. package/src/editor/shortcuts.ts +1043 -329
  192. package/src/editor/slash-menu.ts +1 -0
  193. package/src/files/elements.ts +166 -0
  194. package/src/files/file-ops.ts +178 -31
  195. package/src/files/files.ts +898 -268
  196. package/src/files/fs-events.ts +21 -0
  197. package/src/files/media-meta.ts +203 -0
  198. package/src/files/media-paths.ts +141 -0
  199. package/src/files/media-upload.ts +7 -3
  200. package/src/files/media-usage.ts +189 -0
  201. package/src/github/github-auth.ts +235 -33
  202. package/src/github/github-publish.ts +80 -21
  203. package/src/grid/grid-controller.ts +152 -12
  204. package/src/grid/grid-layout.ts +225 -20
  205. package/src/grid/grid-open.ts +120 -0
  206. package/src/grid/grid-panel.ts +554 -39
  207. package/src/grid/grid-source.ts +27 -1
  208. package/src/grid/redirects-grid.ts +599 -0
  209. package/src/grid/redirects.ts +398 -0
  210. package/src/grid/sources/content-source.ts +108 -2
  211. package/src/i18n/i18n-commands.ts +322 -0
  212. package/src/new-project/add-repo-modal.ts +8 -10
  213. package/src/new-project/import-tab.ts +34 -9
  214. package/src/new-project/location-fields.ts +9 -1
  215. package/src/new-project/new-project-modal.ts +270 -262
  216. package/src/packages/jxsuite-update.ts +97 -48
  217. package/src/packages/pull-package-sync.ts +18 -9
  218. package/src/page-params.ts +2 -1
  219. package/src/panels/activity-bar.ts +152 -91
  220. package/src/panels/activity-panel.ts +481 -0
  221. package/src/panels/ai-chat/chat-markdown.ts +8 -3
  222. package/src/panels/ai-chat/chat-view.ts +245 -18
  223. package/src/panels/ai-chat/composer.ts +31 -5
  224. package/src/panels/ai-chat/sessions-view.ts +17 -10
  225. package/src/panels/ai-panel.ts +279 -120
  226. package/src/panels/block-action-bar.ts +1115 -224
  227. package/src/panels/bottom-dock.ts +337 -0
  228. package/src/panels/canvas-dnd-bridge.ts +37 -8
  229. package/src/panels/chat-panel.ts +29 -12
  230. package/src/panels/data-explorer.ts +254 -112
  231. package/src/panels/data-grid.ts +10 -4
  232. package/src/panels/dnd.ts +28 -51
  233. package/src/panels/editors.ts +434 -107
  234. package/src/panels/elements-panel.ts +66 -2
  235. package/src/panels/empty-state.ts +112 -0
  236. package/src/panels/events-panel.ts +596 -82
  237. package/src/panels/formula-workspace.ts +408 -218
  238. package/src/panels/frontmatter-fields.ts +51 -10
  239. package/src/panels/frontmatter-panel.ts +357 -79
  240. package/src/panels/git-panel.ts +375 -164
  241. package/src/panels/head-panel.ts +791 -85
  242. package/src/panels/i18n-panel.ts +592 -0
  243. package/src/panels/imports-panel.ts +95 -85
  244. package/src/panels/jump-bar.ts +584 -0
  245. package/src/panels/layers-panel.ts +1106 -328
  246. package/src/panels/left-panel.ts +109 -204
  247. package/src/panels/navigator-panels.ts +131 -0
  248. package/src/panels/overlays.ts +7 -2
  249. package/src/panels/pane-context.ts +1477 -0
  250. package/src/panels/pane-grid.ts +406 -0
  251. package/src/panels/panel-registry.ts +340 -0
  252. package/src/panels/panel-scheduler.ts +75 -0
  253. package/src/panels/problems-panel.ts +234 -0
  254. package/src/panels/properties-panel.ts +946 -1139
  255. package/src/panels/provenance.ts +258 -0
  256. package/src/panels/quick-search.ts +791 -122
  257. package/src/panels/right-panel.ts +228 -102
  258. package/src/panels/seo-modal.ts +395 -0
  259. package/src/panels/settings-pane.ts +203 -0
  260. package/src/panels/signals-panel.ts +354 -137
  261. package/src/panels/statement-editor.ts +41 -25
  262. package/src/panels/statusbar.ts +342 -106
  263. package/src/panels/style-panel.ts +974 -463
  264. package/src/panels/style-utils.ts +3 -3
  265. package/src/panels/stylebook-doc.ts +4 -2
  266. package/src/panels/stylebook-layers-panel.ts +3 -2
  267. package/src/panels/stylebook-panel.ts +51 -26
  268. package/src/panels/tab-strip.ts +1058 -78
  269. package/src/panels/target-line.ts +274 -0
  270. package/src/panels/toolbar.ts +631 -484
  271. package/src/panels/welcome-screen.ts +301 -198
  272. package/src/platform-errors.ts +26 -3
  273. package/src/platform.ts +96 -2
  274. package/src/platforms/cloud.ts +71 -8
  275. package/src/platforms/devserver.ts +92 -29
  276. package/src/publish/deploy-checklist.ts +383 -0
  277. package/src/publish/pages-service.ts +10 -1
  278. package/src/publish/publish-commands.ts +218 -0
  279. package/src/publish/publish-panel.ts +110 -16
  280. package/src/recent-projects.ts +3 -2
  281. package/src/services/a11y-report.ts +505 -0
  282. package/src/services/ai-models.ts +75 -2
  283. package/src/services/ai-project-tools.ts +29 -10
  284. package/src/services/ai-system-prompt.ts +53 -14
  285. package/src/services/ai-tools.ts +39 -3
  286. package/src/services/ai-writes.ts +154 -0
  287. package/src/services/announce.ts +97 -0
  288. package/src/services/automation.ts +424 -175
  289. package/src/services/cem-export.ts +9 -4
  290. package/src/services/clock.ts +61 -0
  291. package/src/services/document-assistant.ts +36 -8
  292. package/src/services/idle.ts +179 -0
  293. package/src/services/import-client.ts +26 -0
  294. package/src/services/monaco-buffer.ts +627 -0
  295. package/src/services/monaco-lazy.ts +36 -0
  296. package/src/services/notify.ts +271 -0
  297. package/src/services/profile.ts +155 -0
  298. package/src/services/references.ts +0 -0
  299. package/src/services/token-lint.ts +3 -2
  300. package/src/services/tool-executor.ts +53 -7
  301. package/src/services/trusted-types.ts +123 -0
  302. package/src/settings/contexts-section.ts +427 -0
  303. package/src/settings/contributed-section.ts +149 -11
  304. package/src/settings/css-vars-editor.ts +306 -349
  305. package/src/settings/defs-editor.ts +93 -34
  306. package/src/settings/dependencies-editor.ts +7 -7
  307. package/src/settings/extension-sections.ts +37 -5
  308. package/src/settings/general-settings.ts +204 -121
  309. package/src/settings/locales-section.ts +347 -0
  310. package/src/settings/preferences-accounts.ts +140 -0
  311. package/src/settings/preferences-dialog.ts +620 -0
  312. package/src/settings/preferences-keymap.ts +270 -0
  313. package/src/settings/project-sections.ts +260 -0
  314. package/src/settings/section-registry.ts +169 -0
  315. package/src/settings/settings-document.ts +385 -0
  316. package/src/shell.ts +1549 -0
  317. package/src/site-context.ts +45 -20
  318. package/src/state.ts +14 -275
  319. package/src/store.ts +88 -43
  320. package/src/studio.ts +790 -346
  321. package/src/style/live-preview.ts +36 -0
  322. package/src/style/project-styles.ts +349 -0
  323. package/src/style/token-ref.ts +123 -0
  324. package/src/tabs/patch-ops.ts +16 -4
  325. package/src/tabs/project-config.ts +416 -0
  326. package/src/tabs/selection.ts +323 -0
  327. package/src/tabs/tab.ts +182 -47
  328. package/src/tabs/transact.ts +209 -28
  329. package/src/types.ts +68 -13
  330. package/src/ui/ai-credentials-form.ts +39 -35
  331. package/src/ui/ai-managed-connect.ts +113 -0
  332. package/src/ui/dynamic-slot.ts +224 -81
  333. package/src/ui/expression-editor.ts +71 -63
  334. package/src/ui/field-input.ts +4 -6
  335. package/src/ui/field-row.ts +215 -38
  336. package/src/ui/form-controls.ts +194 -7
  337. package/src/ui/layers.ts +492 -69
  338. package/src/ui/media-picker.ts +115 -25
  339. package/src/ui/panel-resize.ts +109 -91
  340. package/src/ui/progress-modal.ts +131 -50
  341. package/src/ui/regions.ts +462 -0
  342. package/src/ui/schema-form.ts +229 -45
  343. package/src/ui/spectrum.ts +24 -8
  344. package/src/ui/timing.ts +49 -0
  345. package/src/ui/value-selector.ts +20 -0
  346. package/src/ui/value-source.ts +348 -0
  347. package/src/ui/virtual-window.ts +400 -0
  348. package/src/utils/edit-display.ts +3 -2
  349. package/src/utils/inherited-style.ts +67 -37
  350. package/src/view.ts +64 -78
  351. package/src/workspace/pane-derive.ts +1258 -0
  352. package/src/workspace/session.ts +317 -0
  353. package/src/workspace/workspace.ts +1345 -23
  354. package/styles/canvas.css +653 -0
  355. package/styles/forced-colors.css +106 -0
  356. package/styles/inspector.css +1454 -0
  357. package/styles/overlays.css +2704 -0
  358. package/styles/panels.css +1701 -0
  359. package/styles/shell.css +1423 -0
  360. package/styles/tokens.css +151 -0
  361. package/dist/chunks/Overlay.dev-cszx7yn5.js +0 -22
  362. package/dist/chunks/extension-sections-qct0gt8f.js +0 -531
  363. package/dist/chunks/extension-sections-qct0gt8f.js.map +0 -12
  364. package/dist/chunks/files-02zn5y24.js +0 -50
  365. package/dist/chunks/files-02zn5y24.js.map +0 -9
  366. package/dist/chunks/git-panel-mbb0y0hz.js +0 -28
  367. package/dist/chunks/git-panel-mbb0y0hz.js.map +0 -9
  368. package/dist/chunks/layers-panel-9qekmqk1.js +0 -26
  369. package/dist/chunks/layers-panel-9qekmqk1.js.map +0 -9
  370. package/dist/chunks/settings-modal-gj0hrah4.js +0 -29
  371. package/dist/chunks/settings-modal-gj0hrah4.js.map +0 -9
  372. package/dist/chunks/sp-overlay.dev-earn5as8.js +0 -17
  373. package/dist/chunks/sp-overlay.dev-earn5as8.js.map +0 -9
  374. package/dist/chunks/sp-popover.dev-frxtfejn.js +0 -8
  375. package/dist/chunks/sp-popover.dev-frxtfejn.js.map +0 -9
  376. package/dist/chunks/sp-progress-circle.dev-7tvw34h8.js +0 -14
  377. package/dist/chunks/sp-progress-circle.dev-7tvw34h8.js.map +0 -9
  378. package/dist/chunks/sp-tooltip.dev-a9fywesm.js +0 -21
  379. package/dist/chunks/sp-tooltip.dev-a9fywesm.js.map +0 -10
  380. package/dist/chunks/studio-4k0m4fmp.js.map +0 -13
  381. package/dist/chunks/studio-568nx4t5.js +0 -469
  382. package/dist/chunks/studio-568nx4t5.js.map +0 -14
  383. package/dist/chunks/studio-9smfzqhc.js +0 -19
  384. package/dist/chunks/studio-9smfzqhc.js.map +0 -10
  385. package/dist/chunks/studio-asvf0j9e.js +0 -429
  386. package/dist/chunks/studio-asvf0j9e.js.map +0 -13
  387. package/dist/chunks/studio-d77qm3pk.js +0 -3427
  388. package/dist/chunks/studio-d77qm3pk.js.map +0 -26
  389. package/dist/chunks/studio-j6gqyjf4.js +0 -1854
  390. package/dist/chunks/studio-j6gqyjf4.js.map +0 -18
  391. package/dist/chunks/studio-n9zmpxjg.js.map +0 -115
  392. package/dist/chunks/studio-qkvt37c0.js.map +0 -9
  393. package/dist/chunks/studio-rq0vxb19.js +0 -3744
  394. package/dist/chunks/studio-rq0vxb19.js.map +0 -22
  395. package/dist/chunks/studio-wd6ewdvd.js +0 -853
  396. package/dist/chunks/studio-wd6ewdvd.js.map +0 -12
  397. package/dist/chunks/studio-xq0t7n5t.js +0 -140
  398. package/dist/chunks/studio-xq0t7n5t.js.map +0 -10
  399. package/dist/chunks/studio-xze4zrs1.js.map +0 -10
  400. package/dist/chunks/studio-ygzav7z5.js.map +0 -46
  401. package/dist/chunks/studio-zmy7241e.js +0 -399
  402. package/dist/chunks/studio-zmy7241e.js.map +0 -12
  403. package/dist/chunks/ws-client-q7hkz3j9.js.map +0 -11
  404. package/src/browse/browse-modal.ts +0 -69
  405. package/src/browse/browse.ts +0 -904
  406. package/src/canvas/canvas-diff.ts +0 -216
  407. package/src/canvas/nested-site-style.ts +0 -58
  408. package/src/new-project/design-fields.ts +0 -264
  409. package/src/new-project/templates.ts +0 -61
  410. package/src/panels/tab-bar.ts +0 -548
  411. package/src/settings/settings-modal.ts +0 -203
  412. /package/dist/chunks/{adapter-z8fkmbmv.js.map → adapter-qfnesn5v.js.map} +0 -0
  413. /package/dist/chunks/{cf-settings-mmyv436d.js.map → cf-settings-73sh5z4p.js.map} +0 -0
  414. /package/dist/chunks/{format-host-2c188n41.js.map → format-host-mkwsmt8d.js.map} +0 -0
  415. /package/dist/chunks/{jx-validate-qx8degj7.js.map → jx-validate-v6qgknrs.js.map} +0 -0
  416. /package/dist/chunks/{studio-w7c6kvk7.js.map → studio-a7pzveq1.js.map} +0 -0
  417. /package/dist/chunks/{studio-hhj92gcr.js.map → studio-s519zc99.js.map} +0 -0
  418. /package/dist/chunks/{studio-bghqfmea.js.map → studio-wwseqyw0.js.map} +0 -0
  419. /package/dist/chunks/{studio-nqw433kv.js.map → studio-xmvbhg31.js.map} +0 -0
@@ -0,0 +1,400 @@
1
+ /**
2
+ * Virtual-window.ts — the windowing primitive behind every long list in Studio.
3
+ *
4
+ * **Why this is the acceptance criterion and not a nicety.** The Manage view the Library replaces
5
+ * rendered one card per file with no cap, and each Pages/Layouts/Components/Content card mounted a
6
+ * REAL `@jxsuite/runtime` render of that document. Opening "All" on a 300-page project therefore
7
+ * built 300 live documents, in one synchronous lit pass, and kept every one of them alive in an
8
+ * unbounded `Map`. The window is what makes the cost proportional to the VIEWPORT instead of to the
9
+ * project. The two trees pay a smaller version of the same bill — a 5 000-node page drew 5 000
10
+ * rows, each an `sp-icon` custom element — and they now pay it through this file.
11
+ *
12
+ * **It lives in `ui/`, not in `browse/`.** P3 workstream 11 promised "one virtual-list primitive
13
+ * behind the Files tree and the Outline tree, reused by the Library grid", and what shipped sat
14
+ * inside the Library and was imported by the Library alone. A primitive two surfaces share cannot
15
+ * live inside one of them: the next surface that needs it either reaches across a feature boundary
16
+ * or writes a second copy, and a second copy is how "the tree renders every row" survived a phase
17
+ * that had already solved it.
18
+ *
19
+ * The maths is deliberately a pure function ({@link computeWindow}) rather than a class: it is the
20
+ * part that has to be right, it is the part the perf test measures, and it needs no DOM to state.
21
+ * {@link createVirtualWindow} is the thin observer that feeds it scroll positions, and the
22
+ * {@link nearestScroller} half below is the small amount of DOM a list that does NOT own its own
23
+ * scrollbar needs — both trees scroll inside `#left-panel`, which also holds their toolbars.
24
+ *
25
+ * One primitive, three surfaces: the Library (Table rows, Cards, Media), the Files tree and the
26
+ * Outline. The geometry never mentions what an item IS.
27
+ */
28
+
29
+ /// <reference lib="dom" />
30
+
31
+ import { rectOf } from "../utils/geometry";
32
+
33
+ /** The measurement a window is computed from. Everything is in CSS pixels. */
34
+ export interface WindowSpec {
35
+ /** How many items exist in total. */
36
+ count: number;
37
+ /** Height of one ROW. A grid row holds {@link WindowSpec.columns} items. */
38
+ rowHeight: number;
39
+ /** Items per row. 1 for a list. Clamped to at least 1. */
40
+ columns?: number;
41
+ /** The scroller's current `scrollTop`. */
42
+ scrollTop: number;
43
+ /** The scroller's visible height. */
44
+ viewportHeight: number;
45
+ /** Extra rows rendered above and below the viewport. Defaults to {@link DEFAULT_OVERSCAN_ROWS}. */
46
+ overscanRows?: number;
47
+ }
48
+
49
+ /**
50
+ * Which items to render, and how much empty space to reserve either side of them.
51
+ *
52
+ * `padTop`/`padBottom` are spacer heights rather than absolute positioning, so a windowed list is
53
+ * still an ordinary flow container: it wraps, it prints, and a focused control inside it scrolls
54
+ * into view without anything computing coordinates.
55
+ */
56
+ export interface WindowRange {
57
+ /** First item index, inclusive. */
58
+ start: number;
59
+ /** Last item index, EXCLUSIVE. */
60
+ end: number;
61
+ /** Pixels of spacer above the first rendered row. */
62
+ padTop: number;
63
+ /** Pixels of spacer below the last rendered row. */
64
+ padBottom: number;
65
+ /** Total rows the full list would occupy — what the scrollbar is sized from. */
66
+ totalRows: number;
67
+ }
68
+
69
+ /**
70
+ * Rows kept beyond each edge of the viewport.
71
+ *
72
+ * Three, not zero: a window with no overscan repaints on every scroll frame and shows a blank strip
73
+ * for one frame at speed. Three is the smallest value at which neither is observable at a normal
74
+ * wheel velocity, and it is small enough that the LRU preview cap still dominates.
75
+ */
76
+ export const DEFAULT_OVERSCAN_ROWS = 3;
77
+
78
+ /** Clamp `value` into `[low, high]`. */
79
+ function clamp(value: number, low: number, high: number): number {
80
+ return Math.min(high, Math.max(low, value));
81
+ }
82
+
83
+ /**
84
+ * The visible window for a spec.
85
+ *
86
+ * Total-safe by construction: a zero or negative `rowHeight` (an unmeasured container, a display:
87
+ * none pane) yields the whole list rather than an infinite loop or an empty render, because a
88
+ * Library that renders nothing is indistinguishable from a Library that found nothing — the exact
89
+ * confusion this phase exists to remove.
90
+ */
91
+ export function computeWindow(spec: WindowSpec): WindowRange {
92
+ const count = Math.max(0, Math.trunc(spec.count));
93
+ const columns = Math.max(1, Math.trunc(spec.columns ?? 1));
94
+ const totalRows = Math.ceil(count / columns);
95
+
96
+ if (!(spec.rowHeight > 0) || !(spec.viewportHeight > 0)) {
97
+ return { end: count, padBottom: 0, padTop: 0, start: 0, totalRows };
98
+ }
99
+
100
+ const overscan = Math.max(0, Math.trunc(spec.overscanRows ?? DEFAULT_OVERSCAN_ROWS));
101
+ const scrollTop = Math.max(0, spec.scrollTop);
102
+ const firstVisibleRow = Math.floor(scrollTop / spec.rowHeight);
103
+ const visibleRows = Math.ceil(spec.viewportHeight / spec.rowHeight) + 1;
104
+
105
+ const startRow = clamp(firstVisibleRow - overscan, 0, totalRows);
106
+ const endRow = clamp(firstVisibleRow + visibleRows + overscan, startRow, totalRows);
107
+
108
+ return {
109
+ end: Math.min(count, endRow * columns),
110
+ padBottom: (totalRows - endRow) * spec.rowHeight,
111
+ padTop: startRow * spec.rowHeight,
112
+ start: Math.min(count, startRow * columns),
113
+ totalRows,
114
+ };
115
+ }
116
+
117
+ /** Two ranges are the same window — the guard that keeps scrolling from repainting every frame. */
118
+ export function sameWindow(a: WindowRange, b: WindowRange): boolean {
119
+ return a.start === b.start && a.end === b.end && a.totalRows === b.totalRows;
120
+ }
121
+
122
+ // ─── The observer ────────────────────────────────────────────────────────────
123
+
124
+ export interface VirtualWindow {
125
+ /** The current range. Read during render; never stale by more than one frame. */
126
+ range: () => WindowRange;
127
+ /** Re-measure and recompute (count changed, layout changed, container resized). */
128
+ measure: () => void;
129
+ /** Stop listening. Idempotent. */
130
+ destroy: () => void;
131
+ }
132
+
133
+ export interface VirtualWindowOptions {
134
+ /** The element that scrolls. */
135
+ scroller: HTMLElement;
136
+ /**
137
+ * The list inside `scroller`, when the scroller carries more than the list.
138
+ *
139
+ * Both trees scroll inside `#left-panel`, which also holds a project header, a toolbar and — for
140
+ * the Files tree — a search field. Their first row is therefore NOT at `scrollTop` 0, and a
141
+ * window computed from the scroller's own `scrollTop` would be that header's height out from the
142
+ * first scroll onwards. Omit it when the list IS the scroller (the Library's pane).
143
+ */
144
+ list?: HTMLElement;
145
+ /** Item count right now — read on every measure, so it may change without re-creating. */
146
+ count: () => number;
147
+ /** Row height right now. */
148
+ rowHeight: () => number;
149
+ /** Items per row right now. */
150
+ columns?: () => number;
151
+ /** Called only when the window actually CHANGED. */
152
+ onChange: (range: WindowRange) => void;
153
+ overscanRows?: number;
154
+ }
155
+
156
+ /**
157
+ * Watch a scroller and report window changes.
158
+ *
159
+ * Scroll is listened to passively and answered synchronously: the range is cheap to compute and a
160
+ * rAF hop would paint the spacer before the content one frame in three. `ResizeObserver` covers the
161
+ * pane being resized; where the environment has none (happy-dom), `measure()` is the whole contract
162
+ * and the caller drives it — which is also how the perf test drives it.
163
+ */
164
+ export function createVirtualWindow(options: VirtualWindowOptions): VirtualWindow {
165
+ const { scroller, list, count, rowHeight, columns, onChange, overscanRows } = options;
166
+ let current: WindowRange = { end: 0, padBottom: 0, padTop: 0, start: 0, totalRows: 0 };
167
+ let destroyed = false;
168
+
169
+ function measure() {
170
+ if (destroyed) {
171
+ return;
172
+ }
173
+ const next = computeWindow({
174
+ columns: columns?.() ?? 1,
175
+ count: count(),
176
+ overscanRows: overscanRows ?? DEFAULT_OVERSCAN_ROWS,
177
+ rowHeight: rowHeight(),
178
+ scrollTop: list ? listScrollTop(scroller, list) : scroller.scrollTop,
179
+ viewportHeight: scroller.clientHeight,
180
+ });
181
+ const changed = !sameWindow(current, next);
182
+ current = next;
183
+ if (changed) {
184
+ onChange(next);
185
+ }
186
+ }
187
+
188
+ scroller.addEventListener("scroll", measure, { passive: true });
189
+
190
+ let resizeObserver: ResizeObserver | null = null;
191
+ if (typeof ResizeObserver === "function") {
192
+ resizeObserver = new ResizeObserver(() => measure());
193
+ resizeObserver.observe(scroller);
194
+ }
195
+
196
+ measure();
197
+
198
+ return {
199
+ destroy() {
200
+ destroyed = true;
201
+ scroller.removeEventListener("scroll", measure);
202
+ resizeObserver?.disconnect();
203
+ resizeObserver = null;
204
+ },
205
+ measure,
206
+ range: () => current,
207
+ };
208
+ }
209
+
210
+ // ─── A list that does not own its scrollbar ──────────────────────────────────
211
+
212
+ /**
213
+ * The element that scrolls `el`, or null when nothing does.
214
+ *
215
+ * TWO conditions, and the second is the one that makes this honest. An element is the scroller only
216
+ * if it both declares a scrolling overflow AND currently has something to scroll. `.file-tree`
217
+ * declares `overflow-y: auto` (styles/panels.css) but is never given a height, so it grows with its
218
+ * content and never scrolls — treating it as the scroller would read its full content height as the
219
+ * VIEWPORT height and window nothing at all, silently. The `scrollHeight > clientHeight` test walks
220
+ * past it to `#left-panel`, which is where both trees actually scroll.
221
+ *
222
+ * The same test is why a list whose content fits is never windowed: there is nothing to scroll, so
223
+ * there is nothing to save, and the whole list renders exactly as it always did.
224
+ */
225
+ export function nearestScroller(el: HTMLElement | null): HTMLElement | null {
226
+ for (let node: HTMLElement | null = el; node; node = node.parentElement) {
227
+ // Cheap test first: happy-dom performs no layout, so this is 0 > 0 for every element and no
228
+ // Test ever pays for a `getComputedStyle` walk it cannot answer.
229
+ if (node.scrollHeight <= node.clientHeight) {
230
+ continue;
231
+ }
232
+ const overflow = getComputedStyle(node).overflowY;
233
+ if (overflow === "auto" || overflow === "scroll" || overflow === "overlay") {
234
+ return node;
235
+ }
236
+ }
237
+ return null;
238
+ }
239
+
240
+ /**
241
+ * How far `list`'s first row has scrolled above the top of `scroller`'s viewport.
242
+ *
243
+ * Measured from the two rects rather than from `offsetTop`, because the chrome between them is not
244
+ * a fixed height: the Files tree's toolbar and the project header are both conditional. When the
245
+ * list IS the scroller there is no chrome and its own `scrollTop` is the answer.
246
+ */
247
+ export function listScrollTop(scroller: HTMLElement, list: HTMLElement): number {
248
+ if (scroller === list) {
249
+ return Math.max(0, scroller.scrollTop);
250
+ }
251
+ return Math.max(0, rectOf(scroller).top - rectOf(list).top);
252
+ }
253
+
254
+ /**
255
+ * The window for a list rendered inside whatever scrolls it — the trees' entry point.
256
+ *
257
+ * Answers "the whole list" for a list that has not been rendered yet, has been detached, or is not
258
+ * inside anything that scrolls. That is not a fallback bolted on for tests: it is the FIRST paint
259
+ * of every session (nothing can be measured before the rows exist), and it is every list short
260
+ * enough to fit. Both must render everything, and both are what the trees did before they
261
+ * windowed.
262
+ */
263
+ export function listWindow(
264
+ list: HTMLElement | null,
265
+ spec: { count: number; rowHeight: number; columns?: number; overscanRows?: number },
266
+ ): WindowRange {
267
+ const scroller = list?.isConnected === true ? nearestScroller(list) : null;
268
+ if (!scroller || !list) {
269
+ const count = Math.max(0, Math.trunc(spec.count));
270
+ return { end: count, padBottom: 0, padTop: 0, start: 0, totalRows: count };
271
+ }
272
+ return computeWindow({
273
+ columns: spec.columns ?? 1,
274
+ count: spec.count,
275
+ ...(spec.overscanRows === undefined ? {} : { overscanRows: spec.overscanRows }),
276
+ rowHeight: spec.rowHeight,
277
+ scrollTop: listScrollTop(scroller, list),
278
+ viewportHeight: scroller.clientHeight,
279
+ });
280
+ }
281
+
282
+ /**
283
+ * The `scrollTop` that brings row `index` into view, or null when it already is.
284
+ *
285
+ * `block: "nearest"` semantics, stated as arithmetic: a windowed list cannot delegate this to
286
+ * `Element.scrollIntoView`, because the row it must reveal is precisely the one that has no
287
+ * element. Pure, so the reveal rule is testable without a scroller.
288
+ */
289
+ export function scrollTopToReveal(spec: {
290
+ index: number;
291
+ rowHeight: number;
292
+ /** Where the list's first row sits in the scroller's CONTENT, not its viewport. */
293
+ listOffset: number;
294
+ scrollTop: number;
295
+ viewportHeight: number;
296
+ }): number | null {
297
+ const top = spec.listOffset + spec.index * spec.rowHeight;
298
+ const bottom = top + spec.rowHeight;
299
+ if (top < spec.scrollTop) {
300
+ return Math.max(0, top);
301
+ }
302
+ if (bottom > spec.scrollTop + spec.viewportHeight) {
303
+ return Math.max(0, bottom - spec.viewportHeight);
304
+ }
305
+ return null;
306
+ }
307
+
308
+ /**
309
+ * Scroll `list` so that its row at `index` is inside the window on the next render.
310
+ *
311
+ * Returns whether anything moved. The re-render is deliberately NOT triggered here: assigning
312
+ * `scrollTop` fires a `scroll` event, and the watch installed by {@link watchListWindow} is already
313
+ * the one thing that decides when a scroll is worth a repaint. A caller that repainted as well
314
+ * would paint twice for one keystroke.
315
+ */
316
+ export function revealListRow(list: HTMLElement | null, index: number, rowHeight: number): boolean {
317
+ const scroller = list?.isConnected === true ? nearestScroller(list) : null;
318
+ if (!scroller || !list || index < 0 || !(rowHeight > 0)) {
319
+ return false;
320
+ }
321
+ const { scrollTop } = scroller;
322
+ const next = scrollTopToReveal({
323
+ index,
324
+ listOffset: scrollTop - listScrollTop(scroller, list),
325
+ rowHeight,
326
+ scrollTop,
327
+ viewportHeight: scroller.clientHeight,
328
+ });
329
+ if (next === null) {
330
+ return false;
331
+ }
332
+ scroller.scrollTop = next;
333
+ return true;
334
+ }
335
+
336
+ /**
337
+ * The height one row actually has, measured, with the stylesheet's declared height as the answer
338
+ * until a row exists to measure.
339
+ *
340
+ * Both are needed. The DECLARED height (`styles/panels.css` gives `.layer-row` and
341
+ * `.file-tree-item` a `block-size`) is what lets the FIRST render window anything at all, before
342
+ * any row has been laid out. The MEASUREMENT is what stops that constant becoming a lie the day
343
+ * someone changes the row's padding, a user zooms, or a locale's font raises the line box: a window
344
+ * computed from a stale height does not fail loudly, it drifts, and the list quietly ends a few
345
+ * rows short of its own scrollbar.
346
+ */
347
+ export function measuredRowHeight(
348
+ list: HTMLElement | null,
349
+ selector: string,
350
+ declared: number,
351
+ ): number {
352
+ const row = list?.querySelector<HTMLElement>(selector);
353
+ return row && row.offsetHeight > 0 ? row.offsetHeight : declared;
354
+ }
355
+
356
+ /** A live scroll watch over one list element. Held at module scope by the surface that renders it. */
357
+ export interface ListWindowWatch {
358
+ /** The list this watch is bound to. A different element means a different watch. */
359
+ list: HTMLElement;
360
+ /** The scroller it resolved to when it was bound. */
361
+ scroller: HTMLElement;
362
+ window: VirtualWindow;
363
+ }
364
+
365
+ /**
366
+ * Bind — or keep — the scroll watch that repaints a windowed list.
367
+ *
368
+ * Called from the surface's `afterRender`, where the rows exist and the scroller can be resolved,
369
+ * and handed back its own previous handle so re-binding is the exception rather than the rule: the
370
+ * tree element survives every re-render (lit reuses it), so the steady state is "same list, same
371
+ * scroller, nothing to do". A new element — the panel remounted, or moved dock — moves the
372
+ * listener, and a list that no longer scrolls drops it.
373
+ *
374
+ * Returns the handle to keep, or null when nothing scrolls this list.
375
+ */
376
+ export function watchListWindow(
377
+ previous: ListWindowWatch | null,
378
+ list: HTMLElement,
379
+ spec: { count: () => number; rowHeight: () => number; onChange: () => void },
380
+ ): ListWindowWatch | null {
381
+ const scroller = nearestScroller(list);
382
+ if (previous && previous.list === list && previous.scroller === scroller) {
383
+ return previous;
384
+ }
385
+ previous?.window.destroy();
386
+ if (!scroller) {
387
+ return null;
388
+ }
389
+ return {
390
+ list,
391
+ scroller,
392
+ window: createVirtualWindow({
393
+ count: spec.count,
394
+ list,
395
+ onChange: () => spec.onChange(),
396
+ rowHeight: spec.rowHeight,
397
+ scroller,
398
+ }),
399
+ };
400
+ }
@@ -6,6 +6,7 @@
6
6
  */
7
7
 
8
8
  import type { JxMutableNode } from "@jxsuite/schema/types";
9
+ import { displayTagName } from "@jxsuite/schema/guards";
9
10
 
10
11
  const mediaTags = new Set(["img", "video", "source", "iframe", "audio"]);
11
12
  const TRANSPARENT_PX =
@@ -103,10 +104,10 @@ export function computeEmptyPlaceholderClass(node: JxMutableNode): string | null
103
104
  if (Array.isArray(node.children) && node.children.length > 0) {
104
105
  return null;
105
106
  }
106
- if (textTags.has(node.tagName)) {
107
+ if (textTags.has(displayTagName(node.tagName))) {
107
108
  return "empty-text-placeholder";
108
109
  }
109
- if (containerTags.has(node.tagName)) {
110
+ if (containerTags.has(displayTagName(node.tagName))) {
110
111
  return "empty-container-placeholder";
111
112
  }
112
113
  return null;
@@ -1,75 +1,105 @@
1
1
  /**
2
- * Inherited-style.js — Computes the effective inherited style for a given breakpoint tab.
2
+ * Inherited-style.js — the effective inherited style for a breakpoint tab, **and the breakpoint it
3
+ * came from**.
3
4
  *
4
5
  * Walks the cascade (base → each media block in order) up to but not including the active
5
6
  * breakpoint, producing the set of property values that would apply if no explicit override exists
6
7
  * on the current tab.
8
+ *
9
+ * The walk has always known the donor — it is the loop variable — and the answer was thrown away
10
+ * one line later, which left the inspector able to show an inherited value only as an input
11
+ * placeholder, visually identical to the CSS initial value rendered beside it.
12
+ * {@link computeInheritedSources} keeps the donor; {@link computeInheritedStyle} is the projection
13
+ * that drops it, for callers that only want the value.
7
14
  */
8
15
 
9
16
  import type { JxStyle } from "@jxsuite/schema/types";
10
17
 
18
+ /** One inherited property: the value that shows through, and the breakpoint it was set on. */
19
+ export interface InheritedSource {
20
+ value: string | number;
21
+ /** The donor breakpoint's name, or `null` when the value comes from the base context. */
22
+ donor: string | null;
23
+ }
24
+
11
25
  /**
12
- * Compute the inherited style object for a given breakpoint tab.
26
+ * Compute the inherited style map WITH each value's donor breakpoint.
13
27
  *
14
28
  * @param {JxStyle} style — full style object (flat props + @media blocks + selectors)
15
29
  * @param {string[]} mediaNames — ordered breakpoint names (from parseMediaEntries, respects cascade
16
30
  * direction)
17
31
  * @param {string | null} activeTab — current breakpoint tab name, or null for base
18
32
  * @param {string | null} activeSelector — current nested selector, or null
19
- * @returns {Record<string, string | number>} Inherited style map (prop → value)
33
+ * @returns {Record<string, InheritedSource>} Prop → { value, donor }
20
34
  */
21
- export function computeInheritedStyle(
35
+ export function computeInheritedSources(
22
36
  style: JxStyle,
23
37
  mediaNames: string[],
24
38
  activeTab: string | null,
25
39
  activeSelector: string | null = null,
26
- ) {
40
+ ): Record<string, InheritedSource> {
27
41
  if (activeTab === null || mediaNames.length === 0) {
28
42
  return {};
29
43
  }
30
44
 
31
- const inherited: Record<string, string | number> = {};
45
+ const inherited: Record<string, InheritedSource> = {};
32
46
 
33
- if (!activeSelector) {
34
- // Start with base flat props
35
- for (const [p, v] of Object.entries(style)) {
36
- if (typeof v !== "object") {
37
- inherited[p] = (v as string | number) ?? "";
38
- }
39
- }
40
- // Layer each media block in order until current tab
41
- for (const name of mediaNames) {
42
- if (name === activeTab) {
43
- break;
44
- }
45
- const block = (style[`@${name}`] || {}) as JxStyle;
46
- for (const [p, v] of Object.entries(block)) {
47
- if (typeof v !== "object") {
48
- inherited[p] = (v as string | number) ?? "";
49
- }
50
- }
51
- }
52
- } else {
53
- // Selector inheritance: base selector → each media's selector block in order
54
- const baseSel = (style[activeSelector] || {}) as JxStyle;
55
- for (const [p, v] of Object.entries(baseSel)) {
47
+ /** Layer one block over the accumulator, recording `donor` for everything it contributes. */
48
+ const layer = (block: JxStyle, donor: string | null) => {
49
+ for (const [p, v] of Object.entries(block)) {
56
50
  if (typeof v !== "object") {
57
- inherited[p] = (v as string | number) ?? "";
51
+ inherited[p] = { donor, value: (v as string | number) ?? "" };
58
52
  }
59
53
  }
54
+ };
55
+
56
+ if (activeSelector) {
57
+ // Selector inheritance: base selector → each media's selector block, in cascade order.
58
+ layer((style[activeSelector] || {}) as JxStyle, null);
60
59
  for (const name of mediaNames) {
61
60
  if (name === activeTab) {
62
61
  break;
63
62
  }
64
- const selBlock = (((style[`@${name}`] || {}) as Record<string, unknown>)[activeSelector] ||
65
- {}) as JxStyle;
66
- for (const [p, v] of Object.entries(selBlock)) {
67
- if (typeof v !== "object") {
68
- inherited[p] = (v as string | number) ?? "";
69
- }
70
- }
63
+ layer(
64
+ (((style[`@${name}`] || {}) as Record<string, unknown>)[activeSelector] || {}) as JxStyle,
65
+ name,
66
+ );
71
67
  }
68
+ return inherited;
72
69
  }
73
70
 
71
+ // Base flat props first, then each media block in order until the current tab.
72
+ layer(style, null);
73
+ for (const name of mediaNames) {
74
+ if (name === activeTab) {
75
+ break;
76
+ }
77
+ layer((style[`@${name}`] || {}) as JxStyle, name);
78
+ }
74
79
  return inherited;
75
80
  }
81
+
82
+ /**
83
+ * Compute the inherited style object for a given breakpoint tab — values only.
84
+ *
85
+ * @param {JxStyle} style — full style object (flat props + @media blocks + selectors)
86
+ * @param {string[]} mediaNames — ordered breakpoint names (from parseMediaEntries, respects cascade
87
+ * direction)
88
+ * @param {string | null} activeTab — current breakpoint tab name, or null for base
89
+ * @param {string | null} activeSelector — current nested selector, or null
90
+ * @returns {Record<string, string | number>} Inherited style map (prop → value)
91
+ */
92
+ export function computeInheritedStyle(
93
+ style: JxStyle,
94
+ mediaNames: string[],
95
+ activeTab: string | null,
96
+ activeSelector: string | null = null,
97
+ ): Record<string, string | number> {
98
+ const values: Record<string, string | number> = {};
99
+ for (const [prop, source] of Object.entries(
100
+ computeInheritedSources(style, mediaNames, activeTab, activeSelector),
101
+ )) {
102
+ values[prop] = source.value;
103
+ }
104
+ return values;
105
+ }