@jxsuite/studio 6.1.1 → 6.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (637) hide show
  1. package/dist/chunks/{2020-va2wwra4.js → 2020-f2gkpy8d.js} +3 -3
  2. package/dist/chunks/{action-group-2mqby73c.js → action-group-4aan7g6g.js} +3 -3
  3. package/dist/chunks/adapter-4912g339.js +4 -0
  4. package/dist/chunks/cf-account-picker-wccaenh6.js +4 -0
  5. package/dist/chunks/cf-settings-br6105tr.js +4 -0
  6. package/dist/chunks/color-28ksk2qm.js +4 -0
  7. package/dist/chunks/color-area-0fj8a4ga.js +4 -0
  8. package/dist/chunks/color-field-xch8kx2g.js +4 -0
  9. package/dist/chunks/color-slider-jxd8v350.js +4 -0
  10. package/dist/chunks/combobox-p0efwjke.js +4 -0
  11. package/dist/chunks/components-db68vsn7.js +4 -0
  12. package/dist/chunks/content-source-1449qesq.js +4 -0
  13. package/dist/chunks/convert-to-repeater-5h457egj.js +4 -0
  14. package/dist/chunks/dialog-c4x05vyx.js +4 -0
  15. package/dist/chunks/editor-x9jn86a7.js +4 -0
  16. package/dist/chunks/entry-commands-may24cpt.js +4 -0
  17. package/dist/chunks/extension-sections-pvr14vxr.js +4 -0
  18. package/dist/chunks/file-ops-x9v54pcj.js +4 -0
  19. package/dist/chunks/files-j3knqf0s.js +4 -0
  20. package/dist/chunks/format-host-tyre2trb.js +4 -0
  21. package/dist/chunks/fs-events-64ma9htc.js +4 -0
  22. package/dist/chunks/git-panel-84ps5dex.js +4 -0
  23. package/dist/chunks/grid-view-d2wq9ezg.js +51 -0
  24. package/dist/chunks/icons-pnfz478q.js +4 -0
  25. package/dist/chunks/iframe-entry-fvbk89gm.js +5 -0
  26. package/dist/chunks/iframe-entry-jdcktez8.js +5 -0
  27. package/dist/chunks/iframe-entry-x0ygf2yr.js +5 -0
  28. package/dist/chunks/iframe-entry-zap785cw.js +5 -0
  29. package/dist/chunks/iframe-host-ck6vejt5.js +4 -0
  30. package/dist/chunks/index-b8xw3vy9.js +4 -0
  31. package/dist/chunks/{index-2veqmnhr.js → index-zpjmzqfk.js} +3 -3
  32. package/dist/chunks/{javascript-8nbvs12s.js → javascript-m54mnwp6.js} +3 -3
  33. package/dist/chunks/{jsonMode-s8kt9dsj.js → jsonMode-w15cmrcm.js} +8 -8
  34. package/dist/chunks/jx-validate-at3zn8wa.js +4 -0
  35. package/dist/chunks/layers-h4xp1pss.js +4 -0
  36. package/dist/chunks/layers-panel-m5hw1d7g.js +4 -0
  37. package/dist/chunks/listbox-e74qjyq6.js +4 -0
  38. package/dist/chunks/lit-html-8t8t3dnr.js +4 -0
  39. package/dist/chunks/media-picker-2cweexc5.js +4 -0
  40. package/dist/chunks/menu-n6yzswmy.js +4 -0
  41. package/dist/chunks/menu-ynw13y0s.js +4 -0
  42. package/dist/chunks/monaco-binding-7gx83x3r.js +4 -0
  43. package/dist/chunks/monaco-buffer-qzxn567r.js +4 -0
  44. package/dist/chunks/monaco-setup-gxf6656g.js +4 -0
  45. package/dist/chunks/notify-cj5zbrs2.js +4 -0
  46. package/dist/chunks/number-field-y62xq0ty.js +4 -0
  47. package/dist/chunks/parse-b7ddhr8m.js +4 -0
  48. package/dist/chunks/platform-3dfws9js.js +4 -0
  49. package/dist/chunks/popover-jt2gxmzs.js +4 -0
  50. package/dist/chunks/publish-panel-1gvxbj21.js +4 -0
  51. package/dist/chunks/quick-search-0dajrwqg.js +4 -0
  52. package/dist/chunks/references-49qgt404.js +4 -0
  53. package/dist/chunks/right-panel-jm1bwe53.js +4 -0
  54. package/dist/chunks/section-registry-6kwc4fky.js +4 -0
  55. package/dist/chunks/select-rzds841e.js +4 -0
  56. package/dist/chunks/split-n2gfpm5m.js +4 -0
  57. package/dist/chunks/{studio-1q1sh2ax.js → studio-0q988f9w.js} +3 -3
  58. package/dist/chunks/studio-0rt52vpq.js +5 -0
  59. package/dist/chunks/studio-18vgbhs5.js +5 -0
  60. package/dist/chunks/{studio-h46vtzk8.js → studio-1fxkaaea.js} +4 -4
  61. package/dist/chunks/studio-24mccdrj.js +5 -0
  62. package/dist/chunks/studio-2eh507zk.js +9 -0
  63. package/dist/chunks/studio-2hcbccfv.js +5 -0
  64. package/dist/chunks/studio-2j1cb95x.js +5 -0
  65. package/dist/chunks/studio-2jbs3rxv.js +5 -0
  66. package/dist/chunks/{studio-rv11kfwa.js → studio-485046te.js} +7 -7
  67. package/dist/chunks/studio-4h8k4dw9.js +5 -0
  68. package/dist/chunks/studio-5f6n18kx.js +5 -0
  69. package/dist/chunks/studio-5gthpky6.js +5 -0
  70. package/dist/chunks/studio-5ve6bbep.js +5 -0
  71. package/dist/chunks/studio-6ep3js6b.js +9 -0
  72. package/dist/chunks/studio-6wkmf3wz.js +9 -0
  73. package/dist/chunks/studio-841rf3cg.js +5 -0
  74. package/dist/chunks/studio-8gmwgheg.js +5 -0
  75. package/dist/chunks/studio-8qm0ep50.js +5 -0
  76. package/dist/chunks/{studio-8grwxtqk.js → studio-90pq9km1.js} +4 -4
  77. package/dist/chunks/studio-924p1tm1.js +6 -0
  78. package/dist/chunks/{studio-pre0skq6.js → studio-a3g98wjr.js} +4 -4
  79. package/dist/chunks/studio-a3shpz9y.js +5 -0
  80. package/dist/chunks/studio-amsbaaar.js +6 -0
  81. package/dist/chunks/studio-at5spjbc.js +7 -0
  82. package/dist/chunks/studio-b2xv945e.js +5 -0
  83. package/dist/chunks/studio-bn5nkafw.js +5 -0
  84. package/dist/chunks/studio-bw6q5xjf.js +9 -0
  85. package/dist/chunks/studio-c7q0t9x1.js +5 -0
  86. package/dist/chunks/studio-camnhc96.js +6 -0
  87. package/dist/chunks/studio-cffqvnzd.js +5 -0
  88. package/dist/chunks/studio-d81xfh1g.js +5 -0
  89. package/dist/chunks/studio-dgz8d0c1.js +5 -0
  90. package/dist/chunks/studio-efvfrsf8.js +5 -0
  91. package/dist/chunks/studio-er30bpcx.js +5 -0
  92. package/dist/chunks/studio-ezscgr64.js +5 -0
  93. package/dist/chunks/studio-ffrrncvz.js +5 -0
  94. package/dist/chunks/studio-fpphgn8y.js +10 -0
  95. package/dist/chunks/studio-j1kvhdjk.js +4 -0
  96. package/dist/chunks/{studio-vrmcjf9g.js → studio-j430n16g.js} +5 -5
  97. package/dist/chunks/studio-jjkg84nj.js +5 -0
  98. package/dist/chunks/studio-m0q32ars.js +4 -0
  99. package/dist/chunks/studio-m6bdst11.js +5 -0
  100. package/dist/chunks/studio-nv2cptd4.js +64 -0
  101. package/dist/chunks/studio-nwfrc98n.js +5 -0
  102. package/dist/chunks/studio-p1wgvp7r.js +5 -0
  103. package/dist/chunks/studio-pag2wncm.js +5 -0
  104. package/dist/chunks/studio-qvanvzfd.js +5 -0
  105. package/dist/chunks/studio-qwa64kt7.js +5 -0
  106. package/dist/chunks/studio-rk1zc4x3.js +928 -0
  107. package/dist/chunks/studio-se2r3ptc.js +5 -0
  108. package/dist/chunks/studio-sg2a0dh0.js +5 -0
  109. package/dist/chunks/studio-tjzjhe68.js +5 -0
  110. package/dist/chunks/{studio-wszjyh0j.js → studio-tq0rtfwj.js} +4 -4
  111. package/dist/chunks/{studio-kggey1jt.js → studio-v0jzqdjm.js} +4 -4
  112. package/dist/chunks/studio-v2w7q8r1.js +7 -0
  113. package/dist/chunks/studio-v5vjq1h0.js +4 -0
  114. package/dist/chunks/studio-v8aga3vp.js +14 -0
  115. package/dist/chunks/studio-vfcdrfmv.js +5 -0
  116. package/dist/chunks/studio-vmfkgdjf.js +5 -0
  117. package/dist/chunks/studio-wt1816jj.js +5 -0
  118. package/dist/chunks/studio-wz73q5kq.js +4 -0
  119. package/dist/chunks/studio-y04qtxvp.js +7 -0
  120. package/dist/chunks/studio-y21g8zzg.js +5 -0
  121. package/dist/chunks/{studio-zey7zsdw.js → studio-y64hrt8p.js} +7 -7
  122. package/dist/chunks/studio-ynn90f58.js +16 -0
  123. package/dist/chunks/studio-yxz682sx.js +5 -0
  124. package/dist/chunks/studio-zp7snvkq.js +5 -0
  125. package/dist/chunks/studio-zq4z6tm6.js +5 -0
  126. package/dist/chunks/swatch-group-ewh24n1f.js +4 -0
  127. package/dist/chunks/tabs-f5cw3tgp.js +4 -0
  128. package/dist/chunks/textfield-wewc8rq2.js +4 -0
  129. package/dist/chunks/toast-c9fn075p.js +4 -0
  130. package/dist/chunks/toast-host-ke5z4ng7.js +4 -0
  131. package/dist/chunks/{toolbar-2nnzqhrj.js → toolbar-44cm5cta.js} +3 -3
  132. package/dist/chunks/tooltip-5v8ngwj2.js +4 -0
  133. package/dist/chunks/tree-mdq3v5cw.js +4 -0
  134. package/dist/chunks/{tsMode-2frah6h1.js → tsMode-sf49ee9x.js} +7 -7
  135. package/dist/chunks/workspace-jtfahpee.js +4 -0
  136. package/dist/chunks/ws-client-nze0k6mv.js +4 -0
  137. package/dist/iframe-entry.js +16 -16
  138. package/dist/manifest.json +63 -63
  139. package/dist/studio.js +97 -97
  140. package/package.json +6 -6
  141. package/src/about/about-modal.ts +4 -3
  142. package/src/account/action-flow.ts +45 -38
  143. package/src/account-status.ts +7 -2
  144. package/src/browse/library-layouts.ts +8 -5
  145. package/src/browse/library-model.ts +18 -6
  146. package/src/browse/library-pane.ts +37 -21
  147. package/src/browse/library-preview.ts +14 -9
  148. package/src/browse/library-source.ts +1 -1
  149. package/src/canvas/asset-refs.ts +24 -9
  150. package/src/canvas/asset-resolve.ts +19 -23
  151. package/src/canvas/canvas-helpers.ts +7 -6
  152. package/src/canvas/canvas-live-render.ts +44 -53
  153. package/src/canvas/canvas-patcher.ts +30 -30
  154. package/src/canvas/canvas-perf.ts +11 -5
  155. package/src/canvas/canvas-render.ts +62 -41
  156. package/src/canvas/canvas-surface.ts +5 -3
  157. package/src/canvas/canvas-utils.ts +90 -49
  158. package/src/canvas/dialog-path.ts +9 -4
  159. package/src/canvas/dialog-state.ts +1 -2
  160. package/src/canvas/diff-marks.ts +9 -16
  161. package/src/canvas/diff-toolbar.ts +8 -7
  162. package/src/canvas/edit-width-drag.ts +4 -1
  163. package/src/canvas/edit-width.ts +1 -3
  164. package/src/canvas/iframe-channel.ts +27 -13
  165. package/src/canvas/iframe-drop.ts +13 -12
  166. package/src/canvas/iframe-editable-root.ts +29 -16
  167. package/src/canvas/iframe-entry.ts +80 -63
  168. package/src/canvas/iframe-eval.ts +73 -49
  169. package/src/canvas/iframe-host.ts +135 -96
  170. package/src/canvas/iframe-inline-edit.ts +49 -30
  171. package/src/canvas/iframe-insert.ts +5 -4
  172. package/src/canvas/iframe-interaction.ts +30 -25
  173. package/src/canvas/iframe-overlay.ts +1 -1
  174. package/src/canvas/iframe-patch.ts +18 -15
  175. package/src/canvas/iframe-position.ts +12 -6
  176. package/src/canvas/iframe-protocol.ts +12 -16
  177. package/src/canvas/iframe-render.ts +36 -34
  178. package/src/canvas/iframe-slash.ts +24 -11
  179. package/src/canvas/iframe-subtree.ts +6 -2
  180. package/src/canvas/path-mapping.ts +19 -12
  181. package/src/canvas/popover-path.ts +7 -3
  182. package/src/canvas/popover-state.ts +1 -2
  183. package/src/canvas/serialize-scope.ts +13 -5
  184. package/src/canvas/stage-scrollbar.ts +2 -1
  185. package/src/canvas/surface-registry.ts +21 -3
  186. package/src/collab/collab-commands.ts +7 -2
  187. package/src/collab/collab-session.ts +84 -45
  188. package/src/collab/collab-state.ts +4 -3
  189. package/src/collab/monaco-binding.ts +16 -13
  190. package/src/collab/monaco-cursors.ts +5 -2
  191. package/src/commands/budget.ts +3 -3
  192. package/src/commands/command-args.ts +103 -71
  193. package/src/commands/context.ts +22 -20
  194. package/src/commands/keymap.ts +47 -43
  195. package/src/commands/levels.ts +3 -3
  196. package/src/commands/live-context.ts +7 -7
  197. package/src/commands/reference.ts +2 -1
  198. package/src/commands/registry.ts +12 -10
  199. package/src/commands/run-reported.ts +15 -10
  200. package/src/component-props.ts +46 -30
  201. package/src/content/collection-match.ts +5 -4
  202. package/src/content/draft-state.ts +4 -3
  203. package/src/content/entry-commands.ts +3 -2
  204. package/src/content/entry-editor.ts +29 -17
  205. package/src/content/entry-fields.ts +12 -7
  206. package/src/content/entry-model.ts +21 -12
  207. package/src/editor/canvas-context-menu.ts +1 -2
  208. package/src/editor/canvas-slash-bridge.ts +3 -6
  209. package/src/editor/content-slice.ts +16 -14
  210. package/src/editor/context-menu.ts +35 -22
  211. package/src/editor/convert-targets.ts +30 -35
  212. package/src/editor/convert-to-component.ts +11 -5
  213. package/src/editor/convert-to-repeater.ts +34 -22
  214. package/src/editor/editable-tags.ts +17 -2
  215. package/src/editor/file-drop-action.ts +21 -22
  216. package/src/editor/inline-edit-apply.ts +35 -17
  217. package/src/editor/inline-edit.ts +66 -50
  218. package/src/editor/inline-format.ts +59 -56
  219. package/src/editor/inline-link.ts +40 -15
  220. package/src/editor/insert-zone-action.ts +1 -1
  221. package/src/editor/merge-tags.ts +58 -19
  222. package/src/editor/repeater-scope.ts +39 -28
  223. package/src/editor/shortcuts.ts +32 -20
  224. package/src/editor/slash-menu.ts +1 -6
  225. package/src/files/asset-versions.ts +4 -4
  226. package/src/files/components.ts +15 -10
  227. package/src/files/elements.ts +5 -6
  228. package/src/files/file-ops.ts +77 -36
  229. package/src/files/files.ts +90 -95
  230. package/src/files/media-meta.ts +2 -1
  231. package/src/files/media-paths.ts +3 -0
  232. package/src/files/serialize-document.ts +2 -2
  233. package/src/format/constraints.ts +1 -1
  234. package/src/format/convert-file.ts +27 -13
  235. package/src/format/format-choices.ts +2 -1
  236. package/src/format/format-host.ts +76 -50
  237. package/src/github/github-auth-status.ts +27 -3
  238. package/src/github/github-auth.ts +19 -9
  239. package/src/github/github-publish.ts +24 -20
  240. package/src/grid/cell-editors.ts +46 -31
  241. package/src/grid/cell-popovers.ts +5 -3
  242. package/src/grid/edit-buffer.ts +10 -9
  243. package/src/grid/grid-controller.ts +20 -22
  244. package/src/grid/grid-layout.ts +21 -5
  245. package/src/grid/grid-open.ts +3 -6
  246. package/src/grid/grid-panel.ts +11 -4
  247. package/src/grid/grid-source.ts +8 -8
  248. package/src/grid/grid-view.ts +14 -17
  249. package/src/grid/redirects-grid.ts +14 -5
  250. package/src/grid/redirects.ts +16 -14
  251. package/src/grid/schema-columns.ts +25 -19
  252. package/src/grid/sources/connector-source.ts +47 -20
  253. package/src/grid/sources/content-source.ts +25 -24
  254. package/src/grid/sources/csv-file-source.ts +2 -2
  255. package/src/grid/tabulator-tables.d.ts +55 -34
  256. package/src/hosting/stage.ts +7 -7
  257. package/src/i18n/i18n-commands.ts +1 -2
  258. package/src/media/media-pane.ts +4 -1
  259. package/src/new-project/add-repo-modal.ts +20 -19
  260. package/src/new-project/import-tab.ts +10 -14
  261. package/src/new-project/location-fields.ts +2 -1
  262. package/src/new-project/new-project-modal.ts +12 -9
  263. package/src/packages/jxsuite-update.ts +4 -3
  264. package/src/packages/pull-package-sync.ts +26 -25
  265. package/src/page-params.ts +97 -68
  266. package/src/panels/activity-panel.ts +17 -9
  267. package/src/panels/ai-chat/attached-context.ts +13 -7
  268. package/src/panels/ai-chat/chat-view.ts +80 -73
  269. package/src/panels/ai-chat/composer.ts +30 -33
  270. package/src/panels/ai-chat/sessions-view.ts +1 -2
  271. package/src/panels/ai-panel.ts +23 -20
  272. package/src/panels/block-action-bar.ts +46 -35
  273. package/src/panels/bottom-dock.ts +5 -3
  274. package/src/panels/canvas-dnd-bridge.ts +31 -24
  275. package/src/panels/data-explorer.ts +62 -25
  276. package/src/panels/data-grid.ts +14 -11
  277. package/src/panels/dnd.ts +108 -118
  278. package/src/panels/editors.ts +52 -21
  279. package/src/panels/elements-panel.ts +10 -3
  280. package/src/panels/events-panel.ts +84 -69
  281. package/src/panels/formula-workspace.ts +76 -51
  282. package/src/panels/frontmatter-fields.ts +24 -19
  283. package/src/panels/frontmatter-panel.ts +44 -34
  284. package/src/panels/git-diff-open.ts +4 -4
  285. package/src/panels/git-panel.ts +49 -61
  286. package/src/panels/head-panel.ts +131 -111
  287. package/src/panels/i18n-panel.ts +10 -3
  288. package/src/panels/imports-panel.ts +23 -47
  289. package/src/panels/jump-bar.ts +48 -29
  290. package/src/panels/layers-panel.ts +97 -74
  291. package/src/panels/left-panel.ts +3 -3
  292. package/src/panels/pane-context.ts +31 -22
  293. package/src/panels/pane-grid.ts +49 -20
  294. package/src/panels/panel-registry.ts +22 -37
  295. package/src/panels/properties-panel.ts +175 -121
  296. package/src/panels/quick-search.ts +53 -31
  297. package/src/panels/right-panel.ts +3 -3
  298. package/src/panels/seo-modal.ts +13 -17
  299. package/src/panels/settings-menu.ts +15 -15
  300. package/src/panels/signals-panel.ts +153 -131
  301. package/src/panels/statement-editor.ts +230 -140
  302. package/src/panels/style-panel.ts +218 -205
  303. package/src/panels/style-utils.ts +114 -51
  304. package/src/panels/stylebook-doc.ts +57 -54
  305. package/src/panels/stylebook-layers-panel.ts +9 -9
  306. package/src/panels/stylebook-panel.ts +13 -17
  307. package/src/panels/tab-drop.ts +27 -11
  308. package/src/panels/tab-strip.ts +36 -26
  309. package/src/platform-errors.ts +57 -43
  310. package/src/platform.ts +58 -30
  311. package/src/platforms/cloud.ts +345 -175
  312. package/src/platforms/default-platform.ts +10 -4
  313. package/src/platforms/devserver.ts +129 -117
  314. package/src/platforms/read-batcher.ts +16 -8
  315. package/src/project-list.ts +2 -1
  316. package/src/publish/deploy-checklist.ts +8 -2
  317. package/src/publish/pages-service.ts +28 -13
  318. package/src/publish/publish-commands.ts +5 -4
  319. package/src/publish/publish-panel.ts +10 -6
  320. package/src/recent-projects.ts +15 -8
  321. package/src/resize-edges.ts +26 -36
  322. package/src/services/a11y-report.ts +26 -19
  323. package/src/services/agent-seed.ts +23 -11
  324. package/src/services/ai-ask.ts +9 -12
  325. package/src/services/ai-command-tools.ts +68 -62
  326. package/src/services/ai-import-tools.ts +80 -67
  327. package/src/services/ai-models.ts +17 -13
  328. package/src/services/ai-project-tools.ts +68 -32
  329. package/src/services/ai-session-store.ts +19 -10
  330. package/src/services/ai-system-prompt.ts +46 -20
  331. package/src/services/ai-tools.ts +71 -27
  332. package/src/services/ai-write-report.ts +20 -12
  333. package/src/services/ai-writes.ts +4 -4
  334. package/src/services/automation.ts +58 -31
  335. package/src/services/cem-export.ts +149 -58
  336. package/src/services/clock.ts +6 -1
  337. package/src/services/code-services.ts +37 -31
  338. package/src/services/context-manager.ts +14 -10
  339. package/src/services/context-resolver.ts +20 -8
  340. package/src/services/data-service.ts +19 -15
  341. package/src/services/directory-picker.ts +18 -12
  342. package/src/services/document-assistant.ts +39 -19
  343. package/src/services/gated-registry.ts +10 -6
  344. package/src/services/harness/chat-reducer.ts +23 -1
  345. package/src/services/import-client.ts +91 -38
  346. package/src/services/import-seed.ts +8 -2
  347. package/src/services/jx-validate.ts +28 -31
  348. package/src/services/live-preview.ts +0 -0
  349. package/src/services/live-surfaces.ts +6 -3
  350. package/src/services/model-uri.ts +10 -2
  351. package/src/services/module-preloads.ts +12 -8
  352. package/src/services/monaco-buffer.ts +43 -24
  353. package/src/services/monaco-lazy.ts +7 -8
  354. package/src/services/monaco-setup.ts +23 -16
  355. package/src/services/notify.ts +44 -17
  356. package/src/services/popover-report.ts +25 -10
  357. package/src/services/preview-eval.ts +20 -8
  358. package/src/services/profile.ts +13 -6
  359. package/src/services/project-path.ts +13 -10
  360. package/src/services/references.ts +0 -0
  361. package/src/services/render-critic.ts +8 -7
  362. package/src/services/settings/kernel.ts +13 -4
  363. package/src/services/settings/write-queue.ts +9 -9
  364. package/src/services/token-lint.ts +12 -10
  365. package/src/services/tool-executor.ts +17 -5
  366. package/src/services/tool-outcomes.ts +44 -24
  367. package/src/services/trusted-types.ts +24 -7
  368. package/src/settings/contexts-section.ts +11 -8
  369. package/src/settings/contributed-section.ts +79 -60
  370. package/src/settings/css-vars-editor.ts +37 -16
  371. package/src/settings/defs-editor.ts +38 -35
  372. package/src/settings/extension-rows.ts +16 -8
  373. package/src/settings/extension-sections.ts +23 -13
  374. package/src/settings/extensions-section.ts +4 -1
  375. package/src/settings/general-settings.ts +11 -8
  376. package/src/settings/locales-section.ts +20 -12
  377. package/src/settings/preferences-dialog.ts +9 -6
  378. package/src/settings/preferences-keymap.ts +7 -5
  379. package/src/settings/preferences-sections.ts +11 -5
  380. package/src/settings/project-sections.ts +1 -1
  381. package/src/settings/schema-field-ui.ts +9 -0
  382. package/src/settings/settings-document.ts +5 -5
  383. package/src/shell/tree.ts +6 -5
  384. package/src/shell.ts +104 -81
  385. package/src/site-context.ts +37 -22
  386. package/src/state.ts +41 -49
  387. package/src/store.ts +54 -34
  388. package/src/studio.ts +25 -75
  389. package/src/style/live-preview.ts +1 -1
  390. package/src/style/project-styles.ts +6 -7
  391. package/src/style/token-ref.ts +5 -4
  392. package/src/surfaces/about.ts +18 -19
  393. package/src/surfaces/add-repo.ts +17 -19
  394. package/src/surfaces/ai-chat.ts +31 -36
  395. package/src/surfaces/ai-credentials-form.ts +6 -16
  396. package/src/surfaces/ai-managed-connect.ts +3 -4
  397. package/src/surfaces/ai-model-picker.ts +3 -5
  398. package/src/surfaces/block-action-bar.ts +22 -11
  399. package/src/surfaces/boot-failure.ts +4 -5
  400. package/src/surfaces/bottom-dock.ts +9 -18
  401. package/src/surfaces/canvas-stage.ts +12 -11
  402. package/src/surfaces/cf-account-picker.ts +17 -18
  403. package/src/surfaces/commandbar.ts +14 -9
  404. package/src/surfaces/convert-repeater.ts +17 -19
  405. package/src/surfaces/data-actions.ts +9 -7
  406. package/src/surfaces/dialog.ts +26 -28
  407. package/src/surfaces/diff-toolbar.ts +9 -7
  408. package/src/surfaces/doc-header.ts +10 -19
  409. package/src/surfaces/empty-state.ts +7 -10
  410. package/src/surfaces/entry-editor.ts +14 -11
  411. package/src/surfaces/expression-editor.ts +11 -25
  412. package/src/surfaces/files-panel.ts +27 -14
  413. package/src/surfaces/formula-palette.ts +4 -5
  414. package/src/surfaces/git-panel.ts +7 -15
  415. package/src/surfaces/github-auth.ts +16 -20
  416. package/src/surfaces/github-publish.ts +17 -22
  417. package/src/surfaces/grid-cell.ts +30 -31
  418. package/src/surfaces/grid-open.ts +10 -14
  419. package/src/surfaces/grid-panel.ts +13 -9
  420. package/src/surfaces/grid-replace.ts +13 -13
  421. package/src/surfaces/grid-views.ts +15 -18
  422. package/src/surfaces/inspector-dock.ts +13 -12
  423. package/src/surfaces/jump-bar.ts +5 -6
  424. package/src/surfaces/jxsuite-update.ts +16 -22
  425. package/src/surfaces/library-pane.ts +19 -20
  426. package/src/surfaces/logic-panel.ts +17 -30
  427. package/src/surfaces/logic-workspace.ts +13 -15
  428. package/src/surfaces/media-browser.ts +11 -10
  429. package/src/surfaces/media-field.ts +4 -5
  430. package/src/surfaces/media-pane.ts +8 -7
  431. package/src/surfaces/menu.ts +29 -23
  432. package/src/surfaces/navigator-dock.ts +11 -12
  433. package/src/surfaces/new-project.ts +27 -26
  434. package/src/surfaces/pane-context.ts +20 -25
  435. package/src/surfaces/pane-grid.ts +15 -10
  436. package/src/surfaces/panel-activity.ts +5 -6
  437. package/src/surfaces/panel-data.ts +3 -4
  438. package/src/surfaces/panel-deploy-checklist.ts +5 -6
  439. package/src/surfaces/panel-elements.ts +5 -7
  440. package/src/surfaces/panel-i18n.ts +5 -7
  441. package/src/surfaces/panel-imports.ts +6 -13
  442. package/src/surfaces/panel-outline.ts +25 -19
  443. package/src/surfaces/panel-page.ts +11 -18
  444. package/src/surfaces/panel-problems.ts +5 -7
  445. package/src/surfaces/panel-signals.ts +20 -34
  446. package/src/surfaces/panel-stylebook-layers.ts +5 -8
  447. package/src/surfaces/preferences.ts +21 -11
  448. package/src/surfaces/progress-modal.ts +15 -9
  449. package/src/surfaces/properties-panel.json +1 -1
  450. package/src/surfaces/properties-panel.ts +20 -42
  451. package/src/surfaces/publish.ts +18 -18
  452. package/src/surfaces/push-plan.ts +7 -8
  453. package/src/surfaces/rail.ts +13 -13
  454. package/src/surfaces/reference-field.ts +11 -15
  455. package/src/surfaces/schema-builder.ts +16 -22
  456. package/src/surfaces/schema-form.ts +15 -25
  457. package/src/surfaces/secret-field.ts +9 -6
  458. package/src/surfaces/seo.ts +21 -15
  459. package/src/surfaces/settings-contexts.ts +9 -7
  460. package/src/surfaces/settings-contributed.ts +14 -10
  461. package/src/surfaces/settings-css-vars.ts +5 -12
  462. package/src/surfaces/settings-defs.json +2 -2
  463. package/src/surfaces/settings-defs.ts +17 -21
  464. package/src/surfaces/settings-deploy.ts +8 -15
  465. package/src/surfaces/settings-extensions.ts +5 -6
  466. package/src/surfaces/settings-head.ts +33 -39
  467. package/src/surfaces/settings-locales.ts +8 -20
  468. package/src/surfaces/settings-overview.ts +5 -6
  469. package/src/surfaces/settings-packages.ts +8 -7
  470. package/src/surfaces/settings-pane.ts +9 -18
  471. package/src/surfaces/settings-rawjson.ts +5 -6
  472. package/src/surfaces/shell.ts +1 -2
  473. package/src/surfaces/slash-menu.ts +19 -9
  474. package/src/surfaces/statements.ts +13 -18
  475. package/src/surfaces/statusbar.ts +17 -14
  476. package/src/surfaces/style-panel.ts +18 -31
  477. package/src/surfaces/stylebook-chrome.ts +3 -5
  478. package/src/surfaces/tab-strip.ts +14 -13
  479. package/src/surfaces/target-line.ts +6 -7
  480. package/src/surfaces/welcome.ts +5 -7
  481. package/src/tabs/patch-ops.ts +4 -3
  482. package/src/tabs/project-config.ts +32 -21
  483. package/src/tabs/selection.ts +36 -8
  484. package/src/tabs/tab.ts +52 -41
  485. package/src/tabs/transact.ts +98 -53
  486. package/src/types.ts +32 -17
  487. package/src/ui/ai-credentials-form.ts +10 -13
  488. package/src/ui/ai-model-picker.ts +3 -3
  489. package/src/ui/cf-account-picker.ts +7 -9
  490. package/src/ui/color-selector.ts +9 -5
  491. package/src/ui/dynamic-slot.ts +7 -8
  492. package/src/ui/expression-editor.ts +112 -105
  493. package/src/ui/form-controls.ts +57 -48
  494. package/src/ui/formula-catalog.ts +239 -230
  495. package/src/ui/formula-chips.ts +33 -18
  496. package/src/ui/layers.ts +44 -31
  497. package/src/ui/media-picker.ts +18 -9
  498. package/src/ui/popover-surface.ts +20 -12
  499. package/src/ui/progress-modal.ts +13 -8
  500. package/src/ui/regions.ts +7 -6
  501. package/src/ui/schema-form.ts +113 -70
  502. package/src/ui/surface.ts +71 -8
  503. package/src/ui/timing.ts +9 -0
  504. package/src/ui/value-source.ts +44 -31
  505. package/src/ui/virtual-window.ts +3 -2
  506. package/src/utils/base64.ts +3 -1
  507. package/src/utils/edit-display.ts +73 -73
  508. package/src/utils/element-selectors.ts +3 -2
  509. package/src/utils/google-fonts.ts +15 -14
  510. package/src/utils/inherited-style.ts +18 -20
  511. package/src/utils/link-target.ts +1 -1
  512. package/src/utils/preview-format.ts +25 -3
  513. package/src/utils/strip-events.ts +49 -22
  514. package/src/utils/studio-utils.ts +29 -14
  515. package/src/view.ts +2 -3
  516. package/src/workspace/pane-derive.ts +10 -12
  517. package/src/workspace/session.ts +53 -43
  518. package/src/workspace/workspace.ts +40 -36
  519. package/dist/chunks/adapter-f93pafd7.js +0 -4
  520. package/dist/chunks/cf-account-picker-af1h381v.js +0 -4
  521. package/dist/chunks/cf-settings-125tdm0w.js +0 -4
  522. package/dist/chunks/color-area-jqhna8zy.js +0 -4
  523. package/dist/chunks/color-field-embq5ctg.js +0 -4
  524. package/dist/chunks/color-fpezvnkt.js +0 -4
  525. package/dist/chunks/color-slider-1qmw7wqy.js +0 -4
  526. package/dist/chunks/combobox-773hdzyj.js +0 -4
  527. package/dist/chunks/components-ax2f1fnn.js +0 -4
  528. package/dist/chunks/content-source-108aqtk1.js +0 -4
  529. package/dist/chunks/convert-to-repeater-ry5rmppr.js +0 -4
  530. package/dist/chunks/dialog-mtm9fq5t.js +0 -4
  531. package/dist/chunks/editor-pfgdmkb0.js +0 -4
  532. package/dist/chunks/entry-commands-t0k1q0kx.js +0 -4
  533. package/dist/chunks/extension-sections-6mwh2hf8.js +0 -4
  534. package/dist/chunks/file-ops-meej5mqf.js +0 -4
  535. package/dist/chunks/files-44vp8a2s.js +0 -4
  536. package/dist/chunks/format-host-7q1petb9.js +0 -4
  537. package/dist/chunks/fs-events-h2mwnxaz.js +0 -4
  538. package/dist/chunks/git-panel-zngbymvd.js +0 -4
  539. package/dist/chunks/grid-view-sx9vynsp.js +0 -51
  540. package/dist/chunks/icons-p44xhjf0.js +0 -4
  541. package/dist/chunks/iframe-entry-f7mqjwpm.js +0 -5
  542. package/dist/chunks/iframe-entry-haw7jkvz.js +0 -5
  543. package/dist/chunks/iframe-entry-z6qrytv3.js +0 -5
  544. package/dist/chunks/iframe-host-qk3ycq87.js +0 -4
  545. package/dist/chunks/index-wkyfjeqz.js +0 -4
  546. package/dist/chunks/jx-validate-psgd7vsx.js +0 -4
  547. package/dist/chunks/layers-panel-1kq2jyfk.js +0 -4
  548. package/dist/chunks/layers-xd92fg8d.js +0 -4
  549. package/dist/chunks/listbox-s8fkmnq8.js +0 -4
  550. package/dist/chunks/lit-html-ac4cwwr4.js +0 -4
  551. package/dist/chunks/media-picker-0svzkdyb.js +0 -4
  552. package/dist/chunks/menu-37d07z9m.js +0 -4
  553. package/dist/chunks/menu-5c55ea3d.js +0 -4
  554. package/dist/chunks/monaco-binding-z6r70pdt.js +0 -4
  555. package/dist/chunks/monaco-buffer-fz6gq5y7.js +0 -4
  556. package/dist/chunks/monaco-setup-q4axkgj7.js +0 -4
  557. package/dist/chunks/notify-0xgtn8q1.js +0 -4
  558. package/dist/chunks/number-field-gw7sq8rn.js +0 -4
  559. package/dist/chunks/parse-897fvqah.js +0 -4
  560. package/dist/chunks/platform-v1qctbe6.js +0 -4
  561. package/dist/chunks/popover-8htra4er.js +0 -4
  562. package/dist/chunks/publish-panel-ym1fmgq5.js +0 -4
  563. package/dist/chunks/quick-search-a54e6t5n.js +0 -4
  564. package/dist/chunks/references-gmqp82fs.js +0 -4
  565. package/dist/chunks/right-panel-v7t9awbk.js +0 -4
  566. package/dist/chunks/section-registry-vhyf3zsr.js +0 -4
  567. package/dist/chunks/select-n3v7gwas.js +0 -4
  568. package/dist/chunks/split-pff8f3ze.js +0 -4
  569. package/dist/chunks/studio-0bhx5hz6.js +0 -5
  570. package/dist/chunks/studio-0k1ddnyy.js +0 -6
  571. package/dist/chunks/studio-1b04pd0j.js +0 -64
  572. package/dist/chunks/studio-4dwbj6gb.js +0 -5
  573. package/dist/chunks/studio-4tanh828.js +0 -5
  574. package/dist/chunks/studio-55h4mstj.js +0 -4
  575. package/dist/chunks/studio-5tnvjkz4.js +0 -5
  576. package/dist/chunks/studio-60dqgb0c.js +0 -5
  577. package/dist/chunks/studio-61dp09qg.js +0 -9
  578. package/dist/chunks/studio-63epcrj2.js +0 -5
  579. package/dist/chunks/studio-74k29dy9.js +0 -5
  580. package/dist/chunks/studio-7vg6jcd3.js +0 -5
  581. package/dist/chunks/studio-7zx31knw.js +0 -9
  582. package/dist/chunks/studio-8bxk97km.js +0 -5
  583. package/dist/chunks/studio-933e30n4.js +0 -5
  584. package/dist/chunks/studio-98wnwhbg.js +0 -5
  585. package/dist/chunks/studio-9mccjwjh.js +0 -14
  586. package/dist/chunks/studio-a2ke1e4t.js +0 -5
  587. package/dist/chunks/studio-e3a2a6v3.js +0 -4
  588. package/dist/chunks/studio-e81q1vb9.js +0 -5
  589. package/dist/chunks/studio-epgrmqqm.js +0 -5
  590. package/dist/chunks/studio-ery42sqr.js +0 -4
  591. package/dist/chunks/studio-g1shz58g.js +0 -5
  592. package/dist/chunks/studio-gf0fz03c.js +0 -5
  593. package/dist/chunks/studio-gjtpe62q.js +0 -6
  594. package/dist/chunks/studio-gkskhg7m.js +0 -5
  595. package/dist/chunks/studio-gx4dnjzd.js +0 -5
  596. package/dist/chunks/studio-j03zga3y.js +0 -9
  597. package/dist/chunks/studio-jhpxeckz.js +0 -5
  598. package/dist/chunks/studio-k01r7ykp.js +0 -5
  599. package/dist/chunks/studio-kk7h30ne.js +0 -7
  600. package/dist/chunks/studio-kt1zqney.js +0 -5
  601. package/dist/chunks/studio-m3cwhvye.js +0 -5
  602. package/dist/chunks/studio-mcayt6hz.js +0 -5
  603. package/dist/chunks/studio-myjcghv3.js +0 -5
  604. package/dist/chunks/studio-n8bxwc3y.js +0 -4
  605. package/dist/chunks/studio-ngm8gvpb.js +0 -5
  606. package/dist/chunks/studio-nj5a01jy.js +0 -928
  607. package/dist/chunks/studio-pcgz8heb.js +0 -5
  608. package/dist/chunks/studio-qcsq0t15.js +0 -5
  609. package/dist/chunks/studio-qkstvve9.js +0 -5
  610. package/dist/chunks/studio-qv1pwd3c.js +0 -5
  611. package/dist/chunks/studio-rc6dvxjb.js +0 -7
  612. package/dist/chunks/studio-s8dmmz66.js +0 -5
  613. package/dist/chunks/studio-sp5xzbj4.js +0 -6
  614. package/dist/chunks/studio-szsjbj0g.js +0 -5
  615. package/dist/chunks/studio-t1xzawhr.js +0 -16
  616. package/dist/chunks/studio-t6b6qe31.js +0 -5
  617. package/dist/chunks/studio-tdcprbqb.js +0 -5
  618. package/dist/chunks/studio-vyc0b4he.js +0 -10
  619. package/dist/chunks/studio-w0cgf6jg.js +0 -5
  620. package/dist/chunks/studio-w3hskzqk.js +0 -5
  621. package/dist/chunks/studio-xgrgcdyh.js +0 -9
  622. package/dist/chunks/studio-xz2yxfcb.js +0 -5
  623. package/dist/chunks/studio-y6n42tgs.js +0 -5
  624. package/dist/chunks/studio-yb560vxr.js +0 -5
  625. package/dist/chunks/studio-yn9f8vtx.js +0 -5
  626. package/dist/chunks/studio-yw05w8f0.js +0 -7
  627. package/dist/chunks/studio-z16sgavc.js +0 -5
  628. package/dist/chunks/studio-zyqn40w4.js +0 -5
  629. package/dist/chunks/swatch-group-8qh9a3bm.js +0 -4
  630. package/dist/chunks/tabs-jwthg40w.js +0 -4
  631. package/dist/chunks/textfield-spy3hc77.js +0 -4
  632. package/dist/chunks/toast-4d35zd64.js +0 -4
  633. package/dist/chunks/toast-host-8sjmfed3.js +0 -4
  634. package/dist/chunks/tooltip-fd6g9xy3.js +0 -4
  635. package/dist/chunks/tree-h19v1jfz.js +0 -4
  636. package/dist/chunks/workspace-zt81g1ms.js +0 -4
  637. package/dist/chunks/ws-client-n2bh4hkf.js +0 -4
