@jxsuite/studio 6.1.0 → 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 (645) hide show
  1. package/dist/chunks/2020-f2gkpy8d.js +4 -0
  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-gybvv7a6.js → index-zpjmzqfk.js} +3 -3
  32. package/dist/chunks/{javascript-rnvdp3k4.js → javascript-m54mnwp6.js} +3 -3
  33. package/dist/chunks/jsonMode-w15cmrcm.js +13 -0
  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-5g0ag7c0.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-pxg4ap1e.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-qbj760tx.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-anyrgzvb.js → studio-90pq9km1.js} +4 -4
  77. package/dist/chunks/studio-924p1tm1.js +6 -0
  78. package/dist/chunks/{studio-v2bvmbjj.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-txjjzrsm.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-0x3jyb8r.js → studio-tq0rtfwj.js} +4 -4
  111. package/dist/chunks/{studio-cpxkgjrj.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-y64hrt8p.js +12 -0
  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-sf49ee9x.js +14 -0
  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 +98 -98
  140. package/dist/workers/ts.worker.js +3 -3
  141. package/package.json +11 -11
  142. package/src/about/about-modal.ts +4 -3
  143. package/src/account/action-flow.ts +45 -38
  144. package/src/account-status.ts +7 -2
  145. package/src/browse/library-layouts.ts +8 -5
  146. package/src/browse/library-model.ts +18 -6
  147. package/src/browse/library-pane.ts +37 -21
  148. package/src/browse/library-preview.ts +14 -9
  149. package/src/browse/library-source.ts +1 -1
  150. package/src/canvas/asset-refs.ts +24 -9
  151. package/src/canvas/asset-resolve.ts +19 -23
  152. package/src/canvas/canvas-helpers.ts +7 -6
  153. package/src/canvas/canvas-live-render.ts +44 -53
  154. package/src/canvas/canvas-patcher.ts +30 -30
  155. package/src/canvas/canvas-perf.ts +11 -5
  156. package/src/canvas/canvas-render.ts +66 -42
  157. package/src/canvas/canvas-surface.ts +5 -3
  158. package/src/canvas/canvas-utils.ts +163 -64
  159. package/src/canvas/dialog-path.ts +9 -4
  160. package/src/canvas/dialog-state.ts +1 -2
  161. package/src/canvas/diff-marks.ts +9 -16
  162. package/src/canvas/diff-toolbar.ts +8 -7
  163. package/src/canvas/edit-width-drag.ts +4 -1
  164. package/src/canvas/edit-width.ts +1 -3
  165. package/src/canvas/iframe-channel.ts +27 -13
  166. package/src/canvas/iframe-drop.ts +13 -12
  167. package/src/canvas/iframe-editable-root.ts +29 -16
  168. package/src/canvas/iframe-entry.ts +80 -63
  169. package/src/canvas/iframe-eval.ts +73 -49
  170. package/src/canvas/iframe-host.ts +203 -118
  171. package/src/canvas/iframe-inline-edit.ts +69 -32
  172. package/src/canvas/iframe-insert.ts +5 -4
  173. package/src/canvas/iframe-interaction.ts +37 -26
  174. package/src/canvas/iframe-overlay.ts +1 -1
  175. package/src/canvas/iframe-patch.ts +18 -15
  176. package/src/canvas/iframe-position.ts +12 -6
  177. package/src/canvas/iframe-protocol.ts +15 -16
  178. package/src/canvas/iframe-render.ts +41 -36
  179. package/src/canvas/iframe-slash.ts +24 -11
  180. package/src/canvas/iframe-subtree.ts +6 -2
  181. package/src/canvas/path-mapping.ts +19 -12
  182. package/src/canvas/popover-path.ts +7 -3
  183. package/src/canvas/popover-state.ts +1 -2
  184. package/src/canvas/serialize-scope.ts +13 -5
  185. package/src/canvas/stage-scrollbar.ts +2 -1
  186. package/src/canvas/surface-registry.ts +21 -3
  187. package/src/collab/collab-commands.ts +7 -2
  188. package/src/collab/collab-session.ts +84 -45
  189. package/src/collab/collab-state.ts +4 -3
  190. package/src/collab/monaco-binding.ts +16 -13
  191. package/src/collab/monaco-cursors.ts +5 -2
  192. package/src/commands/budget.ts +3 -3
  193. package/src/commands/command-args.ts +103 -71
  194. package/src/commands/context.ts +22 -20
  195. package/src/commands/defaults.ts +4 -1
  196. package/src/commands/keymap.ts +47 -43
  197. package/src/commands/levels.ts +3 -3
  198. package/src/commands/live-context.ts +10 -8
  199. package/src/commands/reference.ts +2 -1
  200. package/src/commands/registry.ts +12 -10
  201. package/src/commands/run-reported.ts +15 -10
  202. package/src/component-props.ts +46 -30
  203. package/src/content/collection-match.ts +5 -4
  204. package/src/content/draft-state.ts +4 -3
  205. package/src/content/entry-commands.ts +3 -2
  206. package/src/content/entry-editor.ts +29 -17
  207. package/src/content/entry-fields.ts +12 -7
  208. package/src/content/entry-model.ts +21 -12
  209. package/src/editor/canvas-context-menu.ts +1 -2
  210. package/src/editor/canvas-slash-bridge.ts +3 -6
  211. package/src/editor/content-slice.ts +16 -14
  212. package/src/editor/context-menu.ts +35 -22
  213. package/src/editor/convert-targets.ts +30 -35
  214. package/src/editor/convert-to-component.ts +11 -5
  215. package/src/editor/convert-to-repeater.ts +34 -22
  216. package/src/editor/editable-tags.ts +17 -2
  217. package/src/editor/file-drop-action.ts +21 -22
  218. package/src/editor/inline-edit-apply.ts +35 -17
  219. package/src/editor/inline-edit.ts +66 -50
  220. package/src/editor/inline-format.ts +59 -56
  221. package/src/editor/inline-link.ts +40 -15
  222. package/src/editor/insert-zone-action.ts +1 -1
  223. package/src/editor/merge-tags.ts +58 -19
  224. package/src/editor/repeater-scope.ts +39 -28
  225. package/src/editor/shortcuts.ts +87 -47
  226. package/src/editor/slash-menu.ts +1 -6
  227. package/src/files/asset-versions.ts +4 -4
  228. package/src/files/components.ts +15 -10
  229. package/src/files/elements.ts +5 -6
  230. package/src/files/file-ops.ts +77 -36
  231. package/src/files/files.ts +90 -95
  232. package/src/files/media-meta.ts +2 -1
  233. package/src/files/media-paths.ts +6 -1
  234. package/src/files/serialize-document.ts +2 -2
  235. package/src/format/constraints.ts +1 -1
  236. package/src/format/convert-file.ts +27 -13
  237. package/src/format/format-choices.ts +2 -1
  238. package/src/format/format-host.ts +76 -50
  239. package/src/github/github-auth-status.ts +27 -3
  240. package/src/github/github-auth.ts +19 -9
  241. package/src/github/github-publish.ts +24 -20
  242. package/src/grid/cell-editors.ts +46 -31
  243. package/src/grid/cell-popovers.ts +5 -3
  244. package/src/grid/edit-buffer.ts +10 -9
  245. package/src/grid/grid-controller.ts +20 -22
  246. package/src/grid/grid-layout.ts +21 -5
  247. package/src/grid/grid-open.ts +3 -6
  248. package/src/grid/grid-panel.ts +11 -4
  249. package/src/grid/grid-source.ts +8 -8
  250. package/src/grid/grid-view.ts +14 -17
  251. package/src/grid/redirects-grid.ts +14 -5
  252. package/src/grid/redirects.ts +16 -14
  253. package/src/grid/schema-columns.ts +25 -19
  254. package/src/grid/sources/connector-source.ts +47 -20
  255. package/src/grid/sources/content-source.ts +25 -24
  256. package/src/grid/sources/csv-file-source.ts +2 -2
  257. package/src/grid/tabulator-tables.d.ts +55 -34
  258. package/src/hosting/stage.ts +7 -7
  259. package/src/i18n/i18n-commands.ts +1 -2
  260. package/src/media/media-pane.ts +4 -1
  261. package/src/new-project/add-repo-modal.ts +20 -19
  262. package/src/new-project/import-tab.ts +10 -14
  263. package/src/new-project/location-fields.ts +2 -1
  264. package/src/new-project/new-project-modal.ts +12 -9
  265. package/src/packages/jxsuite-update.ts +4 -3
  266. package/src/packages/pull-package-sync.ts +26 -25
  267. package/src/page-params.ts +97 -68
  268. package/src/panels/activity-panel.ts +17 -9
  269. package/src/panels/ai-chat/attached-context.ts +13 -7
  270. package/src/panels/ai-chat/chat-view.ts +80 -73
  271. package/src/panels/ai-chat/composer.ts +30 -33
  272. package/src/panels/ai-chat/sessions-view.ts +1 -2
  273. package/src/panels/ai-panel.ts +23 -20
  274. package/src/panels/block-action-bar.ts +46 -35
  275. package/src/panels/bottom-dock.ts +5 -3
  276. package/src/panels/canvas-dnd-bridge.ts +31 -24
  277. package/src/panels/data-explorer.ts +62 -25
  278. package/src/panels/data-grid.ts +14 -11
  279. package/src/panels/dnd.ts +108 -118
  280. package/src/panels/editors.ts +55 -22
  281. package/src/panels/elements-panel.ts +10 -3
  282. package/src/panels/events-panel.ts +84 -69
  283. package/src/panels/formula-workspace.ts +76 -51
  284. package/src/panels/frontmatter-fields.ts +24 -19
  285. package/src/panels/frontmatter-panel.ts +44 -34
  286. package/src/panels/git-diff-open.ts +4 -4
  287. package/src/panels/git-panel.ts +49 -61
  288. package/src/panels/head-panel.ts +131 -111
  289. package/src/panels/i18n-panel.ts +10 -3
  290. package/src/panels/imports-panel.ts +23 -47
  291. package/src/panels/jump-bar.ts +48 -29
  292. package/src/panels/layers-panel.ts +97 -74
  293. package/src/panels/left-panel.ts +3 -3
  294. package/src/panels/pane-context.ts +31 -22
  295. package/src/panels/pane-grid.ts +49 -20
  296. package/src/panels/panel-registry.ts +22 -37
  297. package/src/panels/properties-panel.ts +175 -121
  298. package/src/panels/quick-search.ts +53 -31
  299. package/src/panels/right-panel.ts +3 -3
  300. package/src/panels/seo-modal.ts +13 -17
  301. package/src/panels/settings-menu.ts +15 -15
  302. package/src/panels/signals-panel.ts +153 -131
  303. package/src/panels/statement-editor.ts +230 -140
  304. package/src/panels/style-panel.ts +254 -216
  305. package/src/panels/style-utils.ts +114 -51
  306. package/src/panels/stylebook-doc.ts +57 -54
  307. package/src/panels/stylebook-layers-panel.ts +9 -9
  308. package/src/panels/stylebook-panel.ts +13 -17
  309. package/src/panels/tab-drop.ts +27 -11
  310. package/src/panels/tab-strip.ts +36 -26
  311. package/src/platform-errors.ts +57 -43
  312. package/src/platform.ts +58 -30
  313. package/src/platforms/cloud.ts +345 -175
  314. package/src/platforms/default-platform.ts +10 -4
  315. package/src/platforms/devserver.ts +174 -127
  316. package/src/platforms/read-batcher.ts +16 -8
  317. package/src/project-list.ts +2 -1
  318. package/src/publish/deploy-checklist.ts +8 -2
  319. package/src/publish/pages-service.ts +28 -13
  320. package/src/publish/publish-commands.ts +5 -4
  321. package/src/publish/publish-panel.ts +10 -6
  322. package/src/recent-projects.ts +15 -8
  323. package/src/resize-edges.ts +26 -36
  324. package/src/services/a11y-report.ts +26 -19
  325. package/src/services/agent-seed.ts +23 -11
  326. package/src/services/ai-ask.ts +9 -12
  327. package/src/services/ai-command-tools.ts +68 -62
  328. package/src/services/ai-import-tools.ts +80 -67
  329. package/src/services/ai-models.ts +17 -13
  330. package/src/services/ai-project-tools.ts +68 -32
  331. package/src/services/ai-session-store.ts +19 -10
  332. package/src/services/ai-system-prompt.ts +46 -20
  333. package/src/services/ai-tools.ts +71 -27
  334. package/src/services/ai-write-report.ts +20 -12
  335. package/src/services/ai-writes.ts +4 -4
  336. package/src/services/automation.ts +58 -31
  337. package/src/services/cem-export.ts +149 -58
  338. package/src/services/clock.ts +6 -1
  339. package/src/services/code-services.ts +37 -31
  340. package/src/services/context-manager.ts +14 -10
  341. package/src/services/context-resolver.ts +20 -8
  342. package/src/services/data-service.ts +19 -15
  343. package/src/services/directory-picker.ts +18 -12
  344. package/src/services/document-assistant.ts +39 -19
  345. package/src/services/gated-registry.ts +10 -6
  346. package/src/services/harness/chat-reducer.ts +23 -1
  347. package/src/services/import-client.ts +91 -38
  348. package/src/services/import-seed.ts +8 -2
  349. package/src/services/jx-validate.ts +28 -31
  350. package/src/services/live-preview.ts +0 -0
  351. package/src/services/live-surfaces.ts +6 -3
  352. package/src/services/model-uri.ts +10 -2
  353. package/src/services/module-preloads.ts +12 -8
  354. package/src/services/monaco-buffer.ts +43 -24
  355. package/src/services/monaco-lazy.ts +7 -8
  356. package/src/services/monaco-setup.ts +23 -16
  357. package/src/services/notify.ts +44 -17
  358. package/src/services/popover-report.ts +25 -10
  359. package/src/services/preview-eval.ts +20 -8
  360. package/src/services/profile.ts +13 -6
  361. package/src/services/project-path.ts +13 -10
  362. package/src/services/references.ts +0 -0
  363. package/src/services/render-critic.ts +8 -7
  364. package/src/services/settings/kernel.ts +13 -4
  365. package/src/services/settings/write-queue.ts +9 -9
  366. package/src/services/token-lint.ts +12 -10
  367. package/src/services/tool-executor.ts +17 -5
  368. package/src/services/tool-outcomes.ts +44 -24
  369. package/src/services/trusted-types.ts +24 -7
  370. package/src/settings/contexts-section.ts +11 -8
  371. package/src/settings/contributed-section.ts +79 -60
  372. package/src/settings/css-vars-editor.ts +37 -16
  373. package/src/settings/defs-editor.ts +38 -35
  374. package/src/settings/extension-rows.ts +16 -8
  375. package/src/settings/extension-sections.ts +23 -13
  376. package/src/settings/extensions-section.ts +4 -1
  377. package/src/settings/general-settings.ts +11 -8
  378. package/src/settings/locales-section.ts +20 -12
  379. package/src/settings/preferences-dialog.ts +9 -6
  380. package/src/settings/preferences-keymap.ts +7 -5
  381. package/src/settings/preferences-sections.ts +11 -5
  382. package/src/settings/project-sections.ts +1 -1
  383. package/src/settings/schema-field-ui.ts +9 -0
  384. package/src/settings/settings-document.ts +5 -5
  385. package/src/shell/tree.ts +6 -5
  386. package/src/shell.ts +104 -81
  387. package/src/site-context.ts +37 -22
  388. package/src/state.ts +41 -49
  389. package/src/store.ts +54 -34
  390. package/src/studio.ts +39 -77
  391. package/src/style/live-preview.ts +1 -1
  392. package/src/style/project-styles.ts +6 -7
  393. package/src/style/token-ref.ts +5 -4
  394. package/src/surfaces/about.ts +18 -19
  395. package/src/surfaces/add-repo.ts +17 -19
  396. package/src/surfaces/ai-chat.ts +31 -36
  397. package/src/surfaces/ai-credentials-form.ts +6 -16
  398. package/src/surfaces/ai-managed-connect.ts +3 -4
  399. package/src/surfaces/ai-model-picker.ts +3 -5
  400. package/src/surfaces/block-action-bar.ts +22 -11
  401. package/src/surfaces/boot-failure.ts +4 -5
  402. package/src/surfaces/bottom-dock.ts +9 -18
  403. package/src/surfaces/canvas-stage.ts +12 -11
  404. package/src/surfaces/cf-account-picker.ts +17 -18
  405. package/src/surfaces/commandbar.ts +14 -9
  406. package/src/surfaces/convert-repeater.ts +17 -19
  407. package/src/surfaces/data-actions.ts +9 -7
  408. package/src/surfaces/dialog.ts +26 -28
  409. package/src/surfaces/diff-toolbar.ts +9 -7
  410. package/src/surfaces/doc-header.ts +10 -19
  411. package/src/surfaces/empty-state.ts +7 -10
  412. package/src/surfaces/entry-editor.ts +14 -11
  413. package/src/surfaces/expression-editor.ts +11 -25
  414. package/src/surfaces/files-panel.ts +27 -14
  415. package/src/surfaces/formula-palette.ts +4 -5
  416. package/src/surfaces/git-panel.ts +7 -15
  417. package/src/surfaces/github-auth.ts +16 -20
  418. package/src/surfaces/github-publish.ts +17 -22
  419. package/src/surfaces/grid-cell.ts +30 -31
  420. package/src/surfaces/grid-open.ts +10 -14
  421. package/src/surfaces/grid-panel.ts +13 -9
  422. package/src/surfaces/grid-replace.ts +13 -13
  423. package/src/surfaces/grid-views.ts +15 -18
  424. package/src/surfaces/inspector-dock.ts +13 -12
  425. package/src/surfaces/jump-bar.ts +5 -6
  426. package/src/surfaces/jxsuite-update.ts +16 -22
  427. package/src/surfaces/library-pane.ts +25 -20
  428. package/src/surfaces/logic-panel.ts +17 -30
  429. package/src/surfaces/logic-workspace.ts +13 -15
  430. package/src/surfaces/media-browser.ts +11 -10
  431. package/src/surfaces/media-field.ts +4 -5
  432. package/src/surfaces/media-pane.ts +8 -7
  433. package/src/surfaces/menu.ts +29 -23
  434. package/src/surfaces/navigator-dock.ts +11 -12
  435. package/src/surfaces/new-project.ts +27 -26
  436. package/src/surfaces/pane-context.ts +20 -25
  437. package/src/surfaces/pane-grid.ts +15 -10
  438. package/src/surfaces/panel-activity.ts +5 -6
  439. package/src/surfaces/panel-data.ts +3 -4
  440. package/src/surfaces/panel-deploy-checklist.ts +5 -6
  441. package/src/surfaces/panel-elements.ts +5 -7
  442. package/src/surfaces/panel-i18n.ts +5 -7
  443. package/src/surfaces/panel-imports.ts +6 -13
  444. package/src/surfaces/panel-outline.ts +25 -19
  445. package/src/surfaces/panel-page.ts +11 -18
  446. package/src/surfaces/panel-problems.ts +5 -7
  447. package/src/surfaces/panel-signals.ts +20 -34
  448. package/src/surfaces/panel-stylebook-layers.ts +5 -8
  449. package/src/surfaces/preferences.ts +21 -11
  450. package/src/surfaces/progress-modal.ts +15 -9
  451. package/src/surfaces/properties-panel.json +1 -1
  452. package/src/surfaces/properties-panel.ts +20 -42
  453. package/src/surfaces/publish.ts +18 -18
  454. package/src/surfaces/push-plan.ts +7 -8
  455. package/src/surfaces/rail.json +126 -73
  456. package/src/surfaces/rail.ts +38 -12
  457. package/src/surfaces/reference-field.ts +11 -15
  458. package/src/surfaces/schema-builder.ts +16 -22
  459. package/src/surfaces/schema-form.ts +15 -25
  460. package/src/surfaces/secret-field.ts +9 -6
  461. package/src/surfaces/seo.ts +21 -15
  462. package/src/surfaces/settings-contexts.ts +9 -7
  463. package/src/surfaces/settings-contributed.ts +14 -10
  464. package/src/surfaces/settings-css-vars.ts +5 -12
  465. package/src/surfaces/settings-defs.json +2 -2
  466. package/src/surfaces/settings-defs.ts +17 -21
  467. package/src/surfaces/settings-deploy.ts +8 -15
  468. package/src/surfaces/settings-extensions.ts +5 -6
  469. package/src/surfaces/settings-head.ts +33 -39
  470. package/src/surfaces/settings-locales.ts +8 -20
  471. package/src/surfaces/settings-overview.ts +5 -6
  472. package/src/surfaces/settings-packages.ts +8 -7
  473. package/src/surfaces/settings-pane.ts +9 -18
  474. package/src/surfaces/settings-rawjson.ts +5 -6
  475. package/src/surfaces/shell.ts +1 -2
  476. package/src/surfaces/slash-menu.ts +19 -9
  477. package/src/surfaces/statements.ts +13 -18
  478. package/src/surfaces/statusbar.ts +17 -14
  479. package/src/surfaces/style-panel.ts +18 -31
  480. package/src/surfaces/stylebook-chrome.ts +3 -5
  481. package/src/surfaces/tab-strip.ts +14 -13
  482. package/src/surfaces/target-line.ts +6 -7
  483. package/src/surfaces/welcome.ts +5 -7
  484. package/src/tabs/patch-ops.ts +4 -3
  485. package/src/tabs/project-config.ts +32 -21
  486. package/src/tabs/selection.ts +36 -8
  487. package/src/tabs/tab.ts +52 -41
  488. package/src/tabs/transact.ts +98 -53
  489. package/src/tabs/typing.ts +31 -0
  490. package/src/types.ts +32 -17
  491. package/src/ui/ai-credentials-form.ts +10 -13
  492. package/src/ui/ai-model-picker.ts +3 -3
  493. package/src/ui/cf-account-picker.ts +7 -9
  494. package/src/ui/color-selector.ts +9 -5
  495. package/src/ui/dynamic-slot.ts +7 -8
  496. package/src/ui/expression-editor.ts +112 -105
  497. package/src/ui/form-controls.ts +57 -48
  498. package/src/ui/formula-catalog.ts +239 -230
  499. package/src/ui/formula-chips.ts +33 -18
  500. package/src/ui/layers.ts +44 -31
  501. package/src/ui/media-picker.ts +18 -9
  502. package/src/ui/popover-surface.ts +20 -12
  503. package/src/ui/progress-modal.ts +13 -8
  504. package/src/ui/regions.ts +7 -6
  505. package/src/ui/schema-form.ts +113 -70
  506. package/src/ui/surface.ts +71 -8
  507. package/src/ui/timing.ts +9 -0
  508. package/src/ui/value-source.ts +44 -31
  509. package/src/ui/virtual-window.ts +3 -2
  510. package/src/utils/base64.ts +3 -1
  511. package/src/utils/edit-display.ts +103 -74
  512. package/src/utils/element-selectors.ts +3 -2
  513. package/src/utils/google-fonts.ts +15 -14
  514. package/src/utils/inherited-style.ts +18 -20
  515. package/src/utils/link-target.ts +1 -1
  516. package/src/utils/preview-format.ts +25 -3
  517. package/src/utils/strip-events.ts +49 -22
  518. package/src/utils/studio-utils.ts +29 -14
  519. package/src/view.ts +2 -3
  520. package/src/workspace/pane-derive.ts +10 -12
  521. package/src/workspace/session.ts +53 -43
  522. package/src/workspace/workspace.ts +47 -36
  523. package/dist/chunks/2020-nkxms0ay.js +0 -4
  524. package/dist/chunks/adapter-m948ab5f.js +0 -4
  525. package/dist/chunks/cf-account-picker-n8hr38v7.js +0 -4
  526. package/dist/chunks/cf-settings-356xdtne.js +0 -4
  527. package/dist/chunks/color-area-jqhna8zy.js +0 -4
  528. package/dist/chunks/color-field-embq5ctg.js +0 -4
  529. package/dist/chunks/color-fpezvnkt.js +0 -4
  530. package/dist/chunks/color-slider-1qmw7wqy.js +0 -4
  531. package/dist/chunks/combobox-773hdzyj.js +0 -4
  532. package/dist/chunks/components-yk3t844e.js +0 -4
  533. package/dist/chunks/content-source-bawmyc09.js +0 -4
  534. package/dist/chunks/convert-to-repeater-7wns8kf1.js +0 -4
  535. package/dist/chunks/dialog-mtm9fq5t.js +0 -4
  536. package/dist/chunks/editor-6sc8nacq.js +0 -4
  537. package/dist/chunks/entry-commands-8ay9fh81.js +0 -4
  538. package/dist/chunks/extension-sections-na7rj3nr.js +0 -4
  539. package/dist/chunks/file-ops-gye0qw56.js +0 -4
  540. package/dist/chunks/files-dmc2xvcq.js +0 -4
  541. package/dist/chunks/format-host-t76b53f8.js +0 -4
  542. package/dist/chunks/fs-events-cg4tpz06.js +0 -4
  543. package/dist/chunks/git-panel-yef7bshy.js +0 -4
  544. package/dist/chunks/grid-view-7tt4bxw1.js +0 -51
  545. package/dist/chunks/icons-qbh8akb1.js +0 -4
  546. package/dist/chunks/iframe-entry-f7mqjwpm.js +0 -5
  547. package/dist/chunks/iframe-entry-haw7jkvz.js +0 -5
  548. package/dist/chunks/iframe-entry-z6qrytv3.js +0 -5
  549. package/dist/chunks/iframe-host-tmz30gfp.js +0 -4
  550. package/dist/chunks/index-nag78a9h.js +0 -4
  551. package/dist/chunks/jsonMode-zhwsprqk.js +0 -13
  552. package/dist/chunks/jx-validate-3gbp5nkn.js +0 -4
  553. package/dist/chunks/layers-fvz99859.js +0 -4
  554. package/dist/chunks/layers-panel-gchmbf9p.js +0 -4
  555. package/dist/chunks/listbox-s8fkmnq8.js +0 -4
  556. package/dist/chunks/lit-html-9agdspkt.js +0 -4
  557. package/dist/chunks/media-picker-rhjvyy00.js +0 -4
  558. package/dist/chunks/menu-2e4y9j7b.js +0 -4
  559. package/dist/chunks/menu-37d07z9m.js +0 -4
  560. package/dist/chunks/monaco-binding-2jnhjjvr.js +0 -4
  561. package/dist/chunks/monaco-buffer-rp21359g.js +0 -4
  562. package/dist/chunks/monaco-setup-vhh7gayr.js +0 -4
  563. package/dist/chunks/notify-h3www560.js +0 -4
  564. package/dist/chunks/number-field-gw7sq8rn.js +0 -4
  565. package/dist/chunks/parse-897fvqah.js +0 -4
  566. package/dist/chunks/platform-hz34wcd7.js +0 -4
  567. package/dist/chunks/popover-8htra4er.js +0 -4
  568. package/dist/chunks/publish-panel-exsmjzyj.js +0 -4
  569. package/dist/chunks/quick-search-pgaqeh0m.js +0 -4
  570. package/dist/chunks/references-2cgdxnb8.js +0 -4
  571. package/dist/chunks/right-panel-jf856w5w.js +0 -4
  572. package/dist/chunks/section-registry-jnf8r5gy.js +0 -4
  573. package/dist/chunks/select-n3v7gwas.js +0 -4
  574. package/dist/chunks/split-pff8f3ze.js +0 -4
  575. package/dist/chunks/studio-0f55h9ye.js +0 -5
  576. package/dist/chunks/studio-0sqdhnew.js +0 -12
  577. package/dist/chunks/studio-19x1c75w.js +0 -5
  578. package/dist/chunks/studio-2sqcndak.js +0 -5
  579. package/dist/chunks/studio-3aey7dd3.js +0 -5
  580. package/dist/chunks/studio-3bpdkq2s.js +0 -5
  581. package/dist/chunks/studio-47y6fbpf.js +0 -5
  582. package/dist/chunks/studio-4m3826v2.js +0 -5
  583. package/dist/chunks/studio-4nx2dj1g.js +0 -4
  584. package/dist/chunks/studio-4xv5s47r.js +0 -9
  585. package/dist/chunks/studio-5g4t8v6w.js +0 -5
  586. package/dist/chunks/studio-5py436sf.js +0 -5
  587. package/dist/chunks/studio-77kt594t.js +0 -5
  588. package/dist/chunks/studio-8f87n763.js +0 -10
  589. package/dist/chunks/studio-8mb00b84.js +0 -5
  590. package/dist/chunks/studio-8p51gq2h.js +0 -6
  591. package/dist/chunks/studio-954ds834.js +0 -5
  592. package/dist/chunks/studio-9ft45c83.js +0 -9
  593. package/dist/chunks/studio-a2138x5k.js +0 -7
  594. package/dist/chunks/studio-a9yzstqj.js +0 -5
  595. package/dist/chunks/studio-acdksxx6.js +0 -5
  596. package/dist/chunks/studio-ar8jcf2y.js +0 -5
  597. package/dist/chunks/studio-bmn4mn9j.js +0 -7
  598. package/dist/chunks/studio-e7xxa0sj.js +0 -5
  599. package/dist/chunks/studio-ecaz25vk.js +0 -5
  600. package/dist/chunks/studio-ery42sqr.js +0 -4
  601. package/dist/chunks/studio-fzdywr6p.js +0 -9
  602. package/dist/chunks/studio-g5fmf0c1.js +0 -5
  603. package/dist/chunks/studio-g7nse7zb.js +0 -5
  604. package/dist/chunks/studio-geq1ea6k.js +0 -14
  605. package/dist/chunks/studio-ghkb32r8.js +0 -5
  606. package/dist/chunks/studio-ghrhy2y7.js +0 -5
  607. package/dist/chunks/studio-grawhey1.js +0 -5
  608. package/dist/chunks/studio-h0xgswew.js +0 -5
  609. package/dist/chunks/studio-h9c4p2tx.js +0 -5
  610. package/dist/chunks/studio-jyzj5cxs.js +0 -5
  611. package/dist/chunks/studio-mgx8yhnz.js +0 -64
  612. package/dist/chunks/studio-mpb04y1d.js +0 -6
  613. package/dist/chunks/studio-n17mty64.js +0 -5
  614. package/dist/chunks/studio-nwh2eqsk.js +0 -6
  615. package/dist/chunks/studio-q1h883bt.js +0 -16
  616. package/dist/chunks/studio-q4tv82h7.js +0 -9
  617. package/dist/chunks/studio-qpqa7vk3.js +0 -5
  618. package/dist/chunks/studio-qqz8e5gk.js +0 -5
  619. package/dist/chunks/studio-qvasen3f.js +0 -5
  620. package/dist/chunks/studio-rggdb3hm.js +0 -5
  621. package/dist/chunks/studio-sghy936h.js +0 -4
  622. package/dist/chunks/studio-skprc6ef.js +0 -5
  623. package/dist/chunks/studio-tt2nactf.js +0 -5
  624. package/dist/chunks/studio-tyasmc39.js +0 -5
  625. package/dist/chunks/studio-vk9ezads.js +0 -5
  626. package/dist/chunks/studio-vzacdfsc.js +0 -4
  627. package/dist/chunks/studio-w4c0xmrw.js +0 -5
  628. package/dist/chunks/studio-wfjqhhbk.js +0 -5
  629. package/dist/chunks/studio-wgh5kxpj.js +0 -5
  630. package/dist/chunks/studio-xasj0796.js +0 -5
  631. package/dist/chunks/studio-y78s8r2n.js +0 -5
  632. package/dist/chunks/studio-z8w183sh.js +0 -5
  633. package/dist/chunks/studio-zscd9ty4.js +0 -928
  634. package/dist/chunks/studio-zxp13sss.js +0 -7
  635. package/dist/chunks/studio-zygnmm01.js +0 -5
  636. package/dist/chunks/swatch-group-8qh9a3bm.js +0 -4
  637. package/dist/chunks/tabs-jwthg40w.js +0 -4
  638. package/dist/chunks/textfield-spy3hc77.js +0 -4
  639. package/dist/chunks/toast-4d35zd64.js +0 -4
  640. package/dist/chunks/toast-host-8sjmfed3.js +0 -4
  641. package/dist/chunks/tooltip-fd6g9xy3.js +0 -4
  642. package/dist/chunks/tree-h19v1jfz.js +0 -4
  643. package/dist/chunks/tsMode-gr0m4dzr.js +0 -14
  644. package/dist/chunks/workspace-m15qtcqp.js +0 -4
  645. package/dist/chunks/ws-client-nf61frr1.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,38 +1472,58 @@ 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
1472
1479
  element already DECLARES and the active selector are unioned in after, so nothing an author has
1473
1480
  written can drop out of it. */
1474
- const selectorOptions = [
1475
- ...new Set([
1476
- ...selectorsForNode(node),
1477
- ...declaredSelectors,
1478
- ...(activeSelector ? [activeSelector] : []),
1479
- ]),
1480
- ];
1481
+ /* In Project Styles the coordinate is a TAG PATH (`a`, `table th`) and its states nest under it
1482
+ (`a :hover`). The menu offered the ROOT's states instead, and picking one replaced the tag, so
1483
+ `:hover` styled the document element and a project had no way to write `a:hover` at all. Here
1484
+ the states are the tag's own, spelled as the path they write. */
1485
+ const stylebookPath =
1486
+ stylebookSelector && isTagPath(stylebookSelector) ? stylebookSelector : null;
1487
+ const stylebookLeaf = stylebookPath?.split(" ").at(-1) ?? null;
1488
+ const stylebookDeclared = stylebookPath
1489
+ ? Object.keys(resolveNestedTagStyle(style, stylebookPath)).filter((k) => isNestedSelector(k))
1490
+ : [];
1491
+ const selectorOptions = stylebookPath
1492
+ ? [
1493
+ ...new Set([
1494
+ ...[...selectorsForNode({ tagName: stylebookLeaf ?? "div" }), ...stylebookDeclared].map(
1495
+ (state) => `${stylebookPath} ${state}`,
1496
+ ),
1497
+ ...(activeSelector && activeSelector !== stylebookPath ? [activeSelector] : []),
1498
+ ]),
1499
+ ]
1500
+ : [
1501
+ ...new Set([
1502
+ ...selectorsForNode(node),
1503
+ ...declaredSelectors,
1504
+ ...(activeSelector ? [activeSelector] : []),
1505
+ ]),
1506
+ ];
1481
1507
 
1482
1508
  // ── The Target Line (§6.2) ─────────────────────────────────────────────────
1483
1509
  const stylebookTag = stylebookTagOf(stylebookSelector);
1484
- const elementLabel =
1485
- stylebookTag ?? (typeof node.tagName === "string" ? node.tagName : "element");
1510
+ const elementLabel = stylebookTag ?? (isString(node.tagName) ? node.tagName : "element");
1486
1511
  setTargetLine({
1487
1512
  segments: targetSegments(elementLabel, mediaTab, mediaNames.length > 0, schemeLayer),
1488
1513
  selector: {
1489
- value: activeSelector,
1514
+ // The bare tag path IS the base rule in Project Styles, so it reads as the base entry.
1515
+ value: stylebookPath && activeSelector === stylebookPath ? null : activeSelector,
1490
1516
  options: selectorOptions,
1491
- declared: declaredSelectors,
1517
+ declared: stylebookPath
1518
+ ? new Set(stylebookDeclared.map((state) => `${stylebookPath} ${state}`))
1519
+ : declaredSelectors,
1492
1520
  onSelect: (value) => {
1493
1521
  const current = activeTab.value;
1494
1522
  if (!current) {
1495
1523
  return;
1496
1524
  }
1497
- current.session.ui.activeSelector = value;
1525
+ // Clearing the state in Project Styles returns to the tag, never to the document root.
1526
+ current.session.ui.activeSelector = value ?? stylebookPath;
1498
1527
  /* §6.2's own rule: a control that selects a rendering context has to change the rendering,
1499
1528
  or it is a control over a label. `:hover` gets away with not doing this because you can
1500
1529
  hover the element; `:popover-open` cannot, because a closed popover is not on the screen
@@ -1523,7 +1552,9 @@ function buildEditor(
1523
1552
  isNestedSelector(v.trim()) ? "" : 'A selector must start with ":", ".", "&" or "[".',
1524
1553
  }).then((value) => {
1525
1554
  if (value && activeTab.value) {
1526
- activeTab.value.session.ui.activeSelector = value.trim();
1555
+ activeTab.value.session.ui.activeSelector = stylebookPath
1556
+ ? `${stylebookPath} ${value.trim()}`
1557
+ : value.trim();
1527
1558
  }
1528
1559
  });
1529
1560
  },
@@ -1559,7 +1590,7 @@ function buildEditor(
1559
1590
  contextMutate(target, activeSelector, editMedia)(t, prop, val);
1560
1591
  }
1561
1592
  };
1562
- const commitStyle = (prop: string, val?: string | Record<string, unknown> | undefined) =>
1593
+ const commitStyle = (prop: string, val?: string | JxStyle | undefined) =>
1563
1594
  transactDoc(activeTab.value, (t) => mutateTargets(t, prop, val));
1564
1595
 
1565
1596
  // ── Compute inherited style, and NAME THE DONOR ────────────────────────────
@@ -1570,9 +1601,9 @@ function buildEditor(
1570
1601
  if (schemeLayer) {
1571
1602
  const baseBlock = activeSelector ? (getNestedStyle(style, activeSelector) ?? {}) : style;
1572
1603
  for (const [p, v] of Object.entries(baseBlock)) {
1573
- if (typeof v !== "object") {
1604
+ if (isDeclarationValue(v)) {
1574
1605
  inheritedInfo[p] = {
1575
- value: (v as string | number) ?? "",
1606
+ value: v ?? "",
1576
1607
  donor: "Base",
1577
1608
  jump: () => updateUi(activeTab.value, "previewColorScheme", "auto"),
1578
1609
  };
@@ -1600,8 +1631,10 @@ function buildEditor(
1600
1631
  inherited: inheritedInfo,
1601
1632
  signals: templateSignals,
1602
1633
  clear: (prop: string) => commitStyle(prop),
1603
- ...(mixedProps.size > 0 ? { mixed: { count: targets.length, props: mixedProps } } : {}),
1604
1634
  };
1635
+ if (mixedProps.size > 0) {
1636
+ provCtx.mixed = { count: targets.length, props: mixedProps };
1637
+ }
1605
1638
 
1606
1639
  // Auto-open sections that have properties. Written back only when it actually moves, so the
1607
1640
  // Effect this runs inside settles after one extra pass rather than chasing its own write.
@@ -1638,7 +1671,7 @@ function buildEditor(
1638
1671
  }
1639
1672
 
1640
1673
  /** Register a projected row's verbs, and hand back the row. */
1641
- function register(entry: { row: StyleRowView; actions: RowActions }): StyleRowView {
1674
+ function register(entry: ProjectedRow): StyleRowView {
1642
1675
  _rows.set(entry.row.key, entry.actions);
1643
1676
  return entry.row;
1644
1677
  }
@@ -1647,18 +1680,18 @@ function register(entry: { row: StyleRowView; actions: RowActions }): StyleRowVi
1647
1680
  function propertySections(ctx: EditorCtx): StyleSectionView[] {
1648
1681
  const { activeStyle, tab } = ctx;
1649
1682
  const isFiltering = ctx.filter.length > 0;
1650
- const sectionProps: Record<string, { prop: string; entry: CssPropertyEntry }[]> = {};
1683
+ const sectionProps: Record<string, CssPropertyItem[]> = {};
1651
1684
  for (const sec of cssMeta.$sections) {
1652
1685
  sectionProps[sec.key] = [];
1653
1686
  }
1654
- for (const [prop, entry] of Object.entries(cssMeta.$defs) as [string, CssPropertyEntry][]) {
1655
- if (typeof entry.$shorthand === "string") {
1687
+ for (const [prop, entry] of Object.entries(cssDefs)) {
1688
+ if (isString(entry.$shorthand)) {
1656
1689
  continue;
1657
1690
  }
1658
- sectionProps[(entry.$section as string) || "other"]!.push({ entry, prop });
1691
+ sectionProps[entry.$section || "other"]!.push({ entry, prop });
1659
1692
  }
1660
1693
  for (const sec of cssMeta.$sections) {
1661
- 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);
1662
1695
  }
1663
1696
 
1664
1697
  const out: StyleSectionView[] = [];
@@ -1673,7 +1706,7 @@ function propertySections(ctx: EditorCtx): StyleSectionView[] {
1673
1706
  }
1674
1707
  return (
1675
1708
  inferInputType(entry) === "shorthand" &&
1676
- (getLonghands(prop) as CssLonghand[]).some((l) => activeStyle[l.name] !== undefined)
1709
+ getLonghands(prop).some((l) => activeStyle[l.name] !== undefined)
1677
1710
  );
1678
1711
  });
1679
1712
  const clearSection = () => {
@@ -1683,7 +1716,7 @@ function propertySections(ctx: EditorCtx): StyleSectionView[] {
1683
1716
  ctx.mutate(t, prop);
1684
1717
  }
1685
1718
  if (inferInputType(entry) === "shorthand") {
1686
- for (const l of getLonghands(prop) as CssLonghand[]) {
1719
+ for (const l of getLonghands(prop)) {
1687
1720
  ctx.mutate(t, l.name);
1688
1721
  }
1689
1722
  }
@@ -1713,11 +1746,7 @@ function propertySections(ctx: EditorCtx): StyleSectionView[] {
1713
1746
  }
1714
1747
 
1715
1748
  /** One section's rows: every property that is set, or whose `$show` condition passes. */
1716
- function sectionRows(
1717
- ctx: EditorCtx,
1718
- entries: { prop: string; entry: CssPropertyEntry }[],
1719
- grid: boolean,
1720
- ): StyleRowView[] {
1749
+ function sectionRows(ctx: EditorCtx, entries: CssPropertyItem[], grid: boolean): StyleRowView[] {
1721
1750
  const { activeStyle } = ctx;
1722
1751
  const rows: StyleRowView[] = [];
1723
1752
  for (const { prop, entry } of entries) {
@@ -1735,7 +1764,7 @@ function sectionRows(
1735
1764
  }
1736
1765
  }
1737
1766
  if (type === "shorthand") {
1738
- const longhands = getLonghands(prop) as CssLonghand[];
1767
+ const longhands = getLonghands(prop);
1739
1768
  const hasAny = hasVal || longhands.some((l) => activeStyle[l.name] !== undefined);
1740
1769
  if (!hasAny && !condMet) {
1741
1770
  continue;
@@ -1746,7 +1775,7 @@ function sectionRows(
1746
1775
  continue;
1747
1776
  }
1748
1777
  const inherited = ctx.inheritedStyle[prop];
1749
- const built = fieldRow(ctx, entry, prop, (val as string) ?? "", {
1778
+ const built = fieldRow(ctx, entry, prop, String(val ?? ""), {
1750
1779
  onCommit: (newVal) => ctx.commit(prop, newVal || undefined),
1751
1780
  placeholder: !hasVal && inherited !== undefined ? String(inherited) : "",
1752
1781
  provenance: provenanceOf(prop, ctx.provCtx),
@@ -1798,7 +1827,7 @@ function sectionShell(
1798
1827
  function nestedSection(ctx: EditorCtx): StyleSectionView[] {
1799
1828
  const nestedRules: string[] = [];
1800
1829
  for (const [prop, val] of Object.entries(ctx.activeStyle)) {
1801
- if (val !== null && typeof val === "object" && !Array.isArray(val) && !prop.startsWith("@")) {
1830
+ if (isNestedStyle(val) && !prop.startsWith("@")) {
1802
1831
  nestedRules.push(prop);
1803
1832
  }
1804
1833
  }
@@ -1855,9 +1884,9 @@ function nestedSection(ctx: EditorCtx): StyleSectionView[] {
1855
1884
  function customSection(ctx: EditorCtx): StyleSectionView[] {
1856
1885
  const otherProps: string[] = [];
1857
1886
  for (const prop of Object.keys(ctx.activeStyle)) {
1858
- if (!(cssMeta.$defs as Record<string, unknown>)[prop]) {
1859
- const val = ctx.activeStyle[prop];
1860
- 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])) {
1861
1890
  continue;
1862
1891
  }
1863
1892
  otherProps.push(prop);
@@ -1876,7 +1905,7 @@ function customSection(ctx: EditorCtx): StyleSectionView[] {
1876
1905
  const cssInitialMap = getCssInitialMap();
1877
1906
  /** One target's OWN block at this coordinate — what a write that preserves values reads first. */
1878
1907
  const targetStyleOf = (t: Tab, target: JxPath): JxStyle => {
1879
- const targetNode = getNodeAtPath(t.doc.document, target) as JxMutableNode | undefined;
1908
+ const targetNode = getNodeAtPath(t.doc.document, target);
1880
1909
  return resolveContextStyle(targetNode?.style ?? {}, ctx.activeSelector, ctx.editMedia);
1881
1910
  };
1882
1911
  for (const prop of otherProps) {
@@ -1954,14 +1983,16 @@ function closeRowMenu(): void {
1954
1983
  menu?.close();
1955
1984
  }
1956
1985
 
1957
- /** 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
+ */
1958
1990
  function openRowMenu(
1959
- anchor: unknown,
1991
+ trigger: HTMLElement | null,
1960
1992
  label: string,
1961
1993
  choices: StyleChoice[],
1962
1994
  run: (value: string) => void,
1963
1995
  ): void {
1964
- const trigger = anchor instanceof HTMLElement ? anchor : null;
1965
1996
  closeRowMenu();
1966
1997
  if (!trigger || choices.length === 0) {
1967
1998
  return;
@@ -1975,16 +2006,19 @@ function openRowMenu(
1975
2006
  },
1976
2007
  opener: trigger,
1977
2008
  region: MENU_REGION,
1978
- rows: choices.map((choice) => ({
1979
- ...(choice.checked === undefined
1980
- ? {}
1981
- : { checked: (choice.checked ? "true" : "false") as "true" | "false" }),
1982
- destructive: false,
1983
- disabled: false,
1984
- dividerAbove: choice.divider === true,
1985
- id: choice.value,
1986
- title: choice.label,
1987
- })),
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
+ }),
1988
2022
  run: (value) => run(value),
1989
2023
  });
1990
2024
  }
@@ -2033,7 +2067,9 @@ const ACTIONS: StylePanelActions = {
2033
2067
  return;
2034
2068
  }
2035
2069
  const { choose } = actions;
2036
- openRowMenu(anchor, "Values", actions.choices, (value) => choose(value));
2070
+ openRowMenu(anchor instanceof HTMLElement ? anchor : null, "Values", actions.choices, (value) =>
2071
+ choose(value),
2072
+ );
2037
2073
  },
2038
2074
  pickSource: (key, anchor) => {
2039
2075
  const source = rowActions(key)?.source;
@@ -2041,7 +2077,7 @@ const ACTIONS: StylePanelActions = {
2041
2077
  return;
2042
2078
  }
2043
2079
  openRowMenu(
2044
- anchor,
2080
+ anchor instanceof HTMLElement ? anchor : null,
2045
2081
  "Value source",
2046
2082
  source.caps.map((mode) => ({
2047
2083
  checked: mode === source.mode,
@@ -2049,8 +2085,8 @@ const ACTIONS: StylePanelActions = {
2049
2085
  value: mode,
2050
2086
  })),
2051
2087
  (chosen) => {
2052
- const next = chosen as SlotMode;
2053
- if (next === source.mode) {
2088
+ const next = source.caps.find((mode) => mode === chosen);
2089
+ if (next === undefined || next === source.mode) {
2054
2090
  return;
2055
2091
  }
2056
2092
  source.onChange(
@@ -2058,7 +2094,7 @@ const ACTIONS: StylePanelActions = {
2058
2094
  source.fieldKey,
2059
2095
  source.mode,
2060
2096
  next,
2061
- cloneValue(source.value as JsonValue | undefined),
2097
+ cloneValue(source.value),
2062
2098
  slotModeSeed(next, { stateDefs: source.stateDefs }),
2063
2099
  ),
2064
2100
  );
@@ -2211,35 +2247,37 @@ export function styleCommands(): AnyCommand[] {
2211
2247
  group: "8_style",
2212
2248
  requires: "an element selection",
2213
2249
  when: styleTabOpen,
2214
- run: (_commandCtx, args) => {
2215
- const { selector } = args as { selector?: unknown };
2216
- if (selector === null) {
2217
- const tab = activeTab.value;
2218
- if (tab) {
2219
- tab.session.ui.activeSelector = null;
2220
- }
2221
- return;
2222
- }
2223
- const value = stringArg("style.setSelector", args, "selector");
2224
- if (!isNestedSelector(value)) {
2225
- throw new RangeError(
2226
- `command "style.setSelector" argument "selector": "${value}" is not a nested ` +
2227
- `selector — it must start with ":", ".", "&" or "["`,
2228
- );
2229
- }
2230
- const tab = activeTab.value;
2231
- if (!tab) {
2232
- throw new RangeError(`command "style.setSelector" needs an open document`);
2233
- }
2234
- tab.session.ui.activeSelector = value;
2235
- },
2250
+ run: (_commandCtx, args) => setSelector(args),
2236
2251
  title: "Set Style Selector",
2237
2252
  },
2238
2253
  ];
2239
2254
  }
2240
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
+
2241
2279
  /** The `selector` property's schema: a nested selector, or `null` for the base context. */
2242
- function pathlessSelectorProperty(): object {
2280
+ function pathlessSelectorProperty(): PropertySchema {
2243
2281
  return {
2244
2282
  description:
2245
2283
  'A nested selector such as ":hover", ".child" or "[open]". null edits the base context.',