@jxsuite/studio 4.1.0 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (607) hide show
  1. package/README.md +1 -1
  2. package/data/elements-meta.json +5 -15
  3. package/data/html-meta.json +51 -6
  4. package/dist/chunks/{2020-n6tfwnda.js → 2020-g79n01v9.js} +14 -14
  5. package/dist/chunks/action-group-sp6tna8v.js +118 -0
  6. package/dist/chunks/adapter-nj7tnjfn.js +13 -0
  7. package/dist/chunks/cf-account-picker-fyf63pmy.js +495 -0
  8. package/dist/chunks/cf-settings-71bv5zn9.js +20 -0
  9. package/dist/chunks/color-area-v17df7b8.js +154 -0
  10. package/dist/chunks/color-field-anwybnkn.js +208 -0
  11. package/dist/chunks/color-slider-44ez96fc.js +79 -0
  12. package/dist/chunks/color-xgg3kfw7.js +35 -0
  13. package/dist/chunks/combobox-5e7jev23.js +236 -0
  14. package/dist/chunks/components-2w1f35qs.js +18 -0
  15. package/dist/chunks/content-source-3wqeas45.js +46 -0
  16. package/dist/chunks/convert-to-repeater-d0cgnb6d.js +639 -0
  17. package/dist/chunks/dialog-s890b918.js +85 -0
  18. package/dist/chunks/editor-3qhbg51x.js +35 -0
  19. package/dist/chunks/entry-commands-d6g50j7s.js +42 -0
  20. package/dist/chunks/extension-sections-fy7q4fe9.js +651 -0
  21. package/dist/chunks/file-ops-3btj6st0.js +47 -0
  22. package/dist/chunks/files-ef8fgs3w.js +72 -0
  23. package/dist/chunks/format-host-3c33nwr8.js +40 -0
  24. package/dist/chunks/fs-events-13tj6sex.js +18 -0
  25. package/dist/chunks/git-panel-4wp2rn9b.js +49 -0
  26. package/dist/chunks/icons-yy10wk9r.js +402 -0
  27. package/dist/chunks/iframe-entry-15g3n6xv.js +114 -0
  28. package/dist/chunks/iframe-entry-b27vq81n.js +203 -0
  29. package/dist/chunks/iframe-entry-nbyxee1y.js +154 -0
  30. package/dist/chunks/iframe-host-jgesd924.js +115 -0
  31. package/dist/chunks/{index-wcnt3r9g.js → index-mdmyx6s2.js} +11 -11
  32. package/dist/chunks/index-n4j7qxyr.js +109 -0
  33. package/dist/chunks/{javascript-q8kpmfah.js → javascript-xvrfrpkb.js} +8 -9
  34. package/dist/chunks/{jsonMode-dkr040ga.js → jsonMode-tyx2kj6p.js} +526 -527
  35. package/dist/chunks/jx-validate-j0wxk72w.js +15 -0
  36. package/dist/chunks/layers-1nq5evsh.js +29 -0
  37. package/dist/chunks/layers-panel-ar2gh90g.js +40 -0
  38. package/dist/chunks/listbox-6v2ac08j.js +44 -0
  39. package/dist/chunks/lit-html-m244w9nh.js +17 -0
  40. package/dist/chunks/media-picker-2qbetq73.js +33 -0
  41. package/dist/chunks/menu-q99sy9rf.js +285 -0
  42. package/dist/chunks/menu-rf7n72mf.js +14 -0
  43. package/dist/chunks/{monaco-binding-rj6xpx9y.js → monaco-binding-sw6en4dw.js} +12 -15
  44. package/dist/chunks/monaco-buffer-bhy3hb6m.js +24 -0
  45. package/dist/chunks/{monaco-setup-vx9bhp1d.js → monaco-setup-8r62fj6y.js} +27 -37
  46. package/dist/chunks/notify-628t0anh.js +21 -0
  47. package/dist/chunks/number-field-5cndyft8.js +72 -0
  48. package/dist/chunks/parse-0zx14y15.js +10 -0
  49. package/dist/chunks/platform-ts9cg1a4.js +15 -0
  50. package/dist/chunks/popover-47z3ytva.js +25 -0
  51. package/dist/chunks/publish-panel-98f7sr3g.js +27 -0
  52. package/dist/chunks/quick-search-srzz83k2.js +45 -0
  53. package/dist/chunks/references-n553f0e1.js +26 -0
  54. package/dist/chunks/right-panel-rrgx2dn3.js +45 -0
  55. package/dist/chunks/section-registry-5pq32t0q.js +25 -0
  56. package/dist/chunks/select-2r16ez41.js +49 -0
  57. package/dist/chunks/split-g7nm1rgb.js +191 -0
  58. package/dist/chunks/studio-0hqbpbd0.js +62 -0
  59. package/dist/chunks/studio-18avnhjp.js +2430 -0
  60. package/dist/chunks/{studio-kryvsw6j.js → studio-1k325pwf.js} +48 -48
  61. package/dist/chunks/{studio-msd847zn.js → studio-25hkdrkw.js} +357 -86
  62. package/dist/chunks/studio-26d7myd1.js +258 -0
  63. package/dist/chunks/{studio-4r1pv6zf.js → studio-38emantv.js} +22 -5
  64. package/dist/chunks/studio-3gxv60tn.js +422 -0
  65. package/dist/chunks/{studio-fbf92ckf.js → studio-3hxkd92t.js} +275 -163
  66. package/dist/chunks/studio-4m0haseg.js +185 -0
  67. package/dist/chunks/{studio-ynxwkrzd.js → studio-4wbb9ct0.js} +1953 -1785
  68. package/dist/chunks/{studio-1wagb550.js → studio-5523bpzt.js} +11 -11
  69. package/dist/chunks/{studio-x4qkdat5.js → studio-57mf66w2.js} +1749 -2481
  70. package/dist/chunks/studio-5jy81t3s.js +17463 -0
  71. package/dist/chunks/studio-5rt7sqa0.js +1168 -0
  72. package/dist/chunks/studio-5te5tqng.js +81 -0
  73. package/dist/chunks/{studio-p4ngc046.js → studio-6ewqv390.js} +9 -9
  74. package/dist/chunks/{studio-9ab8gqsm.js → studio-6nzaktvy.js} +7 -7
  75. package/dist/chunks/studio-6xa21ej9.js +28 -0
  76. package/dist/chunks/{studio-5ehhackm.js → studio-727xh0e3.js} +19 -19
  77. package/dist/chunks/{studio-ahn3pdgx.js → studio-79y5sz51.js} +52 -38
  78. package/dist/chunks/{studio-gcye7qka.js → studio-7e11jpa9.js} +21 -21
  79. package/dist/chunks/{studio-q7v10cg2.js → studio-7r42c6ca.js} +17 -7
  80. package/dist/chunks/studio-7xptrg0j.js +954 -0
  81. package/dist/chunks/{studio-cft9r4r7.js → studio-81390n6f.js} +22 -22
  82. package/dist/chunks/{studio-q43a5y9h.js → studio-8jn5yhyn.js} +16 -16
  83. package/dist/chunks/studio-8qtrpzr3.js +839 -0
  84. package/dist/chunks/{studio-dg7zyt5x.js → studio-98b3z07n.js} +1 -1
  85. package/dist/chunks/studio-9xxz1q69.js +1356 -0
  86. package/dist/chunks/studio-akbww0j5.js +13615 -0
  87. package/dist/chunks/{studio-x813zp48.js → studio-arfpe389.js} +30 -30
  88. package/dist/chunks/{studio-trxrabqf.js → studio-az3zd49t.js} +475 -283
  89. package/dist/chunks/studio-cpvb2ed1.js +1171 -0
  90. package/dist/chunks/{studio-f3wqjtk5.js → studio-d0300636.js} +5076 -18
  91. package/dist/chunks/{studio-bgxt93ga.js → studio-d055c37e.js} +13 -13
  92. package/dist/chunks/studio-d1ja2x9r.js +362 -0
  93. package/dist/chunks/{studio-b9b6t1dg.js → studio-e1f24eff.js} +5664 -5666
  94. package/dist/chunks/{studio-3bqpr0zh.js → studio-em9nw8eh.js} +299 -110
  95. package/dist/chunks/studio-erdatzvp.js +1190 -0
  96. package/dist/chunks/{studio-g1wdche0.js → studio-esrxgwh6.js} +80 -80
  97. package/dist/chunks/studio-fabxk6r9.js +2811 -0
  98. package/dist/chunks/{studio-3n4ndrzh.js → studio-h4nvnjvz.js} +303 -303
  99. package/dist/chunks/{studio-dm19q43r.js → studio-hhvs4sdg.js} +10 -10
  100. package/dist/chunks/{studio-q9jd1a60.js → studio-kcgff4r0.js} +15 -18
  101. package/dist/chunks/{studio-685s0ytq.js → studio-nqgawfks.js} +572 -572
  102. package/dist/chunks/{studio-xrapxwwc.js → studio-p1dw8wfw.js} +55 -29
  103. package/dist/chunks/studio-pm6hgy7r.js +4763 -0
  104. package/dist/chunks/studio-q0a3yk4n.js +33 -0
  105. package/dist/chunks/{studio-wcy8trfx.js → studio-q3x06vmj.js} +23 -22
  106. package/dist/chunks/studio-qahxvnan.js +111 -0
  107. package/dist/chunks/{studio-53n8bp04.js → studio-r6xmmtfh.js} +1 -1
  108. package/dist/chunks/{studio-2b946z26.js → studio-rkstz940.js} +3658 -6134
  109. package/dist/chunks/{studio-tvgyw9xw.js → studio-rnyf3f8e.js} +83 -250
  110. package/dist/chunks/studio-rpyah2ew.js +2805 -0
  111. package/dist/chunks/studio-rs76k67r.js +12 -0
  112. package/dist/chunks/{studio-jkftxyvz.js → studio-s6z853aq.js} +5858 -2152
  113. package/dist/chunks/{studio-6yw4y3my.js → studio-sgdc5pfc.js} +8 -8
  114. package/dist/chunks/studio-tb3ev01j.js +763 -0
  115. package/dist/chunks/{studio-t5wqwzdh.js → studio-tjjk7jvk.js} +1 -1
  116. package/dist/chunks/studio-vhv5tgxm.js +51 -0
  117. package/dist/chunks/{studio-qjy2z9bv.js → studio-vz68yn9e.js} +32 -137
  118. package/dist/chunks/studio-wqpngpsk.js +174 -0
  119. package/dist/chunks/{studio-b55y6ewx.js → studio-x3rty6tr.js} +58 -58
  120. package/dist/chunks/studio-xsykpvsh.js +8448 -0
  121. package/dist/chunks/{studio-92ab2jqe.js → studio-y9zedyjn.js} +14 -14
  122. package/dist/chunks/studio-yxa81f45.js +12 -0
  123. package/dist/chunks/swatch-group-t0n6h2rn.js +139 -0
  124. package/dist/chunks/tabs-wbp1cdws.js +156 -0
  125. package/dist/chunks/textfield-abk6qyee.js +73 -0
  126. package/dist/chunks/toast-780pvfbv.js +27 -0
  127. package/dist/chunks/toast-host-3sqv46sn.js +116 -0
  128. package/dist/chunks/toolbar-xbh5bhtr.js +194 -0
  129. package/dist/chunks/tooltip-eptb614w.js +369 -0
  130. package/dist/chunks/tree-a73p0pqm.js +367 -0
  131. package/dist/chunks/{tsMode-76kr53q0.js → tsMode-jy4m86rv.js} +83 -85
  132. package/dist/chunks/workspace-wrew8zk4.js +69 -0
  133. package/dist/chunks/{ws-client-6shc5rze.js → ws-client-ny4zc51r.js} +20 -22
  134. package/dist/iframe-entry.js +1780 -667
  135. package/dist/studio.js +52819 -36480
  136. package/dist/workers/editor.worker.js +1040 -1085
  137. package/dist/workers/json.worker.js +1946 -1991
  138. package/dist/workers/ts.worker.js +4960 -5005
  139. package/index.html +1 -0
  140. package/package.json +18 -52
  141. package/src/about/about-modal.ts +72 -123
  142. package/src/browse/library-commands.ts +20 -49
  143. package/src/browse/library-layouts.ts +87 -218
  144. package/src/browse/library-pane.ts +406 -438
  145. package/src/canvas/canvas-helpers.ts +0 -49
  146. package/src/canvas/canvas-live-render.ts +25 -2
  147. package/src/canvas/canvas-render.ts +666 -231
  148. package/src/canvas/canvas-surface.ts +0 -1
  149. package/src/canvas/canvas-utils.ts +310 -117
  150. package/src/canvas/dialog-path.ts +76 -0
  151. package/src/canvas/dialog-state.ts +67 -0
  152. package/src/canvas/diff-marks.ts +252 -0
  153. package/src/canvas/diff-toolbar.ts +349 -0
  154. package/src/canvas/diff-view.ts +123 -0
  155. package/src/canvas/edit-width-drag.ts +187 -0
  156. package/src/canvas/edit-width.ts +150 -0
  157. package/src/canvas/iframe-entry.ts +125 -8
  158. package/src/canvas/iframe-host.ts +271 -15
  159. package/src/canvas/iframe-interaction.ts +133 -13
  160. package/src/canvas/iframe-patch.ts +7 -1
  161. package/src/canvas/iframe-position.ts +5 -6
  162. package/src/canvas/iframe-protocol.ts +106 -5
  163. package/src/canvas/iframe-render.ts +373 -5
  164. package/src/canvas/iframe-slash.ts +2 -2
  165. package/src/canvas/iframe-subtree.ts +5 -8
  166. package/src/canvas/path-mapping.ts +25 -3
  167. package/src/canvas/popover-path.ts +106 -0
  168. package/src/canvas/popover-state.ts +155 -0
  169. package/src/canvas/surface-registry.ts +21 -10
  170. package/src/collab/presence-chips.ts +51 -73
  171. package/src/commands/app-commands.ts +10 -0
  172. package/src/commands/budget.ts +12 -0
  173. package/src/commands/command-args.ts +369 -6
  174. package/src/commands/defaults.ts +44 -14
  175. package/src/commands/live-context.ts +11 -5
  176. package/src/commands/reference.ts +12 -2
  177. package/src/commands/registry.ts +165 -8
  178. package/src/content/entry-commands.ts +16 -23
  179. package/src/content/entry-editor.ts +144 -101
  180. package/src/editor/canvas-slash-bridge.ts +2 -2
  181. package/src/editor/context-menu.ts +49 -200
  182. package/src/editor/convert-to-component.ts +42 -82
  183. package/src/editor/convert-to-repeater.ts +132 -173
  184. package/src/editor/inline-edit.ts +11 -0
  185. package/src/editor/shortcuts.ts +65 -26
  186. package/src/editor/slash-menu.ts +150 -157
  187. package/src/files/file-ops.ts +117 -11
  188. package/src/files/files.ts +943 -660
  189. package/src/files/json-layout.ts +474 -0
  190. package/src/files/serialize-document.ts +8 -1
  191. package/src/format/convert-file.ts +16 -10
  192. package/src/format/format-host.ts +50 -1
  193. package/src/github/github-auth.ts +71 -29
  194. package/src/github/github-publish.ts +57 -65
  195. package/src/grid/cell-editors.ts +46 -4
  196. package/src/grid/cell-popovers.ts +65 -62
  197. package/src/grid/grid-open.ts +86 -64
  198. package/src/grid/grid-panel.ts +390 -469
  199. package/src/grid/redirects-grid.ts +40 -44
  200. package/src/hosting/layout.ts +2 -0
  201. package/src/i18n/i18n-commands.ts +127 -62
  202. package/src/media/media-pane.ts +83 -133
  203. package/src/new-project/add-repo-modal.ts +125 -176
  204. package/src/new-project/import-tab.ts +181 -252
  205. package/src/new-project/location-fields.ts +93 -160
  206. package/src/new-project/new-project-modal.ts +376 -367
  207. package/src/packages/jxsuite-update.ts +76 -18
  208. package/src/panels/activity-panel.ts +137 -104
  209. package/src/panels/ai-chat/chat-markdown.ts +15 -13
  210. package/src/panels/ai-chat/chat-view.ts +307 -472
  211. package/src/panels/ai-chat/composer.ts +175 -163
  212. package/src/panels/ai-chat/sessions-view.ts +31 -62
  213. package/src/panels/ai-panel.ts +210 -185
  214. package/src/panels/block-action-bar.ts +437 -643
  215. package/src/panels/bottom-dock.ts +112 -47
  216. package/src/panels/chat-panel.ts +19 -14
  217. package/src/panels/data-explorer.ts +141 -102
  218. package/src/panels/data-grid.ts +183 -150
  219. package/src/panels/dnd.ts +75 -20
  220. package/src/panels/editors.ts +26 -19
  221. package/src/panels/elements-panel.ts +243 -186
  222. package/src/panels/empty-state.ts +26 -42
  223. package/src/panels/events-panel.ts +950 -729
  224. package/src/panels/formula-workspace.ts +434 -207
  225. package/src/panels/frontmatter-fields.ts +185 -153
  226. package/src/panels/frontmatter-panel.ts +411 -187
  227. package/src/panels/git-diff-open.ts +148 -0
  228. package/src/panels/git-panel.ts +679 -605
  229. package/src/panels/head-panel.ts +617 -435
  230. package/src/panels/i18n-panel.ts +153 -133
  231. package/src/panels/imports-panel.ts +388 -414
  232. package/src/panels/jump-bar.ts +174 -147
  233. package/src/panels/layers-panel.ts +785 -671
  234. package/src/panels/left-panel.ts +166 -94
  235. package/src/panels/navigator-panels.ts +1 -1
  236. package/src/panels/pane-context.ts +529 -677
  237. package/src/panels/pane-grid.ts +265 -241
  238. package/src/panels/panel-registry.ts +15 -4
  239. package/src/panels/problems-panel.ts +177 -99
  240. package/src/panels/properties-panel.ts +1525 -966
  241. package/src/panels/provenance.ts +31 -113
  242. package/src/panels/quick-search.ts +217 -172
  243. package/src/panels/right-panel.ts +135 -169
  244. package/src/panels/seo-modal.ts +346 -167
  245. package/src/panels/settings-menu.ts +109 -646
  246. package/src/panels/settings-pane.ts +102 -86
  247. package/src/panels/signals-panel.ts +1318 -1172
  248. package/src/panels/statement-editor.ts +556 -428
  249. package/src/panels/style-panel.ts +1367 -630
  250. package/src/panels/style-utils.ts +36 -32
  251. package/src/panels/stylebook-doc.ts +30 -73
  252. package/src/panels/stylebook-layers-panel.ts +202 -109
  253. package/src/panels/stylebook-panel.ts +124 -88
  254. package/src/panels/tab-strip.ts +385 -287
  255. package/src/platforms/cloud.ts +106 -16
  256. package/src/platforms/devserver.ts +13 -0
  257. package/src/publish/deploy-checklist.ts +124 -78
  258. package/src/publish/publish-commands.ts +4 -12
  259. package/src/publish/publish-panel.ts +134 -328
  260. package/src/services/a11y-report.ts +100 -43
  261. package/src/services/ai-command-tools.ts +468 -0
  262. package/src/services/ai-session-store.ts +6 -0
  263. package/src/services/ai-system-prompt.ts +110 -18
  264. package/src/services/ai-tools.ts +28 -143
  265. package/src/services/ai-write-report.ts +174 -0
  266. package/src/services/document-assistant.ts +50 -2
  267. package/src/services/idle.ts +16 -14
  268. package/src/services/live-surfaces.ts +237 -0
  269. package/src/services/model-uri.ts +75 -0
  270. package/src/services/notify.ts +63 -6
  271. package/src/services/popover-report.ts +325 -0
  272. package/src/services/surface-registry.ts +56 -0
  273. package/src/services/tool-executor.ts +6 -0
  274. package/src/settings/contexts-section.ts +301 -299
  275. package/src/settings/contributed-section.ts +361 -222
  276. package/src/settings/css-vars-editor.ts +320 -325
  277. package/src/settings/defs-editor.ts +572 -535
  278. package/src/settings/dependencies-editor.ts +79 -107
  279. package/src/settings/extension-commands.ts +379 -0
  280. package/src/settings/extension-rows.ts +113 -0
  281. package/src/settings/extensions-section.ts +287 -0
  282. package/src/settings/general-settings.ts +130 -181
  283. package/src/settings/head-editor.ts +10 -305
  284. package/src/settings/locales-section.ts +146 -196
  285. package/src/settings/preferences-accounts.ts +1 -1
  286. package/src/settings/preferences-dialog.ts +350 -330
  287. package/src/settings/project-sections.ts +108 -181
  288. package/src/settings/schema-field-ui.ts +11 -399
  289. package/src/settings/section-registry.ts +8 -1
  290. package/src/settings/settings-document.ts +31 -29
  291. package/src/shell/tree.ts +42 -87
  292. package/src/shell.ts +48 -36
  293. package/src/studio.ts +90 -67
  294. package/src/style/token-ref.ts +0 -39
  295. package/src/surfaces/about.json +263 -0
  296. package/src/surfaces/about.ts +163 -0
  297. package/src/surfaces/add-repo.json +486 -0
  298. package/src/surfaces/add-repo.ts +236 -0
  299. package/src/surfaces/ai-chat.json +2185 -0
  300. package/src/surfaces/ai-chat.ts +549 -0
  301. package/src/surfaces/ai-credentials-form.json +332 -0
  302. package/src/surfaces/ai-credentials-form.ts +151 -0
  303. package/src/surfaces/ai-managed-connect.json +108 -0
  304. package/src/surfaces/ai-managed-connect.ts +123 -0
  305. package/src/surfaces/ai-model-picker.json +114 -0
  306. package/src/surfaces/ai-model-picker.ts +139 -0
  307. package/src/surfaces/block-action-bar.json +640 -0
  308. package/src/surfaces/block-action-bar.ts +368 -0
  309. package/src/surfaces/bottom-dock.json +154 -0
  310. package/src/surfaces/bottom-dock.ts +125 -0
  311. package/src/surfaces/canvas-stage.json +444 -0
  312. package/src/surfaces/canvas-stage.ts +253 -0
  313. package/src/surfaces/cf-account-picker.json +300 -0
  314. package/src/surfaces/cf-account-picker.ts +214 -0
  315. package/src/surfaces/commandbar.json +984 -0
  316. package/src/{panels/toolbar.ts → surfaces/commandbar.ts} +414 -389
  317. package/src/surfaces/convert-repeater.json +309 -0
  318. package/src/surfaces/convert-repeater.ts +276 -0
  319. package/src/surfaces/data-actions.json +157 -0
  320. package/src/surfaces/data-actions.ts +115 -0
  321. package/src/surfaces/dialog.json +289 -0
  322. package/src/surfaces/dialog.ts +343 -0
  323. package/src/surfaces/diff-toolbar.json +266 -0
  324. package/src/surfaces/diff-toolbar.ts +139 -0
  325. package/src/surfaces/doc-header.json +739 -0
  326. package/src/surfaces/doc-header.ts +223 -0
  327. package/src/surfaces/empty-state.json +152 -0
  328. package/src/surfaces/empty-state.ts +173 -0
  329. package/src/surfaces/entry-editor.json +281 -0
  330. package/src/surfaces/entry-editor.ts +191 -0
  331. package/src/surfaces/expression-editor.json +977 -0
  332. package/src/surfaces/expression-editor.ts +266 -0
  333. package/src/surfaces/files-panel.json +592 -0
  334. package/src/surfaces/files-panel.ts +301 -0
  335. package/src/surfaces/formula-palette.json +364 -0
  336. package/src/surfaces/formula-palette.ts +340 -0
  337. package/src/surfaces/git-panel.json +1822 -0
  338. package/src/surfaces/git-panel.ts +272 -0
  339. package/src/surfaces/github-auth.json +112 -0
  340. package/src/surfaces/github-auth.ts +139 -0
  341. package/src/surfaces/github-publish.json +206 -0
  342. package/src/surfaces/github-publish.ts +169 -0
  343. package/src/surfaces/grid-cell.json +218 -0
  344. package/src/surfaces/grid-cell.ts +120 -0
  345. package/src/surfaces/grid-open.json +147 -0
  346. package/src/surfaces/grid-open.ts +87 -0
  347. package/src/surfaces/grid-panel.json +706 -0
  348. package/src/surfaces/grid-panel.ts +206 -0
  349. package/src/surfaces/grid-replace.json +179 -0
  350. package/src/surfaces/grid-replace.ts +89 -0
  351. package/src/surfaces/grid-views.json +446 -0
  352. package/src/surfaces/grid-views.ts +172 -0
  353. package/src/surfaces/inspector-dock.json +187 -0
  354. package/src/surfaces/inspector-dock.ts +161 -0
  355. package/src/surfaces/jump-bar.json +243 -0
  356. package/src/surfaces/jump-bar.ts +129 -0
  357. package/src/surfaces/jxsuite-update.json +146 -0
  358. package/src/surfaces/jxsuite-update.ts +149 -0
  359. package/src/surfaces/library-pane.json +1509 -0
  360. package/src/surfaces/library-pane.ts +363 -0
  361. package/src/surfaces/logic-panel.json +1949 -0
  362. package/src/surfaces/logic-panel.ts +369 -0
  363. package/src/surfaces/logic-workspace.json +685 -0
  364. package/src/surfaces/logic-workspace.ts +238 -0
  365. package/src/surfaces/media-browser.json +303 -0
  366. package/src/surfaces/media-browser.ts +145 -0
  367. package/src/surfaces/media-field.json +163 -0
  368. package/src/surfaces/media-field.ts +116 -0
  369. package/src/surfaces/media-pane.json +513 -0
  370. package/src/surfaces/media-pane.ts +197 -0
  371. package/src/surfaces/menu.json +232 -0
  372. package/src/surfaces/menu.ts +375 -0
  373. package/src/surfaces/navigator-dock.json +113 -0
  374. package/src/surfaces/navigator-dock.ts +130 -0
  375. package/src/surfaces/new-project.json +1654 -0
  376. package/src/surfaces/new-project.ts +411 -0
  377. package/src/surfaces/palette.json +535 -0
  378. package/src/surfaces/pane-context.json +1497 -0
  379. package/src/surfaces/pane-context.ts +513 -0
  380. package/src/surfaces/pane-grid.json +188 -0
  381. package/src/surfaces/pane-grid.ts +250 -0
  382. package/src/surfaces/panel-activity.json +563 -0
  383. package/src/surfaces/panel-activity.ts +187 -0
  384. package/src/surfaces/panel-data.json +141 -0
  385. package/src/surfaces/panel-data.ts +156 -0
  386. package/src/surfaces/panel-deploy-checklist.json +283 -0
  387. package/src/surfaces/panel-deploy-checklist.ts +175 -0
  388. package/src/surfaces/panel-elements.json +367 -0
  389. package/src/surfaces/panel-elements.ts +175 -0
  390. package/src/surfaces/panel-i18n.json +569 -0
  391. package/src/surfaces/panel-i18n.ts +212 -0
  392. package/src/surfaces/panel-imports.json +780 -0
  393. package/src/surfaces/panel-imports.ts +209 -0
  394. package/src/surfaces/panel-outline.json +734 -0
  395. package/src/surfaces/panel-outline.ts +326 -0
  396. package/src/surfaces/panel-page.json +1017 -0
  397. package/src/surfaces/panel-page.ts +240 -0
  398. package/src/surfaces/panel-problems.json +497 -0
  399. package/src/surfaces/panel-problems.ts +157 -0
  400. package/src/surfaces/panel-signals.json +1973 -0
  401. package/src/surfaces/panel-signals.ts +436 -0
  402. package/src/surfaces/panel-stylebook-layers.json +317 -0
  403. package/src/surfaces/panel-stylebook-layers.ts +155 -0
  404. package/src/surfaces/preferences.json +850 -0
  405. package/src/surfaces/preferences.ts +332 -0
  406. package/src/surfaces/progress-modal.json +110 -0
  407. package/src/surfaces/progress-modal.ts +150 -0
  408. package/src/surfaces/properties-panel.json +1578 -0
  409. package/src/surfaces/properties-panel.ts +337 -0
  410. package/src/surfaces/publish.json +945 -0
  411. package/src/surfaces/publish.ts +392 -0
  412. package/src/surfaces/push-plan.json +110 -0
  413. package/src/surfaces/push-plan.ts +87 -0
  414. package/src/surfaces/rail.json +255 -0
  415. package/src/surfaces/rail.ts +206 -0
  416. package/src/surfaces/reference-field.json +191 -0
  417. package/src/surfaces/reference-field.ts +168 -0
  418. package/src/surfaces/schema-builder.json +1011 -0
  419. package/src/surfaces/schema-builder.ts +181 -0
  420. package/src/surfaces/schema-form.json +1013 -0
  421. package/src/surfaces/schema-form.ts +287 -0
  422. package/src/surfaces/secret-field.json +60 -0
  423. package/src/surfaces/secret-field.ts +146 -0
  424. package/src/surfaces/seo.json +932 -0
  425. package/src/surfaces/seo.ts +334 -0
  426. package/src/surfaces/settings-contexts.json +538 -0
  427. package/src/surfaces/settings-contexts.ts +196 -0
  428. package/src/surfaces/settings-contributed.json +489 -0
  429. package/src/surfaces/settings-contributed.ts +199 -0
  430. package/src/surfaces/settings-css-vars.json +820 -0
  431. package/src/surfaces/settings-css-vars.ts +199 -0
  432. package/src/surfaces/settings-defs.json +1363 -0
  433. package/src/surfaces/settings-defs.ts +232 -0
  434. package/src/surfaces/settings-deploy.json +108 -0
  435. package/src/surfaces/settings-deploy.ts +135 -0
  436. package/src/surfaces/settings-extensions.json +486 -0
  437. package/src/surfaces/settings-extensions.ts +201 -0
  438. package/src/surfaces/settings-head.json +779 -0
  439. package/src/surfaces/settings-head.ts +397 -0
  440. package/src/surfaces/settings-locales.json +420 -0
  441. package/src/surfaces/settings-locales.ts +137 -0
  442. package/src/surfaces/settings-overview.json +423 -0
  443. package/src/surfaces/settings-overview.ts +218 -0
  444. package/src/surfaces/settings-packages.json +549 -0
  445. package/src/surfaces/settings-packages.ts +158 -0
  446. package/src/surfaces/settings-pane.json +147 -0
  447. package/src/surfaces/settings-pane.ts +150 -0
  448. package/src/surfaces/settings-rawjson.json +104 -0
  449. package/src/surfaces/settings-rawjson.ts +107 -0
  450. package/src/surfaces/shell.json +99 -0
  451. package/src/surfaces/shell.ts +30 -0
  452. package/src/surfaces/slash-menu.json +180 -0
  453. package/src/surfaces/slash-menu.ts +272 -0
  454. package/src/surfaces/statements.json +607 -0
  455. package/src/surfaces/statements.ts +238 -0
  456. package/src/surfaces/statusbar.json +94 -0
  457. package/src/{panels → surfaces}/statusbar.ts +163 -90
  458. package/src/surfaces/style-panel.json +1643 -0
  459. package/src/surfaces/style-panel.ts +338 -0
  460. package/src/surfaces/stylebook-chrome.json +122 -0
  461. package/src/surfaces/stylebook-chrome.ts +122 -0
  462. package/src/surfaces/tab-strip.json +448 -0
  463. package/src/surfaces/tab-strip.ts +200 -0
  464. package/src/surfaces/target-line.json +419 -0
  465. package/src/surfaces/target-line.ts +349 -0
  466. package/src/surfaces/toasts.json +189 -0
  467. package/src/surfaces/welcome.json +571 -0
  468. package/src/surfaces/welcome.ts +338 -0
  469. package/src/tabs/project-config.ts +71 -15
  470. package/src/tabs/tab.ts +52 -1
  471. package/src/types.ts +22 -0
  472. package/src/ui/ai-credentials-form.ts +104 -100
  473. package/src/ui/ai-managed-connect.ts +54 -36
  474. package/src/ui/ai-model-picker.ts +75 -47
  475. package/src/ui/cf-account-picker.ts +76 -141
  476. package/src/ui/color-selector.ts +80 -301
  477. package/src/ui/dynamic-slot.ts +43 -283
  478. package/src/ui/expression-editor.ts +797 -576
  479. package/src/ui/field-input.ts +8 -181
  480. package/src/ui/form-controls.ts +838 -366
  481. package/src/ui/formula-chips.ts +80 -85
  482. package/src/ui/kit.ts +33 -0
  483. package/src/ui/layers.ts +293 -593
  484. package/src/ui/media-picker.ts +203 -284
  485. package/src/ui/panel-resize.ts +251 -64
  486. package/src/ui/popover-surface.ts +153 -0
  487. package/src/ui/progress-modal.ts +22 -60
  488. package/src/ui/regions.ts +13 -7
  489. package/src/ui/schema-form.ts +906 -440
  490. package/src/ui/surface.ts +122 -0
  491. package/src/ui/unit-selector.ts +15 -131
  492. package/src/ui/value-source.ts +14 -0
  493. package/src/ui/virtual-window.ts +88 -9
  494. package/src/utils/canvas-media.ts +72 -0
  495. package/src/utils/edit-display.ts +7 -2
  496. package/src/utils/element-selectors.ts +69 -0
  497. package/src/utils/link-target.ts +2 -2
  498. package/src/utils/studio-utils.ts +0 -10
  499. package/src/view.ts +15 -0
  500. package/src/workspace/pane-derive.ts +36 -14
  501. package/src/workspace/workspace.ts +15 -6
  502. package/styles/canvas.css +72 -467
  503. package/styles/forced-colors.css +48 -82
  504. package/styles/forced-colors.json +41 -0
  505. package/styles/inspector.css +2 -1301
  506. package/styles/overlays.css +96 -2823
  507. package/styles/panels.css +6 -1423
  508. package/styles/shell-frame.css +272 -0
  509. package/styles/shell-frame.json +227 -0
  510. package/styles/shell.css +0 -1568
  511. package/styles/tokens.css +65 -136
  512. package/styles/tokens.json +84 -0
  513. package/dist/chunks/Overlay.dev-etqjwyf9.js +0 -23
  514. package/dist/chunks/adapter-jkcyhgtr.js +0 -14
  515. package/dist/chunks/cf-account-picker-en86v7x7.js +0 -163
  516. package/dist/chunks/cf-settings-45fgyx8y.js +0 -21
  517. package/dist/chunks/components-zmx10t62.js +0 -21
  518. package/dist/chunks/content-source-48xg86wj.js +0 -49
  519. package/dist/chunks/convert-to-repeater-43tfn7cs.js +0 -268
  520. package/dist/chunks/editor-cptq5jp1.js +0 -37
  521. package/dist/chunks/entry-commands-e63f1xc0.js +0 -46
  522. package/dist/chunks/extension-sections-txv08xxg.js +0 -311
  523. package/dist/chunks/file-ops-wbh4hhsx.js +0 -48
  524. package/dist/chunks/files-3kfkwhk4.js +0 -93
  525. package/dist/chunks/flow-qbfgrpeh.js +0 -634
  526. package/dist/chunks/focus-trap.esm-9t2drp0g.js +0 -1025
  527. package/dist/chunks/focus-visible-6eg70z18.js +0 -167
  528. package/dist/chunks/format-host-w16q81tf.js +0 -43
  529. package/dist/chunks/fs-events-hrrg328g.js +0 -21
  530. package/dist/chunks/git-panel-532whr1y.js +0 -59
  531. package/dist/chunks/index-zqxmqxmh.js +0 -109
  532. package/dist/chunks/jx-validate-8er6gbxy.js +0 -19
  533. package/dist/chunks/layers-panel-07hm7g5j.js +0 -67
  534. package/dist/chunks/layers-q75tj2mf.js +0 -45
  535. package/dist/chunks/lit-html-j055mkh8.js +0 -22
  536. package/dist/chunks/monaco-buffer-dqw0xxpx.js +0 -25
  537. package/dist/chunks/notify-yjm79n4s.js +0 -30
  538. package/dist/chunks/parse-24d0gay3.js +0 -19
  539. package/dist/chunks/platform-ba1j1gbh.js +0 -16
  540. package/dist/chunks/publish-panel-v9st96ja.js +0 -29
  541. package/dist/chunks/quick-search-zccjp6gv.js +0 -66
  542. package/dist/chunks/references-3qx8fkyp.js +0 -29
  543. package/dist/chunks/right-panel-vm3b6kwy.js +0 -46
  544. package/dist/chunks/section-registry-sx1ykvkw.js +0 -30
  545. package/dist/chunks/sp-overlay.dev-qgvhnxf4.js +0 -18
  546. package/dist/chunks/sp-popover.dev-n2etmqdp.js +0 -9
  547. package/dist/chunks/sp-progress-circle.dev-hryhj0es.js +0 -15
  548. package/dist/chunks/sp-tooltip.dev-4sastdnw.js +0 -15
  549. package/dist/chunks/sp-tray.dev-tkbscm06.js +0 -199
  550. package/dist/chunks/studio-0a5qrh01.js +0 -3
  551. package/dist/chunks/studio-0bq6eh7v.js +0 -12
  552. package/dist/chunks/studio-18j3bmcz.js +0 -14
  553. package/dist/chunks/studio-1ngbejft.js +0 -181
  554. package/dist/chunks/studio-4b5z1x43.js +0 -424
  555. package/dist/chunks/studio-4n2b5gzb.js +0 -357
  556. package/dist/chunks/studio-4rzyrtjz.js +0 -426
  557. package/dist/chunks/studio-5fx3g9jg.js +0 -4325
  558. package/dist/chunks/studio-5myknm0a.js +0 -1903
  559. package/dist/chunks/studio-7afd21nw.js +0 -67
  560. package/dist/chunks/studio-7nte2hj4.js +0 -613
  561. package/dist/chunks/studio-8jpzqk5s.js +0 -128
  562. package/dist/chunks/studio-8q517dfr.js +0 -24
  563. package/dist/chunks/studio-950k0dx6.js +0 -6939
  564. package/dist/chunks/studio-9n4mrmnb.js +0 -184
  565. package/dist/chunks/studio-a0tswy0w.js +0 -58
  566. package/dist/chunks/studio-ahnzd9rg.js +0 -23
  567. package/dist/chunks/studio-cmb03bg5.js +0 -13890
  568. package/dist/chunks/studio-d1ks410h.js +0 -175
  569. package/dist/chunks/studio-ef49pnzx.js +0 -387
  570. package/dist/chunks/studio-eqq1w638.js +0 -113
  571. package/dist/chunks/studio-ew0djmhg.js +0 -341
  572. package/dist/chunks/studio-gfgb8pe6.js +0 -1652
  573. package/dist/chunks/studio-h4rxscfc.js +0 -10
  574. package/dist/chunks/studio-jc994dnr.js +0 -1066
  575. package/dist/chunks/studio-kyh9avvv.js +0 -28
  576. package/dist/chunks/studio-m3hwn7y1.js +0 -120
  577. package/dist/chunks/studio-mtfnaqs9.js +0 -659
  578. package/dist/chunks/studio-n6j9t10q.js +0 -318
  579. package/dist/chunks/studio-n8b9wqwy.js +0 -291
  580. package/dist/chunks/studio-qcy05dp8.js +0 -1743
  581. package/dist/chunks/studio-qq6jym04.js +0 -53
  582. package/dist/chunks/studio-rdx8jcxy.js +0 -83
  583. package/dist/chunks/studio-rn9ta2mz.js +0 -111
  584. package/dist/chunks/studio-t7qb9db8.js +0 -665
  585. package/dist/chunks/studio-tmc5emrw.js +0 -8
  586. package/dist/chunks/studio-z6nw2ek2.js +0 -18
  587. package/dist/chunks/studio-zczps70q.js +0 -378
  588. package/dist/chunks/studio-zqazsct2.js +0 -1823
  589. package/dist/chunks/workspace-fjx52ryv.js +0 -89
  590. package/src/panels/activity-bar.ts +0 -251
  591. package/src/panels/panel-scheduler.ts +0 -219
  592. package/src/panels/style-inputs.ts +0 -237
  593. package/src/panels/target-line.ts +0 -274
  594. package/src/panels/welcome-screen.ts +0 -346
  595. package/src/ui/button-group.ts +0 -102
  596. package/src/ui/choice-field.ts +0 -72
  597. package/src/ui/field-row.ts +0 -249
  598. package/src/ui/formula-palette.ts +0 -213
  599. package/src/ui/icons.ts +0 -52
  600. package/src/ui/jx-theme.ts +0 -130
  601. package/src/ui/spectrum.ts +0 -340
  602. package/src/ui/value-selector.ts +0 -174
  603. package/src/ui/widgets.ts +0 -172
  604. /package/dist/chunks/{javascript-s56a8dpk.css → javascript-4abzjcy9.css} +0 -0
  605. /package/dist/chunks/{jsonMode-zr9sw0c7.css → jsonMode-vvmpmk34.css} +0 -0
  606. /package/dist/chunks/{monaco-setup-pav68602.css → monaco-setup-8vbyqnhr.css} +0 -0
  607. /package/dist/chunks/{tsMode-ef187txx.css → tsMode-hnxxb73h.css} +0 -0