@@ -44,7 +44,16 @@
44
44
  * @docs studio/design/style-inspector
45
45
  */
46
46
 
47
- import { getNestedStyle } from "@jxsuite/schema/guards";
47
+ import {
48
+ getNestedStyle,
49
+ isFunctionDef,
50
+ isJsonObject,
51
+ isNestedStyle,
52
+ isNodeObject,
53
+ isNonNullObject,
54
+ isNumber,
55
+ isString,
56
+ } from "@jxsuite/schema/guards";
48
57
  import { effect, effectScope, reactive } from "../reactivity";
49
58
  import {
50
59
  cancelStyleDebounce,
@@ -76,6 +85,7 @@ import {
76
85
  varDisplayName,
77
86
  } from "../utils/studio-utils";
78
87
  import { stringArg } from "../commands/command-args";
88
+ import type { CommandArgValues, PropertySchema } from "../commands/command-args";
79
89
  import type { AnyCommand, CommandRegistry } from "../commands/registry";
80
90
  import { showPromptDialog } from "../ui/layers";
81
91
  import { parseMediaEntries, schemeOfQuery } from "../utils/canvas-media";
@@ -92,7 +102,7 @@ import {
92
102
  import {
93
103
  attachTargetLine,
94
104
  openSelectorMenu,
95
- resetTargetLine as resetTargetSelector,
105
+ resetTargetLine,
96
106
  setTargetLine,
97
107
  } from "../surfaces/target-line";
98
108
  import { clickAnythingTo, openPageAction, staleSelectionMessage } from "./empty-state";
@@ -101,20 +111,23 @@ import {
101
111
  autoOpenSections,
102
112
  compressBorderSide,
103
113
  compressShorthand,
114
+ cssDefs,
104
115
  cssMeta,
105
116
  expandBorderSide,
106
117
  expandShorthand,
107
118
  getCssInitialMap,
108
119
  getFontVars,
120
+ isDeclarationValue,
109
121
  TYPO_PREVIEW_CHANNELS,
110
122
  getLonghands,
111
123
  } from "./style-utils";
124
+ import type { CssFontPreset, CssLonghand, CssMetaEntry } from "./style-utils";
112
125
  import { UNIT_RE } from "../ui/unit-selector";
113
126
  import { colorTokens, resolvedColor } from "../ui/color-selector";
114
127
  import { resolveTokenValue, toTokenRef, tokenRefName } from "../style/token-ref";
115
128
  import { mountStylePanelSurface } from "../surfaces/style-panel";
116
129
  import { openMenu } from "../surfaces/menu";
117
- import type { MenuHandle } from "../surfaces/menu";
130
+ import type { MenuHandle, MenuRowProjection } from "../surfaces/menu";
118
131
  import { cloneValue } from "../tabs/doc-op-apply";
119
132
  import { effectiveSlotMode, slotModeSeed, switchSlotMode } from "../ui/dynamic-slot";
120
133
  import { slotCaps, VALUE_SOURCE_HINTS, VALUE_SOURCE_LABELS } from "../ui/value-source";
@@ -138,21 +151,18 @@ import type { JxPath } from "../state";
138
151
  import type { JsonValue } from "../types";
139
152
  import type { JxMutableNode, JxStyle } from "@jxsuite/schema/types";
140
153
 
141
- interface CssLonghand {
142
- name: string;
143
- entry: Record<string, unknown>;
144
- }
145
-
146
- type CssPropertyEntry = Record<string, unknown>;
147
-
148
154
  /** The chip a row wears when its caller has nothing more specific to say. */
149
155
  const DEFAULT_PROVENANCE: FieldProvenance = { state: "default" };
150
156
 
151
- type StyleMutateFn = (
152
- t: Tab,
153
- prop: string,
154
- val?: string | Record<string, unknown> | undefined,
155
- ) => void;
157
+ /**
158
+ * One write to one compound coordinate. A value is a declaration, `undefined` to delete it, or —
159
+ * from Relative Styling's add-a-rule, and nowhere else — the empty block `{}` a new nested rule
160
+ * starts as.
161
+ */
162
+ type StyleMutateFn = (t: Tab, prop: string, val?: string | JxStyle | undefined) => void;
163
+
164
+ /** The `mutateUpdate*Style` writer for one coordinate, at the `string` type they declare. */
165
+ type StyleWriteFn = (t: Tab, prop: string, value: string | undefined) => void;
156
166
 
157
167
  /**
158
168
  * Check if a selector is a stylebook tag path (e.g., "table" or "table th"). Tag paths don't start
@@ -209,7 +219,7 @@ function resolveContextStyle(
209
219
  }
210
220
  const flat: JxStyle = {};
211
221
  for (const [p, v] of Object.entries(base)) {
212
- if (typeof v !== "object") {
222
+ if (isDeclarationValue(v)) {
213
223
  flat[p] = v;
214
224
  }
215
225
  }
@@ -232,38 +242,26 @@ function contextMutate(
232
242
  activeSelector: string | null,
233
243
  editMedia: string | null,
234
244
  ): StyleMutateFn {
245
+ let write: StyleWriteFn;
235
246
  if (activeSelector && isTagPath(activeSelector)) {
236
247
  const stylePath = activeSelector.split(" ");
237
- return editMedia
238
- ? (t, prop, val) =>
239
- mutateUpdateMediaNestedStylePath(
240
- t,
241
- sel,
242
- editMedia,
243
- stylePath,
244
- prop,
245
- val as string | undefined,
246
- )
247
- : (t, prop, val) =>
248
- mutateUpdateNestedStylePath(t, sel, stylePath, prop, val as string | undefined);
249
- }
250
- if (activeSelector) {
251
- return editMedia
252
- ? (t, prop, val) =>
253
- mutateUpdateMediaNestedStyle(
254
- t,
255
- sel,
256
- editMedia,
257
- activeSelector,
258
- prop,
259
- val as string | undefined,
260
- )
261
- : (t, prop, val) =>
262
- mutateUpdateNestedStyle(t, sel, activeSelector, prop, val as string | undefined);
263
- }
264
- return editMedia
265
- ? (t, prop, val) => mutateUpdateMediaStyle(t, sel, editMedia, prop, val as string | undefined)
266
- : (t, prop, val) => mutateUpdateStyle(t, sel, prop, val as string | undefined);
248
+ write = editMedia
249
+ ? (t, prop, val) => mutateUpdateMediaNestedStylePath(t, sel, editMedia, stylePath, prop, val)
250
+ : (t, prop, val) => mutateUpdateNestedStylePath(t, sel, stylePath, prop, val);
251
+ } else if (activeSelector) {
252
+ write = editMedia
253
+ ? (t, prop, val) => mutateUpdateMediaNestedStyle(t, sel, editMedia, activeSelector, prop, val)
254
+ : (t, prop, val) => mutateUpdateNestedStyle(t, sel, activeSelector, prop, val);
255
+ } else {
256
+ write = editMedia
257
+ ? (t, prop, val) => mutateUpdateMediaStyle(t, sel, editMedia, prop, val)
258
+ : (t, prop, val) => mutateUpdateStyle(t, sel, prop, val);
259
+ }
260
+ /* SAFETY: the one non-string value a commit carries is the empty block `{}` Relative Styling
261
+ seeds a new nested rule with (see StyleMutateFn). Every `mutateUpdate*Style` writer stores the
262
+ value it is given as-is, deleting only on `undefined` or `""`, so the block lands as the rule's
263
+ body; their `string` parameter is narrower than what they store. */
264
+ return (t, prop, val) => write(t, prop, val as string | undefined);
267
265
  }
268
266
 
269
267
  /**
@@ -293,7 +291,7 @@ function mixedStyleProps(
293
291
  return mixed;
294
292
  }
295
293
  const blocks = targets.map((path) => {
296
- const node = getNodeAtPath(tab.doc.document, path) as JxMutableNode | undefined;
294
+ const node = getNodeAtPath(tab.doc.document, path);
297
295
  return resolveContextStyle(node?.style ?? {}, activeSelector, editMedia);
298
296
  });
299
297
  const props = new Set<string>();
@@ -314,7 +312,7 @@ function mixedStyleProps(
314
312
 
315
313
  /** Forget the selector menu's element handle — the Inspector unmounted, or a test starts clean. */
316
314
  export function resetSelectorMenu(): void {
317
- resetTargetSelector();
315
+ resetTargetLine();
318
316
  }
319
317
 
320
318
  /**
@@ -340,7 +338,7 @@ export function resetAffectedDisclosure(): void {
340
338
  * §6.2 forbids, so it routes to the definition site through the same command the context bar's
341
339
  * "Manage contexts…" footer runs.
342
340
  */
343
- function runCommand(id: string, args?: Record<string, unknown>): void {
341
+ function runCommand(id: string, args?: CommandArgValues): void {
344
342
  void activeRegistry()?.run(id, args);
345
343
  }
346
344
 
@@ -523,16 +521,16 @@ interface InheritedInfo {
523
521
  * `state.` is optional because both spellings occur in documents and both mean the same entry — the
524
522
  * chip names the signal, not the accessor path it was reached by.
525
523
  */
526
- function templateSignalOf(value: unknown): string | null {
527
- if (typeof value !== "string") {
524
+ function templateSignalOf(value: JxStyle[string]): string | null {
525
+ if (!isString(value)) {
528
526
  return null;
529
527
  }
530
528
  return /\$\{\s*(?:state\.)?([A-Za-z_$][\w$]*)/.exec(value)?.[1] ?? null;
531
529
  }
532
530
 
533
531
  /** Whether a value is a `${}` template rather than a literal — the `bound` rung for a CSS value. */
534
- function isTemplateValue(value: unknown): boolean {
535
- return typeof value === "string" && value.includes("${");
532
+ function isTemplateValue(value: JxStyle[string]): boolean {
533
+ return isString(value) && value.includes("${");
536
534
  }
537
535
 
538
536
  /** What the Style tab can say about one property's origin. */
@@ -582,28 +580,27 @@ function provenanceOf(prop: string, ctx: ProvenanceCtx): FieldProvenance {
582
580
  if (value !== undefined) {
583
581
  if (isTemplateValue(value)) {
584
582
  const signal = templateSignalOf(value);
585
- return {
586
- state: "bound",
587
- ...(signal ? { donor: signal } : {}),
588
- ...(signal && ctx.signals.includes(signal)
589
- ? {
590
- onClick: () => {
591
- runCommand("view.setActivity", { tab: "data" });
592
- runCommand("data.expandRow", { name: signal });
593
- },
594
- }
595
- : {}),
596
- };
583
+ const bound: FieldProvenance = { state: "bound" };
584
+ if (signal) {
585
+ bound.donor = signal;
586
+ if (ctx.signals.includes(signal)) {
587
+ bound.onClick = () => {
588
+ runCommand("view.setActivity", { tab: "data" });
589
+ runCommand("data.expandRow", { name: signal });
590
+ };
591
+ }
592
+ }
593
+ return bound;
597
594
  }
598
595
  return { state: "set", onClick: () => ctx.clear(prop) };
599
596
  }
600
597
  const inherited = ctx.inherited[prop];
601
598
  if (inherited) {
602
- return {
603
- state: "inherited",
604
- donor: inherited.donor,
605
- ...(inherited.jump ? { onClick: inherited.jump } : {}),
606
- };
599
+ const prov: FieldProvenance = { state: "inherited", donor: inherited.donor };
600
+ if (inherited.jump) {
601
+ prov.onClick = inherited.jump;
602
+ }
603
+ return prov;
607
604
  }
608
605
  return { state: "default" };
609
606
  }
@@ -628,14 +625,14 @@ function shorthandProvenance(
628
625
  ctx: ProvenanceCtx,
629
626
  clearAll?: (() => void) | undefined,
630
627
  ): FieldProvenance {
631
- const props = [shortProp, ...(getLonghands(shortProp) as CssLonghand[]).map((l) => l.name)];
628
+ const props = [shortProp, ...getLonghands(shortProp).map((l) => l.name)];
632
629
  const { mixed } = ctx;
633
630
  if (mixed && props.some((p) => mixed.props.has(p))) {
634
- return {
635
- state: "mixed",
636
- donor: String(mixed.count),
637
- ...(clearAll ? { onClick: clearAll } : {}),
638
- };
631
+ const prov: FieldProvenance = { state: "mixed", donor: String(mixed.count) };
632
+ if (clearAll) {
633
+ prov.onClick = clearAll;
634
+ }
635
+ return prov;
639
636
  }
640
637
  for (const p of props) {
641
638
  const prov = provenanceOf(p, ctx);
@@ -683,13 +680,10 @@ function longhandProvenance(
683
680
  /**
684
681
  * A section's tally, counting a shorthand as set when any of its longhands is.
685
682
  *
686
- * @param {{ prop: string; entry: CssPropertyEntry }[]} entries
683
+ * @param {CssPropertyItem[]} entries
687
684
  * @param {ProvenanceCtx} ctx
688
685
  */
689
- function sectionProvenance(
690
- entries: { prop: string; entry: CssPropertyEntry }[],
691
- ctx: ProvenanceCtx,
692
- ): ProvenanceState[] {
686
+ function sectionProvenance(entries: CssPropertyItem[], ctx: ProvenanceCtx): ProvenanceState[] {
693
687
  return entries.map(({ prop, entry }) =>
694
688
  inferInputType(entry) === "shorthand"
695
689
  ? shorthandProvenance(prop, ctx).state
@@ -699,6 +693,24 @@ function sectionProvenance(
699
693
 
700
694
  // ─── The row register ────────────────────────────────────────────────────────
701
695
 
696
+ /** One css-meta property, with its entry, as a section lists it. */
697
+ interface CssPropertyItem {
698
+ prop: string;
699
+ entry: CssMetaEntry;
700
+ }
701
+
702
+ /** One projected row and the verbs registered for it. */
703
+ interface ProjectedRow {
704
+ row: StyleRowView;
705
+ actions: RowActions;
706
+ }
707
+
708
+ /** A button-group row's buttons, and the values only its overflow list offers. */
709
+ interface ButtonGroupView {
710
+ buttons: StyleButtonView[];
711
+ extra: string[];
712
+ }
713
+
702
714
  /** One choice a row's list offers. `divider` draws a rule above the entry. */
703
715
  interface StyleChoice {
704
716
  value: string;
@@ -732,7 +744,8 @@ interface RowActions {
732
744
  mode: SlotMode;
733
745
  caps: SlotMode[];
734
746
  fieldKey: string;
735
- value: unknown;
747
+ /** The field's literal, or `undefined` when it is empty. */
748
+ value: string | undefined;
736
749
  stateDefs: string[];
737
750
  onChange: (value?: JsonValue) => void;
738
751
  };
@@ -795,15 +808,11 @@ function isNumericValue(value: string): boolean {
795
808
  * manifest lacks is a red `tests/metadata.test.ts`, which is where a miss is caught before it ships
796
809
  * rather than degraded after.
797
810
  */
798
- function buttonGroup(
799
- entry: CssPropertyEntry,
800
- rowKey: string,
801
- value: string,
802
- ): { buttons: StyleButtonView[]; extra: string[] } {
803
- const values = (entry.$buttonValues || entry.enum || []) as string[];
804
- const iconMap = (entry.$icons || {}) as Record<string, string>;
805
- const buttonValues = entry.$buttonValues as string[] | undefined;
806
- const enumValues = entry.enum as string[] | undefined;
811
+ function buttonGroup(entry: CssMetaEntry, rowKey: string, value: string): ButtonGroupView {
812
+ const values = entry.$buttonValues || entry.enum || [];
813
+ const iconMap = entry.$icons || {};
814
+ const buttonValues = entry.$buttonValues;
815
+ const enumValues = entry.enum;
807
816
  const extra =
808
817
  buttonValues && enumValues && enumValues.length > buttonValues.length
809
818
  ? enumValues.filter((v) => !buttonValues.includes(v))
@@ -842,7 +851,7 @@ function keywordChoices(options: string[], value: string): StyleChoice[] {
842
851
  * words alone.
843
852
  */
844
853
  function keywordOptions(options: string[], prop: string, face: string): StyleOptionView[] {
845
- const channel = TYPO_PREVIEW_CHANNELS[prop];
854
+ const channel = TYPO_PREVIEW_CHANNELS.get(prop);
846
855
  return options.map((v) => {
847
856
  const option: StyleOptionView = { label: keywordLabel(v), value: v };
848
857
  if (channel) {
@@ -862,16 +871,16 @@ function keywordOptions(options: string[], prop: string, face: string): StyleOpt
862
871
  */
863
872
  function previewFace(ctx: EditorCtx): string {
864
873
  const raw = ctx.activeStyle.fontFamily ?? ctx.inheritedStyle.fontFamily;
865
- if (typeof raw !== "string" && typeof raw !== "number") {
874
+ if (!isString(raw) && !isNumber(raw)) {
866
875
  return "";
867
876
  }
868
- const resolved = resolveTokenValue(getEffectiveStyle(ctx.tab.doc.document?.style), raw);
877
+ const resolved = resolveTokenValue(getEffectiveStyle(ctx.tab.doc.document.style), raw);
869
878
  return resolved === undefined ? "" : String(resolved);
870
879
  }
871
880
 
872
881
  /** The Modern Font Stacks a `fontFamily` entry offers, as css-meta.json states them. */
873
- function fontPresets(entry: CssPropertyEntry): { title: string; value: string }[] {
874
- return Array.isArray(entry.presets) ? (entry.presets as { title: string; value: string }[]) : [];
882
+ function fontPresets(entry: CssMetaEntry): CssFontPreset[] {
883
+ return Array.isArray(entry.presets) ? entry.presets : [];
875
884
  }
876
885
 
877
886
  /**
@@ -886,7 +895,7 @@ function fontPresets(entry: CssPropertyEntry): { title: string; value: string }[
886
895
  * the pick will write is in the list; a preset row is bare, which is what tells the two groups
887
896
  * apart in a list that draws no divider.
888
897
  */
889
- function fontOptions(entry: CssPropertyEntry): StyleOptionView[] {
898
+ function fontOptions(entry: CssMetaEntry): StyleOptionView[] {
890
899
  const fontVars = getFontVars();
891
900
  const style = getEffectiveStyle(activeTab.value?.doc.document?.style);
892
901
  const options: StyleOptionView[] = fontVars.map((fv) => ({
@@ -913,7 +922,7 @@ function fontOptions(entry: CssPropertyEntry): StyleOptionView[] {
913
922
  * minted early would be left behind when they finished typing something else.
914
923
  */
915
924
  function commitFont(
916
- entry: CssPropertyEntry,
925
+ entry: CssMetaEntry,
917
926
  raw: string,
918
927
  mint: boolean,
919
928
  onCommit: (value: string) => void,
@@ -1008,7 +1017,7 @@ interface EditorCtx {
1008
1017
  /** One write, fanned across every selected element, inside the caller's transaction. */
1009
1018
  mutate: StyleMutateFn;
1010
1019
  /** The same write in a transaction of its own. */
1011
- commit: (prop: string, value?: string | Record<string, unknown> | undefined) => void;
1020
+ commit: (prop: string, value?: string | JxStyle | undefined) => void;
1012
1021
  /** The key prefix a row's identity is built from — the coordinate, spelled once. */
1013
1022
  coord: string;
1014
1023
  targets: readonly JxPath[];
@@ -1034,7 +1043,7 @@ function styleCaps(): SlotMode[] {
1034
1043
  /** One ordinary property row: the label, the chip, the ladder and whichever control it draws. */
1035
1044
  function fieldRow(
1036
1045
  ctx: EditorCtx,
1037
- entry: CssPropertyEntry,
1046
+ entry: CssMetaEntry,
1038
1047
  prop: string,
1039
1048
  value: string,
1040
1049
  opts: {
@@ -1045,7 +1054,7 @@ function fieldRow(
1045
1054
  onCommit: (value: string | undefined) => void;
1046
1055
  placeholder?: string;
1047
1056
  },
1048
- ): { row: StyleRowView; actions: RowActions } {
1057
+ ): ProjectedRow {
1049
1058
  const key = `${ctx.coord}|${prop}${opts.child ? "|child" : ""}`;
1050
1059
  const row = blankRow(key, prop, "field");
1051
1060
  const actions: RowActions = {};
@@ -1123,8 +1132,8 @@ function fieldRow(
1123
1132
  break;
1124
1133
  }
1125
1134
  case "number-unit": {
1126
- const units = (entry.$units ?? []) as string[];
1127
- const keywords = (entry.$keywords ?? []) as string[];
1135
+ const units = entry.$units ?? [];
1136
+ const keywords = entry.$keywords ?? [];
1128
1137
  const match = UNIT_RE.exec(value);
1129
1138
  const isKeyword = !match && value !== "" && keywords.includes(value);
1130
1139
  const currentUnit = isKeyword ? (units[0] ?? "") : (match?.[2] ?? units[0] ?? "");
@@ -1136,12 +1145,14 @@ function fieldRow(
1136
1145
  row.choicesHint = `Unit for ${row.label.toLowerCase()}`;
1137
1146
  actions.choices = [
1138
1147
  ...units.map((u) => ({ checked: u === currentUnit, label: u, value: u })),
1139
- ...keywords.map((kw, index) => ({
1140
- checked: kw === value,
1141
- label: kw,
1142
- value: kw,
1143
- ...(index === 0 && units.length > 0 ? { divider: true } : {}),
1144
- })),
1148
+ ...keywords.map((kw, index) => {
1149
+ const choice: StyleChoice = { checked: kw === value, label: kw, value: kw };
1150
+ // The rule between the units and the keywords, drawn only when there are units above.
1151
+ if (index === 0 && units.length > 0) {
1152
+ choice.divider = true;
1153
+ }
1154
+ return choice;
1155
+ }),
1145
1156
  ];
1146
1157
  actions.choose = (chosen) => {
1147
1158
  if (keywords.includes(chosen)) {
@@ -1172,16 +1183,16 @@ function fieldRow(
1172
1183
  row.widget = "number";
1173
1184
  row.min = entry.minimum === undefined ? "" : String(entry.minimum);
1174
1185
  row.max = entry.maximum === undefined ? "" : String(entry.maximum);
1175
- row.step = typeof entry.maximum === "number" && entry.maximum <= 1 ? "0.1" : "";
1186
+ row.step = entry.maximum !== undefined && entry.maximum <= 1 ? "0.1" : "";
1176
1187
  actions.commit = commitLiteral;
1177
1188
  break;
1178
1189
  }
1179
1190
  case "select":
1180
1191
  case "combobox": {
1181
1192
  const options = Array.isArray(entry.enum)
1182
- ? (entry.enum as string[])
1193
+ ? entry.enum
1183
1194
  : Array.isArray(entry.examples)
1184
- ? (entry.examples as string[])
1195
+ ? entry.examples
1185
1196
  : [];
1186
1197
  // A keyword row is the combobox contract exactly: the field IS the value, and the rows
1187
1198
  // Under it are the values worth offering. `allows-custom-value` is what makes an enum a
@@ -1238,17 +1249,13 @@ function unitDisplayValue(
1238
1249
 
1239
1250
  /** A unit field's placeholder is the inherited NUMBER — "500", not "500px". */
1240
1251
  function unitPlaceholder(placeholder: string): string {
1241
- return UNIT_RE.exec(placeholder)?.[1] ?? placeholder ?? "0";
1252
+ return UNIT_RE.exec(placeholder)?.[1] ?? placeholder;
1242
1253
  }
1243
1254
 
1244
1255
  /** A shorthand header row and, when it is expanded, the longhand children under it. */
1245
- function shorthandRows(
1246
- ctx: EditorCtx,
1247
- entry: CssPropertyEntry,
1248
- shortProp: string,
1249
- ): { row: StyleRowView; actions: RowActions }[] {
1250
- const longhands = getLonghands(shortProp) as CssLonghand[];
1251
- const style = ctx.activeStyle as Record<string, unknown>;
1256
+ function shorthandRows(ctx: EditorCtx, entry: CssMetaEntry, shortProp: string): ProjectedRow[] {
1257
+ const longhands = getLonghands(shortProp);
1258
+ const style = ctx.activeStyle;
1252
1259
  const shortVal = style[shortProp];
1253
1260
  const hasLonghands = longhands.some((l) => style[l.name] !== undefined);
1254
1261
  const isExpanded = ctx.tab.session.ui.styleShorthands[shortProp] ?? hasLonghands;
@@ -1284,7 +1291,7 @@ function shorthandRows(
1284
1291
  row.widget = "shorthand";
1285
1292
  row.hasLabel = true;
1286
1293
  row.label = propLabel(entry, shortProp);
1287
- row.value = typeof shortVal === "string" ? shortVal : "";
1294
+ row.value = isString(shortVal) ? shortVal : "";
1288
1295
  row.placeholder = shorthandPlaceholder(shortVal, longhands, style, inherited, shortProp);
1289
1296
  row.expanded = isExpanded;
1290
1297
  row.expandIcon = isExpanded ? "caret-down" : "caret-right";
@@ -1307,14 +1314,14 @@ function shorthandRows(
1307
1314
  }
1308
1315
  };
1309
1316
 
1310
- const out = [{ actions, row }];
1317
+ const out: ProjectedRow[] = [{ actions, row }];
1311
1318
  if (!isExpanded) {
1312
1319
  return out;
1313
1320
  }
1314
1321
 
1315
1322
  const isBorderSide = entry.$shorthandType === "border-side";
1316
1323
  const expanded =
1317
- typeof shortVal === "string" && shortVal
1324
+ isString(shortVal) && shortVal
1318
1325
  ? isBorderSide
1319
1326
  ? expandBorderSide(shortVal)
1320
1327
  : expandShorthand(shortVal, longhands.length)
@@ -1325,9 +1332,11 @@ function shorthandRows(
1325
1332
  const recompress = (idx: number, val: string) =>
1326
1333
  writeShorthand(
1327
1334
  compress(
1335
+ /* As text: the compressors join and compare strings, and a number a document holds reads
1336
+ exactly as the template literal they build would have printed it. */
1328
1337
  longhands.map((l, i) =>
1329
- i === idx ? val : (style[l.name] ?? (expanded ? expanded[i] : emptyVal)),
1330
- ) as string[],
1338
+ String(i === idx ? val : (style[l.name] ?? (expanded ? expanded[i] : emptyVal))),
1339
+ ),
1331
1340
  ),
1332
1341
  );
1333
1342
 
@@ -1350,9 +1359,9 @@ function shorthandRows(
1350
1359
 
1351
1360
  /** The shorthand header's placeholder: the longhands it would compress, or what shows through. */
1352
1361
  function shorthandPlaceholder(
1353
- shortVal: unknown,
1362
+ shortVal: JxStyle[string],
1354
1363
  longhands: CssLonghand[],
1355
- style: Record<string, unknown>,
1364
+ style: JxStyle,
1356
1365
  inherited: Record<string, string | number>,
1357
1366
  shortProp: string,
1358
1367
  ): string {
@@ -1394,7 +1403,9 @@ function buildView(canvasMode: () => string): StylePanelView {
1394
1403
  }
1395
1404
  if (canvasMode() === "stylebook" && shell.stylebook.selection) {
1396
1405
  const node = tab.doc.document;
1397
- if (!node) {
1406
+ /* Checked, not trusted: a tab's document is whatever the file parsed to, asserted rather than
1407
+ verified where it is loaded (`files/files.ts`), and Stylebook reads its root style next. */
1408
+ if (!isNodeObject(node)) {
1398
1409
  return emptyView("Open a page to style what you click.", true);
1399
1410
  }
1400
1411
  // No separate `Styling: <h1>` header. It said the same thing the Target Line's scope chip says,
@@ -1408,7 +1419,7 @@ function buildView(canvasMode: () => string): StylePanelView {
1408
1419
  if (!selected) {
1409
1420
  return emptyView(clickAnythingTo("style it"), false);
1410
1421
  }
1411
- const node = getNodeAtPath(tab.doc.document, selected) as JxMutableNode | undefined;
1422
+ const node = getNodeAtPath(tab.doc.document, selected);
1412
1423
  if (!node) {
1413
1424
  return emptyView(staleSelectionMessage(), false);
1414
1425
  }
@@ -1431,17 +1442,15 @@ function buildEditor(
1431
1442
  opts: { effectiveStyle?: JxStyle | undefined; stylebookSelector?: string | null },
1432
1443
  ): StylePanelView {
1433
1444
  const { effectiveStyle, stylebookSelector = null } = opts;
1445
+ /* SAFETY: `buildView` calls this with a primary selection it has checked, or in Stylebook, where
1446
+ `selectStylebookTag` parks the selection on the root path `[[]]` rather than emptying it. */
1434
1447
  const sel = primarySelection(tab.session.selection) as JxPath;
1435
1448
  /** The document's OWN style — what a commit writes into. */
1436
1449
  const ownRootStyle: JxStyle = node.style || {};
1437
1450
  const style = effectiveStyle ?? ownRootStyle;
1438
1451
  // Signals seeding the ${} template default when a style value escalates (no $ref in JxStyle).
1439
1452
  const templateSignals = Object.entries(tab.doc.document.state || {})
1440
- .filter(
1441
- ([, d]) =>
1442
- !(d as Record<string, unknown>)?.$handler &&
1443
- (d as Record<string, unknown>)?.$prototype !== "Function",
1444
- )
1453
+ .filter(([, d]) => !(isJsonObject(d) && d.$handler) && !isFunctionDef(d))
1445
1454
  .map(([defName]) => defName);
1446
1455
  const { sizeBreakpoints } = parseMediaEntries(getEffectiveMedia(tab.doc.document.$media));
1447
1456
  const mediaNames = sizeBreakpoints.map((bp) => bp.name);
@@ -1455,7 +1464,7 @@ function buildEditor(
1455
1464
  const forcedScheme = tab.session.ui.previewColorScheme;
1456
1465
  const schemeLayer =
1457
1466
  mediaTab === null && (forcedScheme === "light" || forcedScheme === "dark")
1458
- ? (Object.entries(getEffectiveMedia(tab.doc.document.$media) ?? {}).find(
1467
+ ? (Object.entries(getEffectiveMedia(tab.doc.document.$media)).find(
1459
1468
  ([, q]) => schemeOfQuery(String(q)) === forcedScheme,
1460
1469
  )?.[0] ?? null)
1461
1470
  : null;
@@ -1463,9 +1472,7 @@ function buildEditor(
1463
1472
  const editMedia = mediaTab && mediaNames.length > 0 ? mediaTab : schemeLayer;
1464
1473
 
1465
1474
  // ── The selector axis — the one this tab owns ──────────────────────────────
1466
- const contextStyle = editMedia
1467
- ? (style[`@${editMedia}`] as Record<string, unknown>) || {}
1468
- : style;
1475
+ const contextStyle = editMedia ? (getNestedStyle(style, `@${editMedia}`) ?? {}) : style;
1469
1476
  const declaredSelectors = new Set(Object.keys(contextStyle).filter((s) => isNestedSelector(s)));
1470
1477
  /* Element-aware, not the flat global list: `:popover-open` on a `<p>` is a rule that can never
1471
1478
  match, and a menu that offers unmatchable states is a menu people stop reading. What the
@@ -1500,8 +1507,7 @@ function buildEditor(
1500
1507
 
1501
1508
  // ── The Target Line (§6.2) ─────────────────────────────────────────────────
1502
1509
  const stylebookTag = stylebookTagOf(stylebookSelector);
1503
- const elementLabel =
1504
- stylebookTag ?? (typeof node.tagName === "string" ? node.tagName : "element");
1510
+ const elementLabel = stylebookTag ?? (isString(node.tagName) ? node.tagName : "element");
1505
1511
  setTargetLine({
1506
1512
  segments: targetSegments(elementLabel, mediaTab, mediaNames.length > 0, schemeLayer),
1507
1513
  selector: {
@@ -1584,7 +1590,7 @@ function buildEditor(
1584
1590
  contextMutate(target, activeSelector, editMedia)(t, prop, val);
1585
1591
  }
1586
1592
  };
1587
- const commitStyle = (prop: string, val?: string | Record<string, unknown> | undefined) =>
1593
+ const commitStyle = (prop: string, val?: string | JxStyle | undefined) =>
1588
1594
  transactDoc(activeTab.value, (t) => mutateTargets(t, prop, val));
1589
1595
 
1590
1596
  // ── Compute inherited style, and NAME THE DONOR ────────────────────────────
@@ -1595,9 +1601,9 @@ function buildEditor(
1595
1601
  if (schemeLayer) {
1596
1602
  const baseBlock = activeSelector ? (getNestedStyle(style, activeSelector) ?? {}) : style;
1597
1603
  for (const [p, v] of Object.entries(baseBlock)) {
1598
- if (typeof v !== "object") {
1604
+ if (isDeclarationValue(v)) {
1599
1605
  inheritedInfo[p] = {
1600
- value: (v as string | number) ?? "",
1606
+ value: v ?? "",
1601
1607
  donor: "Base",
1602
1608
  jump: () => updateUi(activeTab.value, "previewColorScheme", "auto"),
1603
1609
  };
@@ -1625,8 +1631,10 @@ function buildEditor(
1625
1631
  inherited: inheritedInfo,
1626
1632
  signals: templateSignals,
1627
1633
  clear: (prop: string) => commitStyle(prop),
1628
- ...(mixedProps.size > 0 ? { mixed: { count: targets.length, props: mixedProps } } : {}),
1629
1634
  };
1635
+ if (mixedProps.size > 0) {
1636
+ provCtx.mixed = { count: targets.length, props: mixedProps };
1637
+ }
1630
1638
 
1631
1639
  // Auto-open sections that have properties. Written back only when it actually moves, so the
1632
1640
  // Effect this runs inside settles after one extra pass rather than chasing its own write.
@@ -1663,7 +1671,7 @@ function buildEditor(
1663
1671
  }
1664
1672
 
1665
1673
  /** Register a projected row's verbs, and hand back the row. */
1666
- function register(entry: { row: StyleRowView; actions: RowActions }): StyleRowView {
1674
+ function register(entry: ProjectedRow): StyleRowView {
1667
1675
  _rows.set(entry.row.key, entry.actions);
1668
1676
  return entry.row;
1669
1677
  }
@@ -1672,18 +1680,18 @@ function register(entry: { row: StyleRowView; actions: RowActions }): StyleRowVi
1672
1680
  function propertySections(ctx: EditorCtx): StyleSectionView[] {
1673
1681
  const { activeStyle, tab } = ctx;
1674
1682
  const isFiltering = ctx.filter.length > 0;
1675
- const sectionProps: Record<string, { prop: string; entry: CssPropertyEntry }[]> = {};
1683
+ const sectionProps: Record<string, CssPropertyItem[]> = {};
1676
1684
  for (const sec of cssMeta.$sections) {
1677
1685
  sectionProps[sec.key] = [];
1678
1686
  }
1679
- for (const [prop, entry] of Object.entries(cssMeta.$defs) as [string, CssPropertyEntry][]) {
1680
- if (typeof entry.$shorthand === "string") {
1687
+ for (const [prop, entry] of Object.entries(cssDefs)) {
1688
+ if (isString(entry.$shorthand)) {
1681
1689
  continue;
1682
1690
  }
1683
- sectionProps[(entry.$section as string) || "other"]!.push({ entry, prop });
1691
+ sectionProps[entry.$section || "other"]!.push({ entry, prop });
1684
1692
  }
1685
1693
  for (const sec of cssMeta.$sections) {
1686
- sectionProps[sec.key]!.sort((a, b) => (a.entry.$order as number) - (b.entry.$order as number));
1694
+ sectionProps[sec.key]!.sort((a, b) => a.entry.$order - b.entry.$order);
1687
1695
  }
1688
1696
 
1689
1697
  const out: StyleSectionView[] = [];
@@ -1698,7 +1706,7 @@ function propertySections(ctx: EditorCtx): StyleSectionView[] {
1698
1706
  }
1699
1707
  return (
1700
1708
  inferInputType(entry) === "shorthand" &&
1701
- (getLonghands(prop) as CssLonghand[]).some((l) => activeStyle[l.name] !== undefined)
1709
+ getLonghands(prop).some((l) => activeStyle[l.name] !== undefined)
1702
1710
  );
1703
1711
  });
1704
1712
  const clearSection = () => {
@@ -1708,7 +1716,7 @@ function propertySections(ctx: EditorCtx): StyleSectionView[] {
1708
1716
  ctx.mutate(t, prop);
1709
1717
  }
1710
1718
  if (inferInputType(entry) === "shorthand") {
1711
- for (const l of getLonghands(prop) as CssLonghand[]) {
1719
+ for (const l of getLonghands(prop)) {
1712
1720
  ctx.mutate(t, l.name);
1713
1721
  }
1714
1722
  }
@@ -1738,11 +1746,7 @@ function propertySections(ctx: EditorCtx): StyleSectionView[] {
1738
1746
  }
1739
1747
 
1740
1748
  /** One section's rows: every property that is set, or whose `$show` condition passes. */
1741
- function sectionRows(
1742
- ctx: EditorCtx,
1743
- entries: { prop: string; entry: CssPropertyEntry }[],
1744
- grid: boolean,
1745
- ): StyleRowView[] {
1749
+ function sectionRows(ctx: EditorCtx, entries: CssPropertyItem[], grid: boolean): StyleRowView[] {
1746
1750
  const { activeStyle } = ctx;
1747
1751
  const rows: StyleRowView[] = [];
1748
1752
  for (const { prop, entry } of entries) {
@@ -1760,7 +1764,7 @@ function sectionRows(
1760
1764
  }
1761
1765
  }
1762
1766
  if (type === "shorthand") {
1763
- const longhands = getLonghands(prop) as CssLonghand[];
1767
+ const longhands = getLonghands(prop);
1764
1768
  const hasAny = hasVal || longhands.some((l) => activeStyle[l.name] !== undefined);
1765
1769
  if (!hasAny && !condMet) {
1766
1770
  continue;
@@ -1771,7 +1775,7 @@ function sectionRows(
1771
1775
  continue;
1772
1776
  }
1773
1777
  const inherited = ctx.inheritedStyle[prop];
1774
- const built = fieldRow(ctx, entry, prop, (val as string) ?? "", {
1778
+ const built = fieldRow(ctx, entry, prop, String(val ?? ""), {
1775
1779
  onCommit: (newVal) => ctx.commit(prop, newVal || undefined),
1776
1780
  placeholder: !hasVal && inherited !== undefined ? String(inherited) : "",
1777
1781
  provenance: provenanceOf(prop, ctx.provCtx),
@@ -1823,7 +1827,7 @@ function sectionShell(
1823
1827
  function nestedSection(ctx: EditorCtx): StyleSectionView[] {
1824
1828
  const nestedRules: string[] = [];
1825
1829
  for (const [prop, val] of Object.entries(ctx.activeStyle)) {
1826
- if (val !== null && typeof val === "object" && !Array.isArray(val) && !prop.startsWith("@")) {
1830
+ if (isNestedStyle(val) && !prop.startsWith("@")) {
1827
1831
  nestedRules.push(prop);
1828
1832
  }
1829
1833
  }
@@ -1880,9 +1884,9 @@ function nestedSection(ctx: EditorCtx): StyleSectionView[] {
1880
1884
  function customSection(ctx: EditorCtx): StyleSectionView[] {
1881
1885
  const otherProps: string[] = [];
1882
1886
  for (const prop of Object.keys(ctx.activeStyle)) {
1883
- if (!(cssMeta.$defs as Record<string, unknown>)[prop]) {
1884
- const val = ctx.activeStyle[prop];
1885
- if (val !== null && typeof val === "object") {
1887
+ if (!cssDefs[prop]) {
1888
+ // Arrays and blocks are skipped; a `null` a document holds is listed, as it always was.
1889
+ if (isNonNullObject(ctx.activeStyle[prop])) {
1886
1890
  continue;
1887
1891
  }
1888
1892
  otherProps.push(prop);
@@ -1901,7 +1905,7 @@ function customSection(ctx: EditorCtx): StyleSectionView[] {
1901
1905
  const cssInitialMap = getCssInitialMap();
1902
1906
  /** One target's OWN block at this coordinate — what a write that preserves values reads first. */
1903
1907
  const targetStyleOf = (t: Tab, target: JxPath): JxStyle => {
1904
- const targetNode = getNodeAtPath(t.doc.document, target) as JxMutableNode | undefined;
1908
+ const targetNode = getNodeAtPath(t.doc.document, target);
1905
1909
  return resolveContextStyle(targetNode?.style ?? {}, ctx.activeSelector, ctx.editMedia);
1906
1910
  };
1907
1911
  for (const prop of otherProps) {
@@ -1979,14 +1983,16 @@ function closeRowMenu(): void {
1979
1983
  menu?.close();
1980
1984
  }
1981
1985
 
1982
- /** Open a list under the control that asked for it. One menu at a time, the kit's. */
1986
+ /**
1987
+ * Open a list under the control that asked for it. One menu at a time, the kit's. `trigger` is null
1988
+ * when the surface handed over something that is not an element, which opens nothing.
1989
+ */
1983
1990
  function openRowMenu(
1984
- anchor: unknown,
1991
+ trigger: HTMLElement | null,
1985
1992
  label: string,
1986
1993
  choices: StyleChoice[],
1987
1994
  run: (value: string) => void,
1988
1995
  ): void {
1989
- const trigger = anchor instanceof HTMLElement ? anchor : null;
1990
1996
  closeRowMenu();
1991
1997
  if (!trigger || choices.length === 0) {
1992
1998
  return;
@@ -2000,16 +2006,19 @@ function openRowMenu(
2000
2006
  },
2001
2007
  opener: trigger,
2002
2008
  region: MENU_REGION,
2003
- rows: choices.map((choice) => ({
2004
- ...(choice.checked === undefined
2005
- ? {}
2006
- : { checked: (choice.checked ? "true" : "false") as "true" | "false" }),
2007
- destructive: false,
2008
- disabled: false,
2009
- dividerAbove: choice.divider === true,
2010
- id: choice.value,
2011
- title: choice.label,
2012
- })),
2009
+ rows: choices.map((choice) => {
2010
+ const row: MenuRowProjection = {
2011
+ destructive: false,
2012
+ disabled: false,
2013
+ dividerAbove: choice.divider === true,
2014
+ id: choice.value,
2015
+ title: choice.label,
2016
+ };
2017
+ if (choice.checked !== undefined) {
2018
+ row.checked = choice.checked ? "true" : "false";
2019
+ }
2020
+ return row;
2021
+ }),
2013
2022
  run: (value) => run(value),
2014
2023
  });
2015
2024
  }
@@ -2058,7 +2067,9 @@ const ACTIONS: StylePanelActions = {
2058
2067
  return;
2059
2068
  }
2060
2069
  const { choose } = actions;
2061
- openRowMenu(anchor, "Values", actions.choices, (value) => choose(value));
2070
+ openRowMenu(anchor instanceof HTMLElement ? anchor : null, "Values", actions.choices, (value) =>
2071
+ choose(value),
2072
+ );
2062
2073
  },
2063
2074
  pickSource: (key, anchor) => {
2064
2075
  const source = rowActions(key)?.source;
@@ -2066,7 +2077,7 @@ const ACTIONS: StylePanelActions = {
2066
2077
  return;
2067
2078
  }
2068
2079
  openRowMenu(
2069
- anchor,
2080
+ anchor instanceof HTMLElement ? anchor : null,
2070
2081
  "Value source",
2071
2082
  source.caps.map((mode) => ({
2072
2083
  checked: mode === source.mode,
@@ -2074,8 +2085,8 @@ const ACTIONS: StylePanelActions = {
2074
2085
  value: mode,
2075
2086
  })),
2076
2087
  (chosen) => {
2077
- const next = chosen as SlotMode;
2078
- if (next === source.mode) {
2088
+ const next = source.caps.find((mode) => mode === chosen);
2089
+ if (next === undefined || next === source.mode) {
2079
2090
  return;
2080
2091
  }
2081
2092
  source.onChange(
@@ -2083,7 +2094,7 @@ const ACTIONS: StylePanelActions = {
2083
2094
  source.fieldKey,
2084
2095
  source.mode,
2085
2096
  next,
2086
- cloneValue(source.value as JsonValue | undefined),
2097
+ cloneValue(source.value),
2087
2098
  slotModeSeed(next, { stateDefs: source.stateDefs }),
2088
2099
  ),
2089
2100
  );
@@ -2236,35 +2247,37 @@ export function styleCommands(): AnyCommand[] {
2236
2247
  group: "8_style",
2237
2248
  requires: "an element selection",
2238
2249
  when: styleTabOpen,
2239
- run: (_commandCtx, args) => {
2240
- const { selector } = args as { selector?: unknown };
2241
- if (selector === null) {
2242
- const tab = activeTab.value;
2243
- if (tab) {
2244
- tab.session.ui.activeSelector = null;
2245
- }
2246
- return;
2247
- }
2248
- const value = stringArg("style.setSelector", args, "selector");
2249
- if (!isNestedSelector(value)) {
2250
- throw new RangeError(
2251
- `command "style.setSelector" argument "selector": "${value}" is not a nested ` +
2252
- `selector — it must start with ":", ".", "&" or "["`,
2253
- );
2254
- }
2255
- const tab = activeTab.value;
2256
- if (!tab) {
2257
- throw new RangeError(`command "style.setSelector" needs an open document`);
2258
- }
2259
- tab.session.ui.activeSelector = value;
2260
- },
2250
+ run: (_commandCtx, args) => setSelector(args),
2261
2251
  title: "Set Style Selector",
2262
2252
  },
2263
2253
  ];
2264
2254
  }
2265
2255
 
2256
+ /** `style.setSelector`: edit a nested selector, or the base context for `selector: null`. */
2257
+ function setSelector(args: CommandArgValues): void {
2258
+ if (args.selector === null) {
2259
+ const tab = activeTab.value;
2260
+ if (tab) {
2261
+ tab.session.ui.activeSelector = null;
2262
+ }
2263
+ return;
2264
+ }
2265
+ const value = stringArg("style.setSelector", args, "selector");
2266
+ if (!isNestedSelector(value)) {
2267
+ throw new RangeError(
2268
+ `command "style.setSelector" argument "selector": "${value}" is not a nested ` +
2269
+ `selector — it must start with ":", ".", "&" or "["`,
2270
+ );
2271
+ }
2272
+ const tab = activeTab.value;
2273
+ if (!tab) {
2274
+ throw new RangeError(`command "style.setSelector" needs an open document`);
2275
+ }
2276
+ tab.session.ui.activeSelector = value;
2277
+ }
2278
+
2266
2279
  /** The `selector` property's schema: a nested selector, or `null` for the base context. */
2267
- function pathlessSelectorProperty(): object {
2280
+ function pathlessSelectorProperty(): PropertySchema {
2268
2281
  return {
2269
2282
  description:
2270
2283
  'A nested selector such as ":hover", ".child" or "[open]". null edits the base context.',