@jxsuite/studio 2.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (419) hide show
  1. package/canvas.html +21 -0
  2. package/dist/chunks/Overlay.dev-qfjzkms8.js +23 -0
  3. package/dist/chunks/{Overlay.dev-cszx7yn5.js.map → Overlay.dev-qfjzkms8.js.map} +1 -1
  4. package/dist/chunks/{adapter-z8fkmbmv.js → adapter-qfnesn5v.js} +2 -2
  5. package/dist/chunks/{cf-settings-mmyv436d.js → cf-settings-73sh5z4p.js} +3 -3
  6. package/dist/chunks/{components-0hzentwq.js → components-2bpqp4mq.js} +5 -7
  7. package/dist/chunks/{components-0hzentwq.js.map → components-2bpqp4mq.js.map} +1 -1
  8. package/dist/chunks/content-source-34gnkbnn.js +38 -0
  9. package/dist/chunks/content-source-34gnkbnn.js.map +9 -0
  10. package/dist/chunks/convert-to-repeater-t8kxre5s.js +258 -0
  11. package/dist/chunks/convert-to-repeater-t8kxre5s.js.map +10 -0
  12. package/dist/chunks/extension-sections-6mqydz35.js +301 -0
  13. package/dist/chunks/extension-sections-6mqydz35.js.map +11 -0
  14. package/dist/chunks/files-8kn321h1.js +70 -0
  15. package/dist/chunks/files-8kn321h1.js.map +9 -0
  16. package/dist/chunks/{format-host-2c188n41.js → format-host-mkwsmt8d.js} +3 -3
  17. package/dist/chunks/git-panel-tjbr191d.js +51 -0
  18. package/dist/chunks/git-panel-tjbr191d.js.map +9 -0
  19. package/dist/chunks/{index-baeg2x3y.js → index-7pxchcce.js} +4 -3
  20. package/dist/chunks/{index-baeg2x3y.js.map → index-7pxchcce.js.map} +3 -3
  21. package/dist/chunks/{jx-validate-qx8degj7.js → jx-validate-v6qgknrs.js} +3 -3
  22. package/dist/chunks/layers-panel-87pb25km.js +58 -0
  23. package/dist/chunks/layers-panel-87pb25km.js.map +9 -0
  24. package/dist/chunks/{monaco-setup-zjdp9zdw.js → monaco-setup-e0yh25j4.js} +4 -75
  25. package/dist/chunks/{monaco-setup-zjdp9zdw.js.map → monaco-setup-e0yh25j4.js.map} +3 -4
  26. package/dist/chunks/publish-panel-m0fcgdrt.js +21 -0
  27. package/dist/chunks/publish-panel-m0fcgdrt.js.map +9 -0
  28. package/dist/chunks/quick-search-rhs6cbsb.js +53 -0
  29. package/dist/chunks/quick-search-rhs6cbsb.js.map +9 -0
  30. package/dist/chunks/right-panel-zpdxd98t.js +37 -0
  31. package/dist/chunks/right-panel-zpdxd98t.js.map +9 -0
  32. package/dist/chunks/section-registry-zmckaxf4.js +30 -0
  33. package/dist/chunks/section-registry-zmckaxf4.js.map +9 -0
  34. package/dist/chunks/sp-overlay.dev-9ran0sfq.js +18 -0
  35. package/dist/chunks/sp-overlay.dev-9ran0sfq.js.map +9 -0
  36. package/dist/chunks/sp-popover.dev-rebmdncr.js +9 -0
  37. package/dist/chunks/sp-popover.dev-rebmdncr.js.map +9 -0
  38. package/dist/chunks/sp-progress-circle.dev-5vkppfkt.js +15 -0
  39. package/dist/chunks/sp-progress-circle.dev-5vkppfkt.js.map +9 -0
  40. package/dist/chunks/sp-tooltip.dev-ykqrg8h5.js +15 -0
  41. package/dist/chunks/sp-tooltip.dev-ykqrg8h5.js.map +9 -0
  42. package/dist/chunks/{sp-tray.dev-pb2fsd65.js → sp-tray.dev-ey59vfh7.js} +10 -8
  43. package/dist/chunks/{sp-tray.dev-pb2fsd65.js.map → sp-tray.dev-ey59vfh7.js.map} +2 -2
  44. package/dist/chunks/{studio-9qr6fq7p.js → studio-0rqqhrdy.js} +10 -378
  45. package/dist/chunks/{studio-9qr6fq7p.js.map → studio-0rqqhrdy.js.map} +4 -14
  46. package/dist/chunks/{studio-5x6tqqec.js → studio-2sj1jj3v.js} +10 -8
  47. package/dist/chunks/{studio-5x6tqqec.js.map → studio-2sj1jj3v.js.map} +2 -2
  48. package/dist/chunks/{studio-h3fw0wn8.js → studio-3fk1ac9z.js} +6 -4
  49. package/dist/chunks/{studio-h3fw0wn8.js.map → studio-3fk1ac9z.js.map} +2 -2
  50. package/dist/chunks/studio-41exgzhq.js +6690 -0
  51. package/dist/chunks/studio-41exgzhq.js.map +38 -0
  52. package/dist/chunks/{studio-7v2bpweq.js → studio-4mqb9a0j.js} +6 -4
  53. package/dist/chunks/{studio-7v2bpweq.js.map → studio-4mqb9a0j.js.map} +2 -2
  54. package/dist/chunks/studio-50k77cca.js +1787 -0
  55. package/dist/chunks/studio-50k77cca.js.map +18 -0
  56. package/dist/chunks/{studio-56v3zwk6.js → studio-6a0h7sne.js} +10 -8
  57. package/dist/chunks/{studio-56v3zwk6.js.map → studio-6a0h7sne.js.map} +2 -2
  58. package/dist/chunks/studio-6f0am8f2.js +53 -0
  59. package/dist/chunks/studio-6f0am8f2.js.map +10 -0
  60. package/dist/chunks/studio-6nmjc5qk.js +7002 -0
  61. package/dist/chunks/studio-6nmjc5qk.js.map +32 -0
  62. package/dist/chunks/studio-7afd21nw.js +67 -0
  63. package/dist/chunks/studio-7afd21nw.js.map +10 -0
  64. package/dist/chunks/{studio-xze4zrs1.js → studio-7bmdvzne.js} +2 -2
  65. package/dist/chunks/studio-7bmdvzne.js.map +10 -0
  66. package/dist/chunks/studio-8jpzqk5s.js +128 -0
  67. package/dist/chunks/studio-8jpzqk5s.js.map +10 -0
  68. package/dist/chunks/studio-94m77tgk.js +1482 -0
  69. package/dist/chunks/studio-94m77tgk.js.map +13 -0
  70. package/dist/chunks/studio-9je6w5sq.js +1000 -0
  71. package/dist/chunks/studio-9je6w5sq.js.map +16 -0
  72. package/dist/chunks/{studio-w7c6kvk7.js → studio-a7pzveq1.js} +2 -2
  73. package/dist/chunks/{studio-97qcgvdn.js → studio-adn0z1k3.js} +14 -8
  74. package/dist/chunks/{studio-97qcgvdn.js.map → studio-adn0z1k3.js.map} +3 -3
  75. package/dist/chunks/{studio-kr88ryr7.js → studio-bsxxx3x2.js} +17 -15
  76. package/dist/chunks/{studio-kr88ryr7.js.map → studio-bsxxx3x2.js.map} +7 -6
  77. package/dist/chunks/studio-bw5dz6km.js +608 -0
  78. package/dist/chunks/studio-bw5dz6km.js.map +21 -0
  79. package/dist/chunks/studio-c6s5getc.js +4232 -0
  80. package/dist/chunks/studio-c6s5getc.js.map +39 -0
  81. package/dist/chunks/studio-cjm7fdjf.js +451 -0
  82. package/dist/chunks/studio-cjm7fdjf.js.map +14 -0
  83. package/dist/chunks/{studio-cx91mnh8.js → studio-ckwz4d3x.js} +53 -53
  84. package/dist/chunks/{studio-cx91mnh8.js.map → studio-ckwz4d3x.js.map} +5 -5
  85. package/dist/chunks/studio-dg7zyt5x.js +28 -0
  86. package/dist/chunks/studio-dg7zyt5x.js.map +10 -0
  87. package/dist/chunks/studio-dzcyvvvz.js +166 -0
  88. package/dist/chunks/studio-dzcyvvvz.js.map +10 -0
  89. package/dist/chunks/{studio-7kqe41sa.js → studio-evkr5d3b.js} +15 -7
  90. package/dist/chunks/{studio-7kqe41sa.js.map → studio-evkr5d3b.js.map} +5 -4
  91. package/dist/chunks/studio-f12h6177.js +411 -0
  92. package/dist/chunks/studio-f12h6177.js.map +13 -0
  93. package/dist/chunks/studio-hbp1s8m9.js +1817 -0
  94. package/dist/chunks/studio-hbp1s8m9.js.map +14 -0
  95. package/dist/chunks/studio-k2sxjph5.js +15295 -0
  96. package/dist/chunks/studio-k2sxjph5.js.map +235 -0
  97. package/dist/chunks/studio-myje51t3.js +385 -0
  98. package/dist/chunks/studio-myje51t3.js.map +12 -0
  99. package/dist/chunks/studio-na6rwzhw.js +353 -0
  100. package/dist/chunks/studio-na6rwzhw.js.map +11 -0
  101. package/dist/chunks/{studio-4k0m4fmp.js → studio-p471gg5n.js} +126 -3
  102. package/dist/chunks/studio-p471gg5n.js.map +14 -0
  103. package/dist/chunks/{studio-9bjwf0d4.js → studio-rgwcpast.js} +5 -46
  104. package/dist/chunks/{studio-9bjwf0d4.js.map → studio-rgwcpast.js.map} +4 -5
  105. package/dist/chunks/{studio-hhj92gcr.js → studio-s519zc99.js} +3 -3
  106. package/dist/chunks/studio-sas7mt3k.js +75 -0
  107. package/dist/chunks/studio-sas7mt3k.js.map +10 -0
  108. package/dist/chunks/studio-t5wqwzdh.js +74 -0
  109. package/dist/chunks/studio-t5wqwzdh.js.map +10 -0
  110. package/dist/chunks/{studio-9qpspwfn.js → studio-temv5bw4.js} +5 -5
  111. package/dist/chunks/{studio-9qpspwfn.js.map → studio-temv5bw4.js.map} +1 -1
  112. package/dist/chunks/{studio-qkvt37c0.js → studio-tr20m8g8.js} +669 -18
  113. package/dist/chunks/studio-tr20m8g8.js.map +9 -0
  114. package/dist/chunks/{studio-ygzav7z5.js → studio-v96qfnww.js} +5514 -4291
  115. package/dist/chunks/studio-v96qfnww.js.map +54 -0
  116. package/dist/chunks/studio-vcetc91k.js +659 -0
  117. package/dist/chunks/studio-vcetc91k.js.map +10 -0
  118. package/dist/chunks/{studio-n9zmpxjg.js → studio-vmymjefp.js} +2972 -4937
  119. package/dist/chunks/studio-vmymjefp.js.map +98 -0
  120. package/dist/chunks/{studio-bghqfmea.js → studio-wwseqyw0.js} +2 -2
  121. package/dist/chunks/studio-x9hmy5kr.js +390 -0
  122. package/dist/chunks/studio-x9hmy5kr.js.map +10 -0
  123. package/dist/chunks/studio-xa0ce0n7.js +335 -0
  124. package/dist/chunks/studio-xa0ce0n7.js.map +10 -0
  125. package/dist/chunks/studio-xm3dbxvn.js +378 -0
  126. package/dist/chunks/studio-xm3dbxvn.js.map +19 -0
  127. package/dist/chunks/{studio-nqw433kv.js → studio-xmvbhg31.js} +2 -2
  128. package/dist/chunks/studio-zv37yrfa.js +1054 -0
  129. package/dist/chunks/studio-zv37yrfa.js.map +13 -0
  130. package/dist/chunks/{ws-client-q7hkz3j9.js → ws-client-654ts66j.js} +6 -4
  131. package/dist/chunks/ws-client-654ts66j.js.map +11 -0
  132. package/dist/iframe-entry.js +668 -230
  133. package/dist/studio.js +23711 -35185
  134. package/index.html +35 -4685
  135. package/package.json +17 -15
  136. package/src/about/about-modal.ts +42 -11
  137. package/src/browse/library-commands.ts +240 -0
  138. package/src/browse/library-layouts.ts +369 -0
  139. package/src/browse/library-model.ts +396 -0
  140. package/src/browse/library-pane.ts +1049 -0
  141. package/src/browse/library-preview.ts +252 -0
  142. package/src/browse/library-source.ts +150 -0
  143. package/src/canvas/canvas-helpers.ts +33 -9
  144. package/src/canvas/canvas-live-render.ts +45 -29
  145. package/src/canvas/canvas-patcher.ts +151 -31
  146. package/src/canvas/canvas-perf.ts +46 -7
  147. package/src/canvas/canvas-render.ts +1092 -220
  148. package/src/canvas/canvas-surface.ts +240 -0
  149. package/src/canvas/canvas-utils.ts +1166 -124
  150. package/src/canvas/iframe-drop.ts +2 -1
  151. package/src/canvas/iframe-entry.ts +241 -4
  152. package/src/canvas/iframe-host.ts +1226 -143
  153. package/src/canvas/iframe-inline-edit.ts +31 -0
  154. package/src/canvas/iframe-interaction.ts +76 -16
  155. package/src/canvas/iframe-keys.ts +107 -45
  156. package/src/canvas/iframe-overlay.ts +38 -1
  157. package/src/canvas/iframe-protocol.ts +137 -2
  158. package/src/canvas/iframe-render.ts +70 -2
  159. package/src/canvas/iframe-slash.ts +6 -0
  160. package/src/canvas/path-mapping.ts +56 -6
  161. package/src/canvas/surface-registry.ts +292 -0
  162. package/src/collab/collab-commands.ts +244 -0
  163. package/src/collab/collab-session.ts +198 -32
  164. package/src/collab/collab-state.ts +28 -2
  165. package/src/collab/presence-chips.ts +100 -9
  166. package/src/commands/active-registry.ts +42 -0
  167. package/src/commands/app-commands.ts +176 -0
  168. package/src/commands/budget.ts +158 -0
  169. package/src/commands/command-args.ts +253 -0
  170. package/src/commands/context.ts +387 -0
  171. package/src/commands/defaults.ts +580 -0
  172. package/src/commands/keymap.ts +571 -0
  173. package/src/commands/levels.ts +345 -0
  174. package/src/commands/live-context.ts +193 -0
  175. package/src/commands/reference.ts +227 -0
  176. package/src/commands/registry.ts +293 -0
  177. package/src/component-props.ts +3 -2
  178. package/src/content/draft-state.ts +106 -0
  179. package/src/content/entry-commands.ts +282 -0
  180. package/src/content/entry-editor.ts +327 -0
  181. package/src/content/entry-fields.ts +116 -0
  182. package/src/content/entry-model.ts +175 -0
  183. package/src/editor/canvas-context-menu.ts +17 -11
  184. package/src/editor/canvas-slash-bridge.ts +11 -4
  185. package/src/editor/context-menu.ts +804 -204
  186. package/src/editor/convert-to-component.ts +31 -10
  187. package/src/editor/convert-to-repeater.ts +3 -2
  188. package/src/editor/inline-edit-apply.ts +5 -5
  189. package/src/editor/inline-edit.ts +86 -50
  190. package/src/editor/insert-zone-action.ts +1 -1
  191. package/src/editor/shortcuts.ts +1043 -329
  192. package/src/editor/slash-menu.ts +1 -0
  193. package/src/files/elements.ts +166 -0
  194. package/src/files/file-ops.ts +178 -31
  195. package/src/files/files.ts +898 -268
  196. package/src/files/fs-events.ts +21 -0
  197. package/src/files/media-meta.ts +203 -0
  198. package/src/files/media-paths.ts +141 -0
  199. package/src/files/media-upload.ts +7 -3
  200. package/src/files/media-usage.ts +189 -0
  201. package/src/github/github-auth.ts +235 -33
  202. package/src/github/github-publish.ts +80 -21
  203. package/src/grid/grid-controller.ts +152 -12
  204. package/src/grid/grid-layout.ts +225 -20
  205. package/src/grid/grid-open.ts +120 -0
  206. package/src/grid/grid-panel.ts +554 -39
  207. package/src/grid/grid-source.ts +27 -1
  208. package/src/grid/redirects-grid.ts +599 -0
  209. package/src/grid/redirects.ts +398 -0
  210. package/src/grid/sources/content-source.ts +108 -2
  211. package/src/i18n/i18n-commands.ts +322 -0
  212. package/src/new-project/add-repo-modal.ts +8 -10
  213. package/src/new-project/import-tab.ts +34 -9
  214. package/src/new-project/location-fields.ts +9 -1
  215. package/src/new-project/new-project-modal.ts +270 -262
  216. package/src/packages/jxsuite-update.ts +97 -48
  217. package/src/packages/pull-package-sync.ts +18 -9
  218. package/src/page-params.ts +2 -1
  219. package/src/panels/activity-bar.ts +152 -91
  220. package/src/panels/activity-panel.ts +481 -0
  221. package/src/panels/ai-chat/chat-markdown.ts +8 -3
  222. package/src/panels/ai-chat/chat-view.ts +245 -18
  223. package/src/panels/ai-chat/composer.ts +31 -5
  224. package/src/panels/ai-chat/sessions-view.ts +17 -10
  225. package/src/panels/ai-panel.ts +279 -120
  226. package/src/panels/block-action-bar.ts +1115 -224
  227. package/src/panels/bottom-dock.ts +337 -0
  228. package/src/panels/canvas-dnd-bridge.ts +37 -8
  229. package/src/panels/chat-panel.ts +29 -12
  230. package/src/panels/data-explorer.ts +254 -112
  231. package/src/panels/data-grid.ts +10 -4
  232. package/src/panels/dnd.ts +28 -51
  233. package/src/panels/editors.ts +434 -107
  234. package/src/panels/elements-panel.ts +66 -2
  235. package/src/panels/empty-state.ts +112 -0
  236. package/src/panels/events-panel.ts +596 -82
  237. package/src/panels/formula-workspace.ts +408 -218
  238. package/src/panels/frontmatter-fields.ts +51 -10
  239. package/src/panels/frontmatter-panel.ts +357 -79
  240. package/src/panels/git-panel.ts +375 -164
  241. package/src/panels/head-panel.ts +791 -85
  242. package/src/panels/i18n-panel.ts +592 -0
  243. package/src/panels/imports-panel.ts +95 -85
  244. package/src/panels/jump-bar.ts +584 -0
  245. package/src/panels/layers-panel.ts +1106 -328
  246. package/src/panels/left-panel.ts +109 -204
  247. package/src/panels/navigator-panels.ts +131 -0
  248. package/src/panels/overlays.ts +7 -2
  249. package/src/panels/pane-context.ts +1477 -0
  250. package/src/panels/pane-grid.ts +406 -0
  251. package/src/panels/panel-registry.ts +340 -0
  252. package/src/panels/panel-scheduler.ts +75 -0
  253. package/src/panels/problems-panel.ts +234 -0
  254. package/src/panels/properties-panel.ts +946 -1139
  255. package/src/panels/provenance.ts +258 -0
  256. package/src/panels/quick-search.ts +791 -122
  257. package/src/panels/right-panel.ts +228 -102
  258. package/src/panels/seo-modal.ts +395 -0
  259. package/src/panels/settings-pane.ts +203 -0
  260. package/src/panels/signals-panel.ts +354 -137
  261. package/src/panels/statement-editor.ts +41 -25
  262. package/src/panels/statusbar.ts +342 -106
  263. package/src/panels/style-panel.ts +974 -463
  264. package/src/panels/style-utils.ts +3 -3
  265. package/src/panels/stylebook-doc.ts +4 -2
  266. package/src/panels/stylebook-layers-panel.ts +3 -2
  267. package/src/panels/stylebook-panel.ts +51 -26
  268. package/src/panels/tab-strip.ts +1058 -78
  269. package/src/panels/target-line.ts +274 -0
  270. package/src/panels/toolbar.ts +631 -484
  271. package/src/panels/welcome-screen.ts +301 -198
  272. package/src/platform-errors.ts +26 -3
  273. package/src/platform.ts +96 -2
  274. package/src/platforms/cloud.ts +71 -8
  275. package/src/platforms/devserver.ts +92 -29
  276. package/src/publish/deploy-checklist.ts +383 -0
  277. package/src/publish/pages-service.ts +10 -1
  278. package/src/publish/publish-commands.ts +218 -0
  279. package/src/publish/publish-panel.ts +110 -16
  280. package/src/recent-projects.ts +3 -2
  281. package/src/services/a11y-report.ts +505 -0
  282. package/src/services/ai-models.ts +75 -2
  283. package/src/services/ai-project-tools.ts +29 -10
  284. package/src/services/ai-system-prompt.ts +53 -14
  285. package/src/services/ai-tools.ts +39 -3
  286. package/src/services/ai-writes.ts +154 -0
  287. package/src/services/announce.ts +97 -0
  288. package/src/services/automation.ts +424 -175
  289. package/src/services/cem-export.ts +9 -4
  290. package/src/services/clock.ts +61 -0
  291. package/src/services/document-assistant.ts +36 -8
  292. package/src/services/idle.ts +179 -0
  293. package/src/services/import-client.ts +26 -0
  294. package/src/services/monaco-buffer.ts +627 -0
  295. package/src/services/monaco-lazy.ts +36 -0
  296. package/src/services/notify.ts +271 -0
  297. package/src/services/profile.ts +155 -0
  298. package/src/services/references.ts +0 -0
  299. package/src/services/token-lint.ts +3 -2
  300. package/src/services/tool-executor.ts +53 -7
  301. package/src/services/trusted-types.ts +123 -0
  302. package/src/settings/contexts-section.ts +427 -0
  303. package/src/settings/contributed-section.ts +149 -11
  304. package/src/settings/css-vars-editor.ts +306 -349
  305. package/src/settings/defs-editor.ts +93 -34
  306. package/src/settings/dependencies-editor.ts +7 -7
  307. package/src/settings/extension-sections.ts +37 -5
  308. package/src/settings/general-settings.ts +204 -121
  309. package/src/settings/locales-section.ts +347 -0
  310. package/src/settings/preferences-accounts.ts +140 -0
  311. package/src/settings/preferences-dialog.ts +620 -0
  312. package/src/settings/preferences-keymap.ts +270 -0
  313. package/src/settings/project-sections.ts +260 -0
  314. package/src/settings/section-registry.ts +169 -0
  315. package/src/settings/settings-document.ts +385 -0
  316. package/src/shell.ts +1549 -0
  317. package/src/site-context.ts +45 -20
  318. package/src/state.ts +14 -275
  319. package/src/store.ts +88 -43
  320. package/src/studio.ts +790 -346
  321. package/src/style/live-preview.ts +36 -0
  322. package/src/style/project-styles.ts +349 -0
  323. package/src/style/token-ref.ts +123 -0
  324. package/src/tabs/patch-ops.ts +16 -4
  325. package/src/tabs/project-config.ts +416 -0
  326. package/src/tabs/selection.ts +323 -0
  327. package/src/tabs/tab.ts +182 -47
  328. package/src/tabs/transact.ts +209 -28
  329. package/src/types.ts +68 -13
  330. package/src/ui/ai-credentials-form.ts +39 -35
  331. package/src/ui/ai-managed-connect.ts +113 -0
  332. package/src/ui/dynamic-slot.ts +224 -81
  333. package/src/ui/expression-editor.ts +71 -63
  334. package/src/ui/field-input.ts +4 -6
  335. package/src/ui/field-row.ts +215 -38
  336. package/src/ui/form-controls.ts +194 -7
  337. package/src/ui/layers.ts +492 -69
  338. package/src/ui/media-picker.ts +115 -25
  339. package/src/ui/panel-resize.ts +109 -91
  340. package/src/ui/progress-modal.ts +131 -50
  341. package/src/ui/regions.ts +462 -0
  342. package/src/ui/schema-form.ts +229 -45
  343. package/src/ui/spectrum.ts +24 -8
  344. package/src/ui/timing.ts +49 -0
  345. package/src/ui/value-selector.ts +20 -0
  346. package/src/ui/value-source.ts +348 -0
  347. package/src/ui/virtual-window.ts +400 -0
  348. package/src/utils/edit-display.ts +3 -2
  349. package/src/utils/inherited-style.ts +67 -37
  350. package/src/view.ts +64 -78
  351. package/src/workspace/pane-derive.ts +1258 -0
  352. package/src/workspace/session.ts +317 -0
  353. package/src/workspace/workspace.ts +1345 -23
  354. package/styles/canvas.css +653 -0
  355. package/styles/forced-colors.css +106 -0
  356. package/styles/inspector.css +1454 -0
  357. package/styles/overlays.css +2704 -0
  358. package/styles/panels.css +1701 -0
  359. package/styles/shell.css +1423 -0
  360. package/styles/tokens.css +151 -0
  361. package/dist/chunks/Overlay.dev-cszx7yn5.js +0 -22
  362. package/dist/chunks/extension-sections-qct0gt8f.js +0 -531
  363. package/dist/chunks/extension-sections-qct0gt8f.js.map +0 -12
  364. package/dist/chunks/files-02zn5y24.js +0 -50
  365. package/dist/chunks/files-02zn5y24.js.map +0 -9
  366. package/dist/chunks/git-panel-mbb0y0hz.js +0 -28
  367. package/dist/chunks/git-panel-mbb0y0hz.js.map +0 -9
  368. package/dist/chunks/layers-panel-9qekmqk1.js +0 -26
  369. package/dist/chunks/layers-panel-9qekmqk1.js.map +0 -9
  370. package/dist/chunks/settings-modal-gj0hrah4.js +0 -29
  371. package/dist/chunks/settings-modal-gj0hrah4.js.map +0 -9
  372. package/dist/chunks/sp-overlay.dev-earn5as8.js +0 -17
  373. package/dist/chunks/sp-overlay.dev-earn5as8.js.map +0 -9
  374. package/dist/chunks/sp-popover.dev-frxtfejn.js +0 -8
  375. package/dist/chunks/sp-popover.dev-frxtfejn.js.map +0 -9
  376. package/dist/chunks/sp-progress-circle.dev-7tvw34h8.js +0 -14
  377. package/dist/chunks/sp-progress-circle.dev-7tvw34h8.js.map +0 -9
  378. package/dist/chunks/sp-tooltip.dev-a9fywesm.js +0 -21
  379. package/dist/chunks/sp-tooltip.dev-a9fywesm.js.map +0 -10
  380. package/dist/chunks/studio-4k0m4fmp.js.map +0 -13
  381. package/dist/chunks/studio-568nx4t5.js +0 -469
  382. package/dist/chunks/studio-568nx4t5.js.map +0 -14
  383. package/dist/chunks/studio-9smfzqhc.js +0 -19
  384. package/dist/chunks/studio-9smfzqhc.js.map +0 -10
  385. package/dist/chunks/studio-asvf0j9e.js +0 -429
  386. package/dist/chunks/studio-asvf0j9e.js.map +0 -13
  387. package/dist/chunks/studio-d77qm3pk.js +0 -3427
  388. package/dist/chunks/studio-d77qm3pk.js.map +0 -26
  389. package/dist/chunks/studio-j6gqyjf4.js +0 -1854
  390. package/dist/chunks/studio-j6gqyjf4.js.map +0 -18
  391. package/dist/chunks/studio-n9zmpxjg.js.map +0 -115
  392. package/dist/chunks/studio-qkvt37c0.js.map +0 -9
  393. package/dist/chunks/studio-rq0vxb19.js +0 -3744
  394. package/dist/chunks/studio-rq0vxb19.js.map +0 -22
  395. package/dist/chunks/studio-wd6ewdvd.js +0 -853
  396. package/dist/chunks/studio-wd6ewdvd.js.map +0 -12
  397. package/dist/chunks/studio-xq0t7n5t.js +0 -140
  398. package/dist/chunks/studio-xq0t7n5t.js.map +0 -10
  399. package/dist/chunks/studio-xze4zrs1.js.map +0 -10
  400. package/dist/chunks/studio-ygzav7z5.js.map +0 -46
  401. package/dist/chunks/studio-zmy7241e.js +0 -399
  402. package/dist/chunks/studio-zmy7241e.js.map +0 -12
  403. package/dist/chunks/ws-client-q7hkz3j9.js.map +0 -11
  404. package/src/browse/browse-modal.ts +0 -69
  405. package/src/browse/browse.ts +0 -904
  406. package/src/canvas/canvas-diff.ts +0 -216
  407. package/src/canvas/nested-site-style.ts +0 -58
  408. package/src/new-project/design-fields.ts +0 -264
  409. package/src/new-project/templates.ts +0 -61
  410. package/src/panels/tab-bar.ts +0 -548
  411. package/src/settings/settings-modal.ts +0 -203
  412. /package/dist/chunks/{adapter-z8fkmbmv.js.map → adapter-qfnesn5v.js.map} +0 -0
  413. /package/dist/chunks/{cf-settings-mmyv436d.js.map → cf-settings-73sh5z4p.js.map} +0 -0
  414. /package/dist/chunks/{format-host-2c188n41.js.map → format-host-mkwsmt8d.js.map} +0 -0
  415. /package/dist/chunks/{jx-validate-qx8degj7.js.map → jx-validate-v6qgknrs.js.map} +0 -0
  416. /package/dist/chunks/{studio-w7c6kvk7.js.map → studio-a7pzveq1.js.map} +0 -0
  417. /package/dist/chunks/{studio-hhj92gcr.js.map → studio-s519zc99.js.map} +0 -0
  418. /package/dist/chunks/{studio-bghqfmea.js.map → studio-wwseqyw0.js.map} +0 -0
  419. /package/dist/chunks/{studio-nqw433kv.js.map → studio-xmvbhg31.js.map} +0 -0