@@ -25,16 +25,32 @@
25
25
  * them means running the page and axe-core over it, which is a different program with a different
26
26
  * lifetime. `unavailableChecks()` names them so the absence is stated rather than implied.
27
27
  *
28
+ * **The structural rules live in `@jxsuite/schema/a11y`** — an unnamed control, an image with no
29
+ * alt, an aria reference to an id nothing has, a tab, menu item or option outside its container, a
30
+ * tablist with no selected tab, an unnamed dialog, aria-activedescendant on an element that cannot
31
+ * take focus — so `jx validate`, the kit's conformance tests and this report judge a document alike
32
+ * (spec §8.8). What stays here is what needs the whole page rather than a node: the heading
33
+ * outline, duplicate ids, the document language, link wording, autocomplete purposes and media.
34
+ *
28
35
  * @docs studio/interface/problems-and-progress
29
36
  */
30
37
 
31
38
  import { activeTab } from "../workspace/workspace";
32
- import { clearProblems, notify } from "./notify";
39
+ import { clearProblems, notify, problemFindings } from "./notify";
40
+ import { findA11yDefects } from "@jxsuite/schema/a11y";
33
41
  import type { AnyCommand, CommandRegistry } from "../commands/registry";
34
42
  import type { JxElement } from "@jxsuite/schema/types";
35
43
 
36
44
  /** WCAG success criteria these checks bind to, by the id ATAG B.3.1 asks a report to carry. */
37
- export type A11yCriterion = "1.1.1" | "1.3.1" | "1.3.5" | "2.4.4" | "2.4.6" | "3.1.1" | "4.1.2";
45
+ export type A11yCriterion =
46
+ | "1.1.1"
47
+ | "1.3.1"
48
+ | "1.3.5"
49
+ | "2.1.1"
50
+ | "2.4.4"
51
+ | "2.4.6"
52
+ | "3.1.1"
53
+ | "4.1.2";
38
54
 
39
55
  /** One thing that is wrong with the document, and — where one exists — the command that fixes it. */
40
56
  export interface A11yFinding {
@@ -59,8 +75,23 @@ export interface A11yFinding {
59
75
  action?: string;
60
76
  }
61
77
 
62
- /** Elements whose whole purpose is an image, and which therefore owe alternative text. */
63
- const IMAGE_TAGS = new Set(["img", "area", "input-image"]);
78
+ /**
79
+ * Whether the element's whole purpose is an image, so its `alt` is the text a reader hears.
80
+ *
81
+ * `<input type="image">` is one of the three elements HTML gives an `alt`, and it is a TYPE rather
82
+ * than a tag. The set this replaced spelled it `"input-image"` alongside `img` and `area` — a tag
83
+ * name no document can have — so the wording check below never once ran on a submit image.
84
+ *
85
+ * @param {JxElement} node
86
+ * @param {string} tag The node's lowercased tag name.
87
+ * @returns {boolean}
88
+ */
89
+ function isImage(node: JxElement, tag: string): boolean {
90
+ if (tag === "img" || tag === "area") {
91
+ return true;
92
+ }
93
+ return tag === "input" && literal(node, "type")?.toLowerCase() === "image";
94
+ }
64
95
 
65
96
  /** Controls that owe an accessible name. */
66
97
  const CONTROL_TAGS = new Set(["input", "select", "textarea", "button"]);
@@ -170,20 +201,14 @@ export function checkDocument(doc: JxElement): A11yFinding[] {
170
201
  const where = tag === "" ? `element ${index}` : `<${tag}>`;
171
202
 
172
203
  // ── 1.1.1 Non-text Content ────────────────────────────────────────────────
173
- if (IMAGE_TAGS.has(tag)) {
204
+ if (isImage(node, tag)) {
174
205
  const alt = literal(node, "alt");
175
- if (!has(node, "alt")) {
176
- findings.push({
177
- criterion: "1.1.1",
178
- detail:
179
- "A screen reader falls back to reading the file name, which is rarely useful and " +
180
- "sometimes misleading. An image that is purely decorative takes an EMPTY alt, which " +
181
- "is a decision — the absent attribute is not.",
182
- id: `img-no-alt:${index}`,
183
- message: `${where} has no alt text.`,
184
- severity: "error",
185
- });
186
- } else if (alt !== null && /^(image|photo|picture|graphic|icon)\b/i.test(alt.trim())) {
206
+ /*
207
+ * A missing alt is the schema engine's: `img-alt-missing` for an `<img>`, and
208
+ * `interactive-unnamed` for the two that are also controls, an `<area href>` and an
209
+ * `<input type="image">`. Only the WORDING is judged here, which no engine rule does.
210
+ */
211
+ if (alt !== null && /^(image|photo|picture|graphic|icon)\b/i.test(alt.trim())) {
187
212
  findings.push({
188
213
  criterion: "1.1.1",
189
214
  detail:
@@ -233,19 +258,7 @@ export function checkDocument(doc: JxElement): A11yFinding[] {
233
258
  if (CONTROL_TAGS.has(tag)) {
234
259
  const type = literal(node, "type")?.toLowerCase() ?? "";
235
260
  const selfLabelling = tag === "button" || SELF_LABELLING_INPUTS.has(type);
236
- const labelled = hasAccessibleName(node) || has(node, "id");
237
- if (!selfLabelling && !labelled) {
238
- findings.push({
239
- criterion: "4.1.2",
240
- detail:
241
- 'A control with no name is announced as its type alone — "edit", "combo box" — ' +
242
- "which tells a reader nothing about what to type. Give it an aria-label, or an id a " +
243
- "<label> points at.",
244
- id: `control-unnamed:${index}`,
245
- message: `${where} has no label.`,
246
- severity: "error",
247
- });
248
- }
261
+ // An unnamed control is the schema rule `interactive-unnamed`, with `<label for>` resolved.
249
262
  // 1.3.5: a field asking for the user's own data should say which (autocomplete).
250
263
  if (tag === "input" && !selfLabelling && !has(node, "autocomplete")) {
251
264
  const name = `${literal(node, "name") ?? ""} ${literal(node, "id") ?? ""}`.toLowerCase();
@@ -269,17 +282,8 @@ export function checkDocument(doc: JxElement): A11yFinding[] {
269
282
  .toLowerCase()
270
283
  .replaceAll(/[^a-z ]/g, "")
271
284
  .trim();
272
- if (!hasAccessibleName(node)) {
273
- findings.push({
274
- criterion: "4.1.2",
275
- detail:
276
- "A link with no text is announced as its URL, one character at a time in some " +
277
- "readers. If the link is an icon, give it an aria-label.",
278
- id: `link-unnamed:${index}`,
279
- message: "A link has no text.",
280
- severity: "error",
281
- });
282
- } else if (VAGUE_LINK_TEXT.has(text)) {
285
+ // A link with no text is the schema rule `interactive-unnamed`; only the wording is judged here.
286
+ if (hasAccessibleName(node) && VAGUE_LINK_TEXT.has(text)) {
283
287
  findings.push({
284
288
  criterion: "2.4.4",
285
289
  detail:
@@ -363,7 +367,38 @@ export function checkDocument(doc: JxElement): A11yFinding[] {
363
367
  });
364
368
  }
365
369
 
366
- return findings;
370
+ // The structural rules, from the engine every judge of a Jx document shares.
371
+ for (const defect of findA11yDefects(doc)) {
372
+ findings.push({
373
+ criterion: defect.criterion as A11yCriterion,
374
+ detail: defect.detail,
375
+ id: `${defect.rule}:${defect.path.join("/")}`,
376
+ message: defect.message,
377
+ severity: defect.severity,
378
+ });
379
+ }
380
+ return distinctIds(findings);
381
+ }
382
+
383
+ /**
384
+ * Give every finding an id no other finding shares.
385
+ *
386
+ * A Problem key REPLACES rather than stacks, so two findings under one id are one row, and the
387
+ * author is told about the last of them and never hears about the rest. Two rules produce
388
+ * duplicates honestly: `aria-target-missing` files one per token that resolves to nothing, so
389
+ * `aria-labelledby="a b c"` is three at one rule and one path; and `duplicate-id` files one per
390
+ * extra element sharing an id, so three elements are two findings under one id. The ordinal is
391
+ * applied to every finding rather than to one loop, because "a key names a finding" is either true
392
+ * of all of them or it is not the rule it claims to be. The first keeps its bare id, so nothing
393
+ * already distinct moves and no existing key changes.
394
+ */
395
+ function distinctIds(findings: A11yFinding[]): A11yFinding[] {
396
+ const seen = new Map<string, number>();
397
+ return findings.map((finding) => {
398
+ const before = seen.get(finding.id) ?? 0;
399
+ seen.set(finding.id, before + 1);
400
+ return before === 0 ? finding : { ...finding, id: `${finding.id}#${before}` };
401
+ });
367
402
  }
368
403
 
369
404
  /** A check this program cannot run, and the honest reason. */
@@ -464,8 +499,30 @@ export function a11yCommands(): AnyCommand[] {
464
499
  description:
465
500
  "Check the open document for accessibility problems an author can fix — missing alt " +
466
501
  "text, unlabelled controls, skipped heading levels, vague link text, duplicate ids — " +
467
- "and file each as a Problem naming its WCAG criterion.",
502
+ "and file each as a Problem naming its WCAG criterion. The findings come back as data.",
468
503
  name: "check_accessibility",
504
+ /* A read the model has no other way to perform. The report is the PANEL's own read — the
505
+ Problems store filtered by this source — minus the unchecked-coverage rows, which are
506
+ about what this run could not see and are counted as such rather than listed as
507
+ defects. "Check this page and fix what you find" is two rounds, not a parse of a toast. */
508
+ report: () => {
509
+ const unchecked = unavailableChecks().length;
510
+ const findings = problemFindings(A11Y_PROBLEM_SOURCE).filter(
511
+ (finding) => !finding.key?.startsWith("a11y.unavailable."),
512
+ );
513
+ const n = findings.length;
514
+ const coverage =
515
+ unchecked === 0
516
+ ? ""
517
+ : ` ${unchecked} check${unchecked === 1 ? "" : "s"} could not run and ${unchecked === 1 ? "is" : "are"} listed in Problems as such.`;
518
+ return {
519
+ data: findings,
520
+ summary:
521
+ n === 0
522
+ ? `No accessibility problems found in this document.${coverage}`
523
+ : `Filed ${n} accessibility problem${n === 1 ? "" : "s"} in Problems.${coverage}`,
524
+ };
525
+ },
469
526
  },
470
527
  category: "Document",
471
528
  group: "2_document",
@@ -0,0 +1,468 @@
1
+ /**
2
+ * Ai-command-tools.ts — the assistant's tools that ARE command records.
3
+ *
4
+ * A `ToolRegistry` that is a VIEW over this window's active command registry, in the idiom of
5
+ * `services/automation.ts`'s `scriptableCommands()`: nothing is registered here, ever. Every call to
6
+ * `listForLLM()` enumerates the records that carry an `aiTool` projection and builds one function
7
+ * schema per record whose `parameters` is the record's own `args` object — by reference, so a
8
+ * `derivedEnumProperty` getter serialises live on every agent-loop round — whose `description` is
9
+ * the record's, and which is advertised exactly while `registry.isEnabled(id)` holds. `execute` is
10
+ * `registry.run(id, args)`.
11
+ *
12
+ * **Why a view and not a registration.** `const assistant = createDocumentAssistant()` runs at
13
+ * module load of `panels/ai-panel.ts`, before the bootstrap composes the registry, so a
14
+ * registration step would have to import `app-commands.ts` (every command module into the
15
+ * assistant's import graph) and read `report` from a record built with `noopCommandDeps` — a
16
+ * second instance of every record, whose closures are not the app's. A view has neither problem,
17
+ * and it cannot be dormant: issue 273 was forty-seven declarations that reached the assistant from
18
+ * none of them, because each waited on a hand-registered tool nobody wrote. There is no
19
+ * registration step between `aiTool` and the tool, so there is nothing to omit.
20
+ *
21
+ * **What the model reads.** A refusal is the person's own sentence: `CommandUnavailableError.message`
22
+ * IS `refusalSentence`, and a `RangeError` from `coerceArgs` (which `registry.run` applies for
23
+ * every caller) or from inside `run` already names the value and the declared list. Both pass
24
+ * through untouched. There is deliberately no pre-check with `refusalMessage` — `run` refuses with
25
+ * the same sentence, and a second call site for one predicate is precisely the divergence
26
+ * `specs/studio-ui-guidelines.md` §12.4 is about. And because this module owns `execute`,
27
+ * `@jxsuite/ai`'s `Tool "<name>" execution error:` prefix never wraps a refusal.
28
+ *
29
+ * **The write witness.** A record with `undo: "document"` is held to having written: `transactDoc`
30
+ * replaces the document's root reference on every applied transaction (`tabs/transact.ts`), so an
31
+ * unchanged reference after `run` means the collab freeze refused it, or `run` returned early, and
32
+ * the model is told "changed nothing" rather than congratulated. This is what closes the
33
+ * phantom-success finding for every bridged document write, not just the one tool it was found on.
34
+ *
35
+ * **`validate()` is a no-op, on purpose.** `@jxsuite/ai`'s validator never checks `enum` and treats
36
+ * a required `null` as missing (`packages/ai/src/tools.ts`), which would refuse `select_node
37
+ * { path: null }`. The registry's coercion is the single validator, so every projected tool is
38
+ * `strict: false`.
39
+ *
40
+ * @license MIT
41
+ */
42
+
43
+ import { toolError } from "@jxsuite/ai/tools";
44
+ import type { ToolDefinition, ToolRegistry, ToolResult } from "@jxsuite/ai/tools";
45
+ import { activeRegistry } from "../commands/active-registry";
46
+ import { argsSchema } from "../commands/command-args";
47
+ import { inCanvas } from "../commands/context";
48
+ import { toRaw } from "../reactivity";
49
+ import { recordWrite } from "./ai-writes";
50
+ import { reportDocumentWrite, snapshotBeforeWrite } from "./ai-write-report";
51
+ import type { WriteReportDeps, WriteSnapshot } from "./ai-write-report";
52
+ import type { CommandContext } from "../commands/context";
53
+ import type { AiToolReport, AnyCommand, CommandRegistry } from "../commands/registry";
54
+ import type { Tab } from "../tabs/tab";
55
+
56
+ /**
57
+ * The record a selection-level projection composes through.
58
+ *
59
+ * The model addresses nodes by path, so a selection verb's tool takes `paths` and the bridge runs
60
+ * this selector first. "Act on whatever is selected" would be a verb over state the model cannot
61
+ * observe — the same objection `specs/studio.md` §13.5 raises against a `toggle*` id.
62
+ */
63
+ export const SELECTOR = "selection.setPaths";
64
+
65
+ /** What the bridge needs that a command record cannot know: the tab, and the write reporter's deps. */
66
+ export interface CommandToolDeps extends WriteReportDeps {
67
+ /**
68
+ * The active tab — the document a `undo: "document"` record wrote, for the witness and the
69
+ * ledger.
70
+ */
71
+ getTab: () => Tab | null;
72
+ }
73
+
74
+ /** Schema for a record that declares no `args`: an empty object, so the wire shape is uniform. */
75
+ const EMPTY_ARGS = argsSchema({});
76
+
77
+ /** The loosely-typed view of a record's `args` this module reads. */
78
+ interface ArgsShape {
79
+ properties?: Record<string, object>;
80
+ required?: readonly string[];
81
+ }
82
+
83
+ /**
84
+ * The selector's own `paths` property object, taken BY REFERENCE from the live record so the
85
+ * description the model reads is the selector's. `undefined` when the selector is not registered,
86
+ * in which case no selection-level record can be composed and none is projected.
87
+ */
88
+ function pathsProperty(registry: CommandRegistry): object | undefined {
89
+ const args = registry.get(SELECTOR)?.args as ArgsShape | undefined;
90
+ return args?.properties?.paths;
91
+ }
92
+
93
+ /**
94
+ * A selection-level record's parameters: the selector's `paths` in front, REQUIRED, then the
95
+ * record's own properties. Merging the record's after the selector's is what lets a future
96
+ * selection verb with an argument of its own compose without a rule change — and `register()`
97
+ * refuses a selection-level record that declares `paths` itself, so the two cannot collide.
98
+ */
99
+ function withPaths(paths: object, args: object | undefined): object {
100
+ const own = (args ?? EMPTY_ARGS) as ArgsShape;
101
+ return {
102
+ additionalProperties: false,
103
+ properties: { paths, ...own.properties },
104
+ required: ["paths", ...(own.required ?? [])],
105
+ type: "object",
106
+ };
107
+ }
108
+
109
+ /**
110
+ * The tail every projected description carries, so a record says neither in its own sentence.
111
+ *
112
+ * `undo` names what ⌘Z reaches; `destructive` is the same flag the menus derive the danger styling
113
+ * from. A record with no `undo` is a read or a navigation, and says nothing.
114
+ */
115
+ function undoSuffix(command: AnyCommand): string {
116
+ const undo =
117
+ command.undo === "document"
118
+ ? " Undo: document."
119
+ : command.undo === "project"
120
+ ? " Undo: project."
121
+ : command.undo === "none"
122
+ ? " Not undoable."
123
+ : "";
124
+ return `${undo}${command.destructive ? " Destructive." : ""}`;
125
+ }
126
+
127
+ /**
128
+ * Whether a selection-level projection can be composed and honoured right now: a document tree is
129
+ * on the canvas (`inCanvas`, the predicate `hasSelection` is built from — read, not respelled) and
130
+ * the selector is usable, which is "a document is open". The `count > 0` conjunct of the verb's own
131
+ * `when` is what the bridge satisfies itself by running the selector.
132
+ */
133
+ function selectionAdvertised(registry: CommandRegistry): boolean {
134
+ return (
135
+ inCanvas(registry.context()) &&
136
+ registry.get(SELECTOR) !== undefined &&
137
+ registry.isEnabled(SELECTOR)
138
+ );
139
+ }
140
+
141
+ /**
142
+ * Whether one record's tool is advertised this round. For a project- or document-level record it is
143
+ * the record's own gate, evaluated as the palette evaluates it — a module-state `enablement`
144
+ * closure included. Selection-level records are the one derived case, above.
145
+ */
146
+ function advertised(registry: CommandRegistry, command: AnyCommand): boolean {
147
+ return command.level === "selection"
148
+ ? selectionAdvertised(registry)
149
+ : registry.isEnabled(command.id);
150
+ }
151
+
152
+ /** Every record carrying a projection, enabled or not. */
153
+ function projected(registry: CommandRegistry): AnyCommand[] {
154
+ return registry.list().filter((command) => command.aiTool !== undefined);
155
+ }
156
+
157
+ /**
158
+ * The definition one record projects, or `undefined` for a selection-level record when the selector
159
+ * it composes through is not registered.
160
+ */
161
+ function definitionOf(
162
+ registry: CommandRegistry,
163
+ command: AnyCommand,
164
+ execute: (name: string, args: object) => Promise<ToolResult>,
165
+ ): ToolDefinition | undefined {
166
+ const tool = command.aiTool!;
167
+ let parameters: object;
168
+ if (command.level === "selection") {
169
+ const paths = pathsProperty(registry);
170
+ if (paths === undefined) {
171
+ return undefined;
172
+ }
173
+ parameters = withPaths(paths, command.args);
174
+ } else {
175
+ parameters = command.args ?? EMPTY_ARGS;
176
+ }
177
+ return {
178
+ name: tool.name,
179
+ description: `${tool.description}${undoSuffix(command)}`,
180
+ parameters: parameters as ToolDefinition["parameters"],
181
+ strict: false,
182
+ llmStrict: false,
183
+ execute: (args) => execute(tool.name, args),
184
+ };
185
+ }
186
+
187
+ /**
188
+ * Pure: the definitions a given registry projects RIGHT NOW (advertised = the record's gate).
189
+ *
190
+ * Read by `listForLLM()` and by {@link commandToolBlurbs} both, so the prompt advertises exactly
191
+ * what the gate will honour — one function, not two that agree until someone edits one.
192
+ *
193
+ * @param {CommandRegistry} registry
194
+ * @param {(name: string, args: object) => Promise<ToolResult>} [execute] What each definition's
195
+ * `execute` calls. Defaults to a refusal, so a caller that only reads definitions cannot run
196
+ * one.
197
+ * @returns {ToolDefinition[]}
198
+ */
199
+ export function advertisedCommandTools(
200
+ registry: CommandRegistry,
201
+ execute: (name: string, args: object) => Promise<ToolResult> = async (name) =>
202
+ toolError(`Tool "${name}" was listed without an executor.`),
203
+ ): ToolDefinition[] {
204
+ const definitions: ToolDefinition[] = [];
205
+ for (const command of projected(registry)) {
206
+ if (!advertised(registry, command)) {
207
+ continue;
208
+ }
209
+ const definition = definitionOf(registry, command, execute);
210
+ if (definition) {
211
+ definitions.push(definition);
212
+ }
213
+ }
214
+ return definitions;
215
+ }
216
+
217
+ /** The parameter list a prompt blurb prints: `a, b?` — required names bare, optional ones marked. */
218
+ function signatureOf(parameters: object): string {
219
+ const shape = parameters as ArgsShape;
220
+ const required = new Set(shape.required);
221
+ return Object.keys(shape.properties ?? {})
222
+ .map((key) => (required.has(key) ? key : `${key}?`))
223
+ .join(", ");
224
+ }
225
+
226
+ /**
227
+ * Pure: the prompt lines for the definitions a registry projects right now, in the hand tools' own
228
+ * blurb shape: `name(a, b?) — description`. `null` (no registry composed yet) is no lines.
229
+ *
230
+ * Why `isEnabled` rather than `isVisible` with a "(not right now)" line: the prompt's invariant is
231
+ * "advertises exactly what the gate will honour", and with the kept records there is no steady
232
+ * state in which a projected record is visible and disabled, so the line would be empty in practice
233
+ * and cost a wasted round when it was not. If a future record wants the palette's "why can't I"
234
+ * answer in the prompt, this is the one function to extend.
235
+ *
236
+ * @param {CommandRegistry | null} registry
237
+ * @returns {string[]}
238
+ */
239
+ export function commandToolBlurbs(registry: CommandRegistry | null): string[] {
240
+ if (!registry) {
241
+ return [];
242
+ }
243
+ return advertisedCommandTools(registry).map(
244
+ (definition) =>
245
+ `${definition.name}(${signatureOf(definition.parameters)}) — ${definition.description}`,
246
+ );
247
+ }
248
+
249
+ /** A `report` return, normalised: a bare string is a summary alone. */
250
+ function normaliseReport(report: string | AiToolReport): AiToolReport {
251
+ return typeof report === "string" ? { summary: report } : report;
252
+ }
253
+
254
+ /**
255
+ * The ledger paths a record's write defaults to when `report` names none, by undo scope. `"none"`
256
+ * has no default on purpose — see {@link AiToolReport.wrote}.
257
+ */
258
+ function defaultPaths(command: AnyCommand, tab: Tab | null): string[] {
259
+ if (command.undo === "document") {
260
+ return [tab?.documentPath ?? "(untitled)"];
261
+ }
262
+ if (command.undo === "project") {
263
+ return ["project.json"];
264
+ }
265
+ return ["(unknown file)"];
266
+ }
267
+
268
+ /**
269
+ * File a projected run in the "Changed N files" ledger, by undo scope.
270
+ *
271
+ * No `undo` is a read or a navigation and records nothing. `disk` is `undo === "none"`, because
272
+ * `undo: "project"` IS a transaction ⌘Z reaches and the ledger's flag means "unreachable by undo".
273
+ * An EMPTY `wrote` is a run that changed nothing — an idempotent verb asked for a state it already
274
+ * had — and files nothing, because the ledger names files that changed; `undefined` is a report
275
+ * that named none and takes the record's defaults.
276
+ */
277
+ function fileLedger(command: AnyCommand, tab: Tab | null, wrote?: readonly string[]): void {
278
+ if (command.undo === undefined) {
279
+ return;
280
+ }
281
+ const disk = command.undo === "none";
282
+ for (const path of wrote ?? defaultPaths(command, tab)) {
283
+ recordWrite({ disk, ok: true, path, tool: command.title });
284
+ }
285
+ }
286
+
287
+ /**
288
+ * A ToolRegistry whose tools are the active registry's `aiTool` records. Nothing is registered.
289
+ *
290
+ * @param {CommandToolDeps} deps
291
+ * @returns {ToolRegistry}
292
+ */
293
+ export function createCommandToolRegistry(deps: CommandToolDeps): ToolRegistry {
294
+ /** The record projecting `name`, enabled or not — so `execute` can reach a refusal. */
295
+ function commandFor(name: string): AnyCommand | undefined {
296
+ return activeRegistry()
297
+ ?.list()
298
+ .find((command) => command.aiTool?.name === name);
299
+ }
300
+
301
+ /**
302
+ * Run one projected record as the agent — the whole of §3 of the design.
303
+ *
304
+ * @param {string} name
305
+ * @param {object} received
306
+ * @returns {Promise<ToolResult>}
307
+ */
308
+ async function execute(name: string, received: object): Promise<ToolResult> {
309
+ const registry = activeRegistry();
310
+ const command = commandFor(name);
311
+ if (!registry || !command) {
312
+ /* Two absences, one shape. A window with no registry is answered in today's wording; a name
313
+ no record projects is `@jxsuite/ai`'s own `Unknown tool` sentence. */
314
+ return registry
315
+ ? toolError(`Unknown tool: "${name}"`)
316
+ : { error: `Cannot run "${name}" — this window has no command registry.`, success: false };
317
+ }
318
+ const tool = command.aiTool!;
319
+ const args = received as Record<string, unknown>;
320
+
321
+ /* The witness and the snapshot, only for a record that writes the document: `rootBefore` is
322
+ what `transactDoc` replaces, and the snapshot is what the verdict compares against. */
323
+ const tab = command.undo === "document" ? deps.getTab() : null;
324
+ const rootBefore = tab ? toRaw(tab.doc.document) : null;
325
+ let snapshot: WriteSnapshot | null = null;
326
+ let before: CommandContext;
327
+ try {
328
+ if (tab) {
329
+ snapshot = await snapshotBeforeWrite(tab, deps);
330
+ }
331
+ if (command.level === "selection") {
332
+ /* The selector first, with its own gate and its own argument refusal ("addresses no
333
+ node", "expected an array of document paths"); then the verb, against the selection the
334
+ selector made. Both steps are records; the bridge holds no predicate of its own. On a
335
+ refusal at the second step the selection stays where the selector put it — the honest
336
+ trace of what the agent attempted, and restoring it would be a second write inside a
337
+ refusal path. */
338
+ const { paths, ...rest } = args;
339
+ await registry.run(SELECTOR, { paths });
340
+ before = registry.context();
341
+ await registry.run(command.id, rest);
342
+ } else {
343
+ before = registry.context();
344
+ await registry.run(command.id, args);
345
+ }
346
+ } catch (error) {
347
+ return { error: error instanceof Error ? error.message : String(error), success: false };
348
+ }
349
+
350
+ if (tab && toRaw(tab.doc.document) === rootBefore) {
351
+ const frozen = registry.context().collab.sourceCanonical
352
+ ? " A collaborator is editing its source, so structural edits are paused."
353
+ : "";
354
+ return {
355
+ error: `${command.title} changed nothing: the document is exactly as it was.${frozen}`,
356
+ success: false,
357
+ };
358
+ }
359
+
360
+ const after = registry.context();
361
+ let report: AiToolReport;
362
+ try {
363
+ report = normaliseReport(tool.report({ after, args: args as never, before }));
364
+ } catch (error) {
365
+ /* `run` has resolved, so the write HAPPENED; a report that throws must not make it vanish.
366
+ Left to the loop, the throw would be labelled "Failed to parse arguments" and the ledger
367
+ would never be filed. So the ledger is filed from `undo`'s defaults — the report that would
368
+ have named the paths is the thing that failed — and the model is told both halves. */
369
+ fileLedger(command, tab);
370
+ const message = error instanceof Error ? error.message : String(error);
371
+ return { error: `${command.title} ran, but its report failed: ${message}`, success: false };
372
+ }
373
+ fileLedger(command, tab, report.wrote);
374
+
375
+ let { summary } = report;
376
+ if (tab && snapshot) {
377
+ const verdict = await reportDocumentWrite(tab, snapshot, summary, deps);
378
+ if (!verdict.success) {
379
+ return verdict;
380
+ }
381
+ /* A success verdict may have appended the token hints; the report's sentence is kept. */
382
+ summary = verdict.summary ?? summary;
383
+ }
384
+ return {
385
+ success: true,
386
+ summary,
387
+ ...(report.data === undefined ? {} : { data: report.data }),
388
+ };
389
+ }
390
+
391
+ return {
392
+ register() {
393
+ throw new Error(
394
+ "command tools are a view over the registry; declare aiTool on the record instead",
395
+ );
396
+ },
397
+ list() {
398
+ const registry = activeRegistry();
399
+ return registry ? advertisedCommandTools(registry, execute) : [];
400
+ },
401
+ listForLLM() {
402
+ return this.list().map((definition) => ({
403
+ type: "function",
404
+ function: {
405
+ name: definition.name,
406
+ description: definition.description,
407
+ parameters: definition.parameters,
408
+ },
409
+ }));
410
+ },
411
+ getDefinition(name) {
412
+ const registry = activeRegistry();
413
+ const command = commandFor(name);
414
+ return registry && command ? definitionOf(registry, command, execute) : undefined;
415
+ },
416
+ validate() {
417
+ return { valid: true };
418
+ },
419
+ execute,
420
+ };
421
+ }
422
+
423
+ /**
424
+ * Union of two registries for the loop: hand tools gated by tier, command tools gated by the
425
+ * record.
426
+ *
427
+ * `list`/`listForLLM` concatenate (hand first); `getDefinition`/`validate`/`execute` go to
428
+ * whichever side has the definition, hand first; an unknown name answers `Unknown tool: "<name>"`
429
+ * as `@jxsuite/ai` does; `register` goes to `hand`. A name on both sides is a defect the parity
430
+ * test in `tests/ai-system-prompt.test.ts` catches; the composite's hand-first order is not a
431
+ * feature anyone may rely on.
432
+ *
433
+ * @param {ToolRegistry} hand
434
+ * @param {ToolRegistry} commands
435
+ * @returns {ToolRegistry}
436
+ */
437
+ export function composeToolRegistries(hand: ToolRegistry, commands: ToolRegistry): ToolRegistry {
438
+ function sideOf(name: string): ToolRegistry | undefined {
439
+ if (hand.getDefinition(name)) {
440
+ return hand;
441
+ }
442
+ return commands.getDefinition(name) ? commands : undefined;
443
+ }
444
+ return {
445
+ register(tool) {
446
+ hand.register(tool);
447
+ },
448
+ list() {
449
+ return [...hand.list(), ...commands.list()];
450
+ },
451
+ listForLLM() {
452
+ return [...hand.listForLLM(), ...commands.listForLLM()];
453
+ },
454
+ getDefinition(name) {
455
+ return sideOf(name)?.getDefinition(name);
456
+ },
457
+ validate(name, args) {
458
+ const side = sideOf(name);
459
+ return side
460
+ ? side.validate(name, args)
461
+ : { valid: false, errors: [`Unknown tool: "${name}"`] };
462
+ },
463
+ async execute(name, args) {
464
+ const side = sideOf(name);
465
+ return side ? side.execute(name, args) : toolError(`Unknown tool: "${name}"`);
466
+ },
467
+ };
468
+ }
@@ -45,6 +45,12 @@ export interface PersistedMessage {
45
45
  id?: string;
46
46
  role: string;
47
47
  content: string;
48
+ /**
49
+ * A thinking model's chain-of-thought for the turn. Persisted because the provider requires it
50
+ * back: a restored DeepSeek conversation whose reasoning was dropped on the way to storage is one
51
+ * the next send cannot replay (see `@jxsuite/ai`'s `toMessagesArray`).
52
+ */
53
+ reasoningContent?: string;
48
54
  toolCalls?: unknown[];
49
55
  toolCallId?: string;
50
56
  timestamp?: number;