@@ -1,214 +1,463 @@
1
1
  /// <reference lib="dom" />
2
2
  /**
3
- * Automation hook — a small, gated scripting surface for driving Studio from a browser-automation
4
- * runner (see scripts/screenshots/ at the repo root). Installed as `window.__jxAutomation` only
5
- * when the page URL carries `?automation=1`; without the flag production behavior is untouched.
3
+ * Automation.ts — the scripting surface, as a PROJECTION of the app.
6
4
  *
7
- * The API mutates the same reactive state the real UI mutates (canvas mode, activity tab,
8
- * selection, inspector tab, function editor) instead of clicking through Spectrum shadow DOM, so
9
- * shot definitions stay stable across chrome refactors.
5
+ * Installed as `window.__jxAutomation` only when the page URL carries `?automation=1`. It exposes
6
+ * exactly three members (spec studio.md §13.5):
7
+ *
8
+ * - `run(id, args)` — **is** `registry.run(id, args)`, behind {@link isScriptable}. There is no
9
+ * second action table. A hand-maintained parallel list of what the app can do is what plan §2
10
+ * principle 1 defines as a defect, and this module used to be 39 entries of exactly that, 16 of
11
+ * them holding raw XPath into the shell and three of them matching RENDERED TEXT.
12
+ * - `seed(id, args)` — the {@link SEEDS} registry, under the **Remote Rule**: a seed may only write
13
+ * state whose real writer is a network or IPC boundary. It stands in for a remote, never for a
14
+ * user. {@link REFUSED_SEEDS} names what that excludes and why.
15
+ * - `probe` — read-only. {@link AutomationProbe.idle} (the predicate that replaced 115 sleeps),
16
+ * `state()` (the full `CommandContext`), `commands()`, `seeds()`, and `pointAt`/`revealPath`,
17
+ * which compose the app's own transforms and answer in top-document coordinates.
18
+ *
19
+ * **What is deliberately absent**, each a normative refusal in spec §13.5:
20
+ *
21
+ * 1. No method that takes a selector. If a script cannot say it in command ids and `JxPath`s, it
22
+ * cannot say it here.
23
+ * 2. No `setStatus`. Staging the word "Ready" over the status bar is lying to the reader; if a capture
24
+ * needs a calm status bar, the app has to BE calm.
25
+ * 3. No `toggle*`. A delta against unstated state is what silently inverted 23 manifest steps when the
26
+ * assistant's default flipped, and an agent calling `view.toggleAssistant` is guessing at state
27
+ * it cannot observe. {@link TOGGLE_ID} refuses them at runtime.
28
+ * 4. No compatibility shim. The predecessor carried `setRightTab`'s `if (tab === "assistant")`
29
+ * redirect, whose own comment said it existed "to keep the Screenshot-manifest verb working" — a
30
+ * pipeline artefact living in production code.
31
+ * 5. No presentation-state pokes. Automation mutates documents by running the commands a user runs, so
32
+ * every write goes through the transaction log.
10
33
  */
11
34
 
12
- import { renderOnly, updateUi } from "../store";
13
- import { setProjectState } from "../state";
35
+ import { render } from "../store";
14
36
  import { seedProjectList } from "../project-list";
15
- import { activeTab, closeAllTabs, workspace } from "../workspace/workspace";
16
- import type { ProjectListEntry } from "../types";
17
- import { applyPanelCollapse, view } from "../view";
18
- import { setEditZoom as applyEditZoomLevel } from "../canvas/canvas-utils";
37
+ import { activeTab } from "../workspace/workspace";
38
+ import { shell } from "../shell";
19
39
  import { collabState } from "../collab/collab-state";
40
+ import { canvasPointAt, revealCanvasPath } from "../canvas/iframe-host";
41
+ import { probeIdle } from "./idle";
42
+ import type { CanvasPoint } from "../canvas/iframe-host";
43
+ import type { IdleOptions } from "./idle";
44
+ import type { CommandContext } from "../commands/context";
45
+ import type { AnyCommand, CommandRegistry } from "../commands/registry";
20
46
  import type { PeerPresence } from "../collab/collab-state";
21
47
  import type { SeededAssistantMessage } from "../panels/ai-panel";
22
48
  import type { PagesDeploymentInfo } from "../publish/pages-service";
49
+ import type { GitBranchesResult, GitStatusResult, ProjectListEntry } from "../types";
50
+ import type { GitLogEntry } from "../shell";
23
51
  import type { JxPath } from "../state";
24
52
 
25
- /** Callbacks injected from studio.ts (module-local helpers or imports kept out of this module). */
53
+ /** Loosely-typed arguments, as they arrive from a manifest step, a palette prompt or a tool call. */
54
+ export type AutomationArgs = Record<string, unknown>;
55
+
56
+ /**
57
+ * What the hook needs that it cannot import.
58
+ *
59
+ * Three entries, and the shape is the point: everything the predecessor took by injection —
60
+ * `getCanvasMode`, `openBrowseModal`, `openSettingsModal`, `render`, `setCanvasMode`,
61
+ * `statusMessage` — was a capability being re-implemented here instead of being a command.
62
+ */
26
63
  export interface AutomationDeps {
27
- getCanvasMode: () => string;
28
- openBrowseModal: () => void;
29
- openConnectorGrid: (connection: string | undefined, table: string) => void;
30
- openNewProjectModal: () => void;
31
- openQuickSearchPalette: () => void;
32
- openSettingsModal: (section?: string) => void;
33
- render: () => void;
34
- renderActivityBar: () => void;
64
+ /** The registry `run()` projects. One per window, exactly as the bootstrap builds it. */
65
+ registry: CommandRegistry;
66
+ /** The assistant's transcript sink — stands in for the model stream. */
35
67
  seedAssistantMessages: (messages: SeededAssistantMessage[]) => void;
68
+ /** The publish panel's connected state — stands in for the Cloudflare Pages API. */
36
69
  seedPublishConnected: (options: { accountId?: string; deployment: PagesDeploymentInfo }) => void;
37
- setCanvasMode: (mode: string) => void;
38
- statusMessage: (text: string) => void;
39
70
  }
40
71
 
41
- export interface AutomationApi {
42
- editDef: (defName: string) => void;
43
- editFunction: (path: JxPath, eventKey: string) => void;
44
- openBrowse: () => void;
45
- openDataGrid: (options: { connection?: string; table: string }) => void;
46
- openNewProject: () => void;
47
- openQuickSearch: () => void;
48
- openSettings: (section?: string) => void;
49
- seedAssistant: (options: { messages: SeededAssistantMessage[] }) => void;
50
- seedCollab: (options: { peers: PeerPresence[] }) => void;
51
- seedPublish: (options: { accountId?: string; deployment: PagesDeploymentInfo }) => void;
52
- showWelcome: (options?: { projects?: ProjectListEntry[] }) => void;
53
- getState: () => {
54
- activeTabId: string | null;
55
- canvasMode: string;
56
- canvasStatus: string | null;
57
- leftTab: string;
58
- };
59
- select: (path: JxPath | null) => void;
60
- setActivity: (tab: string) => void;
61
- setCanvasMode: (mode: string) => void;
62
- setRightTab: (tab: string) => void;
63
- setStatus: (text: string) => void;
64
- setTheme: (color: string) => void;
65
- setZoom: (zoom: number) => void;
66
- setEditZoom: (zoom: number) => void;
67
- waitForCanvasReady: (timeoutMs?: number) => Promise<void>;
72
+ // ─── The idempotence rule ─────────────────────────────────────────────────────
73
+
74
+ /** `view.toggleAssistant` names a delta; `view.setAssistant` names a state. Only one is runnable. */
75
+ export const TOGGLE_ID = /\.toggle[A-Z]/;
76
+
77
+ /** The idempotent id a toggle should have become — printed so the fix is inside the refusal. */
78
+ export function setterFor(id: string): string {
79
+ return id.replace(/\.toggle([A-Z])/, (_match, initial: string) => `.set${initial}`);
68
80
  }
69
81
 
70
- /** The hook is opt-in per page load: only `?automation=1` installs it. */
71
- export function shouldInstallAutomation(search: string): boolean {
72
- return new URLSearchParams(search).get("automation") === "1";
82
+ /**
83
+ * Whether a command projects into `run()`.
84
+ *
85
+ * Derived, not declared: everything the registry holds is a capability with an id, a gate and a
86
+ * `run`, which is exactly what a script needs. The one exclusion is the idempotence rule, so the
87
+ * projection cannot drift from the registry the way a hand-kept allow-list would.
88
+ */
89
+ export function isScriptable(command: AnyCommand): boolean {
90
+ return !TOGGLE_ID.test(command.id);
73
91
  }
74
92
 
75
- export function createAutomationApi(deps: AutomationDeps): AutomationApi {
76
- return {
77
- editDef(defName: string) {
78
- updateUi("editingFunction", { defName, type: "def" });
79
- deps.render();
80
- },
81
- editFunction(path: JxPath, eventKey: string) {
82
- updateUi("editingFunction", { eventKey, path, type: "event" });
83
- deps.render();
84
- },
85
- openBrowse() {
86
- deps.openBrowseModal();
93
+ // ─── The registry-gap declaration ─────────────────────────────────────────────
94
+
95
+ /**
96
+ * How a manifest id the registry does not declare is going to stop existing.
97
+ *
98
+ * - `command` — a thing a user does, so it becomes a registry record. The value names who lands it.
99
+ * - `seed` — a remote's state, addressed through {@link AutomationApi.seed}.
100
+ * - `refused` — §13.3 says the app will never provide it; the manifest step is deleted.
101
+ */
102
+ export type GapDisposition = "command" | "seed" | "refused";
103
+
104
+ export interface ManifestId {
105
+ disposition: GapDisposition;
106
+ /** The phase that lands the record, or the sentence that refuses it. One line, for the error. */
107
+ note: string;
108
+ }
109
+
110
+ /**
111
+ * Ids the screenshot manifest still depends on and no registry declares. **No handlers, no
112
+ * behaviour.**
113
+ *
114
+ * The export name is the contract `scripts/check-shot-contract.ts` reads (`loadCommandTable` looks
115
+ * for `defaultCommandSet()`, `seedIds()` or `AUTOMATION_COMMANDS`), and it is all that is left of
116
+ * the 39-entry action table this module used to be: every `press`, every XPath, every rendered-text
117
+ * matcher and both label mirrors are gone, and `run()` never consults this map to decide what to DO
118
+ * — only to explain, when the registry refuses an id, which of the three fates that id has.
119
+ *
120
+ * This is a countdown, in the idiom of the checker's own `TOGGLE_DEBT`: it may only shrink. It went
121
+ * 39 → 8 when the manifest converted (S2), and 8 → 7 when `element.insertData` became `insert.data`
122
+ * (P5, `canvas/canvas-render.ts`). What left: every `seed` entry, now read off {@link seedIds} and
123
+ * answered from the live registry rather than from a hand-kept list; every `toggle*` entry, which
124
+ * {@link TOGGLE_ID} refuses before this map is ever consulted; and every `command` entry whose
125
+ * record has since landed.
126
+ *
127
+ * What is left is two kinds. Two `command` entries are the manifest's real registry gaps — each
128
+ * reached today by an `input` step clicking a hand-stamped region, and each of those steps carries
129
+ * an `unstable` hatch naming the phase that lands the record, so this map and that hatch count are
130
+ * the same debt. Five `refused` entries are NOT a countdown: they are §13.3's normative refusals,
131
+ * and they stay so that a caller reaching for one gets the reason instead of "unknown command".
132
+ */
133
+ export const AUTOMATION_COMMANDS: Readonly<Record<string, ManifestId>> = {
134
+ "file.contextMenu": {
135
+ disposition: "refused",
136
+ note: "opening a menu to press an item names a control; the ITEM is the command",
137
+ },
138
+ "layers.contextMenu": {
139
+ disposition: "refused",
140
+ note: "matched RENDERED TEXT, which §13's R1 forbids; P5.5 stamps data-jx-path on rows",
141
+ },
142
+ "media.browse": { disposition: "command", note: "P7.5 — media.browse" },
143
+ "project.showWelcome": {
144
+ disposition: "refused",
145
+ note: "cold start is a startup profile (?profile=fresh), not an action",
146
+ },
147
+ "settings.setSection": {
148
+ disposition: "refused",
149
+ note: 'mirrored the section registry\'s LABELS; say settings.open { section: "cssVars" }',
150
+ },
151
+ "view.setStatus": {
152
+ disposition: "refused",
153
+ note: "the status bar is not staged — if a capture needs a calm shell, the app must BE calm",
154
+ },
155
+ };
156
+
157
+ // ─── The seed registry, under the Remote Rule ─────────────────────────────────
158
+
159
+ /** One staged fixture, and the boundary whose job it is doing. */
160
+ export interface SeedDefinition {
161
+ id: string;
162
+ /** The network or IPC boundary this seed stands in for. The Remote Rule, written down per seed. */
163
+ boundary: string;
164
+ run: (args: AutomationArgs) => void | Promise<void>;
165
+ }
166
+
167
+ /**
168
+ * Writes a seed will not do, and the reason, quoted back at the caller.
169
+ *
170
+ * Every one of these was a method on the old surface. Each writes state a USER writes, so each is a
171
+ * COMMAND — and a fixture that stages it photographs a state no session could have reached.
172
+ */
173
+ export const REFUSED_SEEDS: Readonly<Record<string, string>> = {
174
+ openSettings: 'a user opens Settings — run("settings.open")',
175
+ select: 'a user selects a node — run("selection.set", { path })',
176
+ setActivity: 'a user picks a Navigator panel — run("navigator.showPanel", { panel })',
177
+ setRightTab: 'a user picks an Inspector tab — run("inspector.setTab", { tab })',
178
+ setStatus:
179
+ "the status bar reports what the app just did; staging it is the one lie §13.3 names outright",
180
+ setZoom: 'a user zooms — run("canvas.setZoom", { zoom })',
181
+ };
182
+
183
+ function arrayArg<T>(args: AutomationArgs, key: string): T[] {
184
+ const value = args[key];
185
+ if (value === undefined) {
186
+ return [];
187
+ }
188
+ if (!Array.isArray(value)) {
189
+ throw new TypeError(`seed argument "${key}" must be an array`);
190
+ }
191
+ return value as T[];
192
+ }
193
+
194
+ function requiredArg<T>(args: AutomationArgs, key: string): T {
195
+ const value = args[key];
196
+ if (value === null || typeof value !== "object") {
197
+ throw new TypeError(`seed argument "${key}" must be an object`);
198
+ }
199
+ return value as T;
200
+ }
201
+
202
+ function optionalString(args: AutomationArgs, key: string): string | undefined {
203
+ const value = args[key];
204
+ if (value === undefined) {
205
+ return undefined;
206
+ }
207
+ if (typeof value !== "string") {
208
+ throw new TypeError(`seed argument "${key}" must be a string`);
209
+ }
210
+ return value;
211
+ }
212
+
213
+ /**
214
+ * The declared seeds.
215
+ *
216
+ * Each one writes exactly what its named boundary would have written, and nothing else — the git
217
+ * seed does not also open the Source Control panel, because a user does that.
218
+ */
219
+ export function createSeeds(deps: AutomationDeps): SeedDefinition[] {
220
+ return [
221
+ {
222
+ boundary: "the assistant's model stream",
223
+ id: "seed.assistant",
224
+ run: (args) => {
225
+ deps.seedAssistantMessages(arrayArg<SeededAssistantMessage>(args, "messages"));
226
+ },
87
227
  },
88
- openDataGrid(options: { connection?: string; table: string }) {
89
- deps.openConnectorGrid(options.connection, options.table);
228
+ {
229
+ boundary: "the collaboration websocket (awareness + sync)",
230
+ id: "seed.collab",
231
+ run: (args) => {
232
+ const tab = activeTab.value;
233
+ if (!tab) {
234
+ return;
235
+ }
236
+ const state = collabState(tab);
237
+ state.status = "synced";
238
+ state.active = true;
239
+ const peers: PeerPresence[] = [];
240
+ for (const peer of arrayArg<PeerPresence>(args, "peers")) {
241
+ const peerState = { ...peer.state };
242
+ // A peer with no focused document is focused on THIS one — the presence overlay keys on it.
243
+ peerState.focusedPath ??= tab.documentPath;
244
+ peers.push({ ...peer, state: peerState });
245
+ }
246
+ state.peers = peers;
247
+ render();
248
+ },
90
249
  },
91
- openNewProject() {
92
- deps.openNewProjectModal();
250
+ {
251
+ boundary: "the Cloudflare Pages API",
252
+ id: "seed.publish",
253
+ run: (args) => {
254
+ const accountId = optionalString(args, "accountId");
255
+ deps.seedPublishConnected({
256
+ deployment: requiredArg<PagesDeploymentInfo>(args, "deployment"),
257
+ ...(accountId ? { accountId } : {}),
258
+ });
259
+ },
93
260
  },
94
- openQuickSearch() {
95
- deps.openQuickSearchPalette();
261
+ {
262
+ boundary: "the platform's git routes (`gitStatus` / `gitBranches` / `gitLog`)",
263
+ id: "seed.git",
264
+ run: (args) => {
265
+ // The working tree really is a remote here: the git-panel shot photographs whatever the
266
+ // Author happened to have dirty, because its project lives inside this repository.
267
+ shell.git.status = (args.status as GitStatusResult | undefined) ?? null;
268
+ shell.git.branches = (args.branches as GitBranchesResult | undefined) ?? null;
269
+ shell.git.logEntries = (args.log as GitLogEntry[] | undefined) ?? null;
270
+ shell.git.loading = false;
271
+ shell.git.error = null;
272
+ render();
273
+ },
96
274
  },
97
- openSettings(section?: string) {
98
- deps.openSettingsModal(section);
275
+ {
276
+ boundary: "the platform's recent-projects store (desktop IPC / cloud API)",
277
+ id: "seed.projectList",
278
+ run: (args) => {
279
+ seedProjectList(arrayArg<ProjectListEntry>(args, "projects"));
280
+ render();
281
+ },
99
282
  },
100
- seedAssistant(options: { messages: SeededAssistantMessage[] }) {
101
- deps.seedAssistantMessages(options.messages);
283
+ ];
284
+ }
285
+
286
+ /**
287
+ * Every declared seed id, read off {@link createSeeds} itself.
288
+ *
289
+ * `scripts/check-shot-contract.ts` needs to answer "is `seed.projectList` a real seed?" in a bare
290
+ * Bun process with no app. It could have read a hand-kept list — and a hand-kept list is precisely
291
+ * how `seed.git` and `seed.projectList` came to be missing from {@link AUTOMATION_COMMANDS} while
292
+ * both were shipping. Constructing the definitions is free: every seed's dependency is used inside
293
+ * its `run`, never while building it, so the no-op deps below can never be called.
294
+ */
295
+ export function seedIds(): string[] {
296
+ const inert = {
297
+ registry: undefined as unknown as CommandRegistry,
298
+ seedAssistantMessages: () => {},
299
+ seedPublishConnected: () => {},
300
+ } satisfies AutomationDeps;
301
+ return createSeeds(inert).map((seed) => seed.id);
302
+ }
303
+
304
+ // ─── The API ──────────────────────────────────────────────────────────────────
305
+
306
+ /** One command as a script sees it — the projection, not a second declaration. */
307
+ export interface ScriptableCommand {
308
+ id: string;
309
+ title: string;
310
+ /** Whether its own `enablement` predicate would let it run right now. */
311
+ enabled: boolean;
312
+ /** The `requires` sentence when it would not. */
313
+ requires?: string;
314
+ /** The command's own JSON Schema — the palette's prompt and the AI tool's parameters. */
315
+ args?: object;
316
+ }
317
+
318
+ /** What `pointAt` can address. Region ids join `path` when P3.8's region registry lands. */
319
+ export interface AutomationTarget {
320
+ path: JxPath;
321
+ }
322
+
323
+ /** Read-only questions. Nothing here writes document or presentation state. */
324
+ export interface AutomationProbe {
325
+ /**
326
+ * Resolve when Studio has settled; reject with `NotIdleError.blockedBy` naming what it is waiting
327
+ * on. See `services/idle.ts` — the rejection is the reason this exists.
328
+ */
329
+ idle: (options?: Pick<IdleOptions, "frames" | "timeoutMs">) => Promise<void>;
330
+ /** The full `CommandContext` — the same record every `when` predicate and AI tool gate reads. */
331
+ state: () => CommandContext;
332
+ /** Every command a script may run right now, with its gate already evaluated. */
333
+ commands: () => ScriptableCommand[];
334
+ /** The declared seed ids, with the boundary each stands in for. */
335
+ seeds: () => { id: string; boundary: string }[];
336
+ /** Where a node is on screen, in TOP-DOCUMENT coordinates. Measures; moves nothing. */
337
+ pointAt: (target: AutomationTarget) => Promise<CanvasPoint | null>;
338
+ /** Bring a node into view and answer where it landed — the app's own jump-to-node. */
339
+ revealPath: (path: JxPath) => Promise<CanvasPoint | null>;
340
+ }
341
+
342
+ export interface AutomationApi {
343
+ run: (id: string, args?: AutomationArgs) => Promise<void>;
344
+ seed: (id: string, args?: AutomationArgs) => Promise<void>;
345
+ probe: AutomationProbe;
346
+ }
347
+
348
+ /** Thrown when the surface refuses an id on principle rather than because it is unknown. */
349
+ export class AutomationRefusedError extends Error {
350
+ readonly id: string;
351
+
352
+ constructor(id: string, reason: string) {
353
+ super(`automation refuses "${id}" — ${reason}`);
354
+ this.name = "AutomationRefusedError";
355
+ this.id = id;
356
+ }
357
+ }
358
+
359
+ /** The projection: every visible, scriptable command with its gate already evaluated. */
360
+ export function scriptableCommands(registry: CommandRegistry): ScriptableCommand[] {
361
+ const projected: ScriptableCommand[] = [];
362
+ for (const command of registry.visible()) {
363
+ if (!isScriptable(command)) {
364
+ continue;
365
+ }
366
+ const requires = registry.disabledReason(command.id);
367
+ const entry: ScriptableCommand = {
368
+ enabled: requires === undefined,
369
+ id: command.id,
370
+ title: command.title,
371
+ };
372
+ if (command.args) {
373
+ entry.args = command.args;
374
+ }
375
+ if (requires !== undefined) {
376
+ entry.requires = requires;
377
+ }
378
+ projected.push(entry);
379
+ }
380
+ return projected;
381
+ }
382
+
383
+ /** Why an id the registry does not declare is not runnable — the countdown, said out loud. */
384
+ function unknownCommandMessage(
385
+ id: string,
386
+ registry: CommandRegistry,
387
+ seeds: ReadonlySet<string>,
388
+ ): string {
389
+ // Derived from the live seed registry, not from a hand-kept `disposition: "seed"` list: a seed
390
+ // Added next week answers this question without anyone remembering to write it down twice.
391
+ if (seeds.has(id)) {
392
+ return `"${id}" is a seed, not a command — call seed("${id}", …)`;
393
+ }
394
+ const gap = AUTOMATION_COMMANDS[id];
395
+ if (gap?.disposition === "refused") {
396
+ return `"${id}" is refused by spec §13.5: ${gap.note}`;
397
+ }
398
+ if (gap) {
399
+ return `"${id}" has no command record yet (${gap.note}) — __jxAutomation.run projects the registry and nothing else`;
400
+ }
401
+ const known = registry
402
+ .list()
403
+ .filter((command) => isScriptable(command))
404
+ .map((command) => command.id);
405
+ return `unknown command "${id}" — the registry declares ${known.length} scriptable id(s)`;
406
+ }
407
+
408
+ export function createAutomationApi(deps: AutomationDeps): AutomationApi {
409
+ const seeds = new Map(createSeeds(deps).map((seed) => [seed.id, seed]));
410
+ const seedIdSet = new Set(seeds.keys());
411
+
412
+ return {
413
+ probe: {
414
+ commands: () => scriptableCommands(deps.registry),
415
+ idle: (options = {}) => probeIdle(options),
416
+ pointAt: (target) => canvasPointAt(target.path),
417
+ revealPath: (path) => revealCanvasPath(path),
418
+ seeds: () => [...seeds.values()].map((seed) => ({ boundary: seed.boundary, id: seed.id })),
419
+ state: () => deps.registry.context(),
102
420
  },
103
- seedCollab(options: { peers: PeerPresence[] }) {
104
- // Stage a live co-editing session on the active tab: the toolbar's presence chips and the
105
- // Canvas peer-selection boxes both read this same reactive store.
106
- const tab = activeTab.value;
107
- if (!tab) {
108
- return;
421
+ async run(id, args = {}) {
422
+ if (TOGGLE_ID.test(id)) {
423
+ throw new AutomationRefusedError(
424
+ id,
425
+ `it names a delta against state the caller cannot observe; call "${setterFor(id)}"`,
426
+ );
109
427
  }
110
- const state = collabState(tab);
111
- state.status = "synced";
112
- state.active = true;
113
- state.peers = options.peers.map((peer) => ({
114
- ...peer,
115
- state: {
116
- ...peer.state,
117
- focusedPath: peer.state.focusedPath ?? tab.documentPath,
118
- },
119
- }));
120
- deps.render();
121
- },
122
- seedPublish(options: { accountId?: string; deployment: PagesDeploymentInfo }) {
123
- deps.seedPublishConnected(options);
124
- },
125
- showWelcome(options?: { projects?: ProjectListEntry[] }) {
126
- // The devserver platform auto-opens the repo-root project on boot; the welcome
127
- // Screen only renders with no project and no tabs, so stage that state directly.
128
- // A staged catalogue replaces the real one (screenshots must not leak local paths).
129
- closeAllTabs();
130
- setProjectState(null);
131
- if (options?.projects) {
132
- seedProjectList(options.projects);
428
+ const { registry } = deps;
429
+ if (!registry.get(id)) {
430
+ throw new Error(unknownCommandMessage(id, registry, seedIdSet));
133
431
  }
134
- view.chatPanelCollapsed = true;
135
- applyPanelCollapse();
136
- deps.render();
137
- },
138
- getState() {
139
- return {
140
- activeTabId: workspace.activeTabId,
141
- canvasMode: deps.getCanvasMode(),
142
- canvasStatus: activeTab.value?.session.canvas.status ?? null,
143
- leftTab: view.leftTab,
144
- };
432
+ // Refusal propagates as `CommandUnavailableError`: a step asking for a state the app refuses
433
+ // Must FAIL, because that step is lying about what the app can be.
434
+ await registry.run(id, args);
145
435
  },
146
- select(path: JxPath | null) {
147
- const tab = activeTab.value;
148
- if (tab) {
149
- tab.session.selection = path;
436
+ async seed(id, args = {}) {
437
+ const bare = id.startsWith("seed.") ? id.slice(5) : id;
438
+ const refusal = REFUSED_SEEDS[bare];
439
+ if (refusal) {
440
+ throw new AutomationRefusedError(
441
+ id,
442
+ `a seed stands in for a remote, never for a user: ${refusal}`,
443
+ );
150
444
  }
151
- deps.render();
152
- },
153
- setActivity(tab: string) {
154
- view.leftTab = tab;
155
- view.leftPanelCollapsed = false;
156
- applyPanelCollapse();
157
- renderOnly("leftPanel");
158
- deps.renderActivityBar();
159
- },
160
- setCanvasMode(mode: string) {
161
- deps.setCanvasMode(mode);
162
- deps.render();
163
- },
164
- setRightTab(tab: string) {
165
- // "assistant" now lives in the persistent chat sidebar, not the right panel — keep the
166
- // Screenshot-manifest verb working by opening that sidebar instead.
167
- if (tab === "assistant") {
168
- view.chatPanelCollapsed = false;
169
- applyPanelCollapse();
170
- renderOnly("chatPanel");
171
- deps.render();
172
- return;
445
+ const seed = seeds.get(id.startsWith("seed.") ? id : `seed.${id}`);
446
+ if (!seed) {
447
+ throw new Error(
448
+ `unknown seed "${id}" — declared seeds are ${[...seeds.keys()].join(", ")}`,
449
+ );
173
450
  }
174
- updateUi("rightTab", tab);
175
- deps.render();
176
- },
177
- setStatus(text: string) {
178
- deps.statusMessage(text);
179
- },
180
- setTheme(color: string) {
181
- document.querySelector("sp-theme")?.setAttribute("color", color);
182
- },
183
- setZoom(zoom: number) {
184
- updateUi("zoom", zoom);
185
- deps.render();
186
- },
187
- setEditZoom(zoom: number) {
188
- // Deliberately NOT deps.render(): live edit zoom must never re-render the canvas (it would
189
- // Rebuild the iframe DOM) — setEditZoom applies bare style writes, matching production paths.
190
- applyEditZoomLevel(zoom);
191
- },
192
- waitForCanvasReady(timeoutMs = 30_000) {
193
- return new Promise((resolve, reject) => {
194
- const started = Date.now();
195
- const tick = () => {
196
- if (activeTab.value?.session.canvas.status === "ready") {
197
- resolve();
198
- return;
199
- }
200
- if (Date.now() - started > timeoutMs) {
201
- reject(new Error(`canvas not ready after ${timeoutMs}ms`));
202
- return;
203
- }
204
- setTimeout(tick, 50);
205
- };
206
- tick();
207
- });
451
+ await seed.run(args);
208
452
  },
209
453
  };
210
454
  }
211
455
 
456
+ /** The hook is opt-in per page load: only `?automation=1` installs it. */
457
+ export function shouldInstallAutomation(search: string): boolean {
458
+ return new URLSearchParams(search).get("automation") === "1";
459
+ }
460
+
212
461
  /**
213
462
  * Install the hook on globalThis when the current URL opts in. Returns whether it installed, so
214
463
  * callers (and tests) can assert the gate.
@@ -1,6 +1,11 @@
1
1
  /// <reference lib="dom" />
2
2
  import type { CemParameter, JxMutableNode, JxStateObject } from "@jxsuite/schema/types";
3
- import { isFunctionDef, isJsonObject } from "@jxsuite/schema/guards";
3
+ import {
4
+ displayTagName,
5
+ isFunctionDef,
6
+ isJsonObject,
7
+ isPrivateStateKey,
8
+ } from "@jxsuite/schema/guards";
4
9
 
5
10
  /** Collect slot elements from the document tree. Whitespace-only names count as unnamed. */
6
11
  export function collectSlots(node?: JxMutableNode | null, slots: string[] = []) {
@@ -58,7 +63,7 @@ export function exportCemManifest(
58
63
  const { defCategory, normParam, collectCssParts } = helpers;
59
64
  const doc = S.document;
60
65
  const { tagName } = doc;
61
- if (!tagName || !tagName.includes("-")) {
66
+ if (!tagName || !displayTagName(tagName).includes("-")) {
62
67
  return;
63
68
  }
64
69
 
@@ -69,9 +74,9 @@ export function exportCemManifest(
69
74
  const seenEvents = new Set<string>();
70
75
 
71
76
  for (const [key, d] of Object.entries(state)) {
72
- if (key.startsWith("#")) {
77
+ if (isPrivateStateKey(key)) {
73
78
  continue;
74
- } // Private
79
+ } // Private (spec.md §5.6)
75
80
 
76
81
  const cat = defCategory(d);
77
82