@jxsuite/studio 8.1.0 → 10.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 (266) hide show
  1. package/dist/chunks/{2020-dg097k3e.js → 2020-j56903f1.js} +3 -3
  2. package/dist/chunks/adapter-wc5zvg9a.js +4 -0
  3. package/dist/chunks/add-new-flow-snx1s4b0.js +4 -0
  4. package/dist/chunks/cf-account-picker-xyqw0jex.js +4 -0
  5. package/dist/chunks/cf-settings-hp1pjm0n.js +4 -0
  6. package/dist/chunks/color-04361ttk.js +4 -0
  7. package/dist/chunks/{color-area-vh9cew51.js → color-area-h8dbasp1.js} +2 -2
  8. package/dist/chunks/{color-field-e78f16bs.js → color-field-qv5r2b16.js} +3 -3
  9. package/dist/chunks/{color-slider-fg4yhvk6.js → color-slider-nz2mrzky.js} +2 -2
  10. package/dist/chunks/components-nkwphrx3.js +4 -0
  11. package/dist/chunks/content-source-6a253wmk.js +4 -0
  12. package/dist/chunks/{convert-to-repeater-grnmea6c.js → convert-to-repeater-fhdwyg14.js} +3 -3
  13. package/dist/chunks/editor-wqrc68ca.js +4 -0
  14. package/dist/chunks/entry-commands-nef0wrmp.js +4 -0
  15. package/dist/chunks/{extension-sections-rp7fkega.js → extension-sections-8ez2rrk8.js} +3 -3
  16. package/dist/chunks/file-ops-y3w71k1n.js +4 -0
  17. package/dist/chunks/files-de90apn9.js +4 -0
  18. package/dist/chunks/format-host-zfjz4rzm.js +4 -0
  19. package/dist/chunks/fs-events-77c0mr0x.js +4 -0
  20. package/dist/chunks/grid-open-4n4fjfpf.js +4 -0
  21. package/dist/chunks/{grid-view-x26rfpz1.js → grid-view-ka55z38f.js} +10 -10
  22. package/dist/chunks/{iframe-entry-pkgmcgp0.js → iframe-entry-2x9bn5ng.js} +4 -4
  23. package/dist/chunks/{iframe-entry-02wzcyvy.js → iframe-entry-btck0e9s.js} +4 -4
  24. package/dist/chunks/iframe-host-na2mywz4.js +4 -0
  25. package/dist/chunks/index-ewmgdb7c.js +4 -0
  26. package/dist/chunks/{index-203h71wy.js → index-qb98xhse.js} +3 -3
  27. package/dist/chunks/{javascript-ww5pv5ae.js → javascript-ces13wny.js} +2 -2
  28. package/dist/chunks/{jsonMode-vwbdwwq3.js → jsonMode-6md16d4v.js} +6 -6
  29. package/dist/chunks/jx-validate-b309c020.js +4 -0
  30. package/dist/chunks/layers-8rn8y6qv.js +4 -0
  31. package/dist/chunks/layers-panel-eyah7h9z.js +4 -0
  32. package/dist/chunks/lit-html-wyqw04y2.js +4 -0
  33. package/dist/chunks/media-library-gs3np47y.js +4 -0
  34. package/dist/chunks/media-picker-tt7dk4tb.js +4 -0
  35. package/dist/chunks/{monaco-binding-63tqze27.js → monaco-binding-p8j80s4w.js} +3 -3
  36. package/dist/chunks/monaco-buffer-wsf6cmnq.js +4 -0
  37. package/dist/chunks/{monaco-setup-zyyynnj8.js → monaco-setup-wasktzfg.js} +3 -3
  38. package/dist/chunks/notify-qwst108r.js +4 -0
  39. package/dist/chunks/parse-n8dfxnqf.js +4 -0
  40. package/dist/chunks/platform-c16mqvmp.js +4 -0
  41. package/dist/chunks/publish-panel-d34bhvwj.js +4 -0
  42. package/dist/chunks/{publish-quick-ajzdhhs6.js → publish-quick-vr695j26.js} +3 -3
  43. package/dist/chunks/publish-workspace-2yr60mt4.js +4 -0
  44. package/dist/chunks/quick-search-e6xsdv0d.js +4 -0
  45. package/dist/chunks/references-gsj6trdv.js +4 -0
  46. package/dist/chunks/right-panel-33jv1knc.js +4 -0
  47. package/dist/chunks/source-control-bj9bejjv.js +4 -0
  48. package/dist/chunks/studio-03mqh8ax.js +5 -0
  49. package/dist/chunks/{studio-957jg8g3.js → studio-06j4en5h.js} +4 -4
  50. package/dist/chunks/studio-0ydxv2mr.js +7 -0
  51. package/dist/chunks/{studio-v2ek69cb.js → studio-1e8dw2w2.js} +4 -4
  52. package/dist/chunks/studio-2hsqbq7b.js +7 -0
  53. package/dist/chunks/studio-2j86y34y.js +6 -0
  54. package/dist/chunks/studio-2ks6ka4g.js +5 -0
  55. package/dist/chunks/studio-34jrz12k.js +5 -0
  56. package/dist/chunks/{studio-1meqbxns.js → studio-35vrtpdq.js} +4 -4
  57. package/dist/chunks/studio-38g0jdz7.js +5 -0
  58. package/dist/chunks/{studio-25a5by3h.js → studio-396srjbk.js} +4 -4
  59. package/dist/chunks/{studio-zm7dnr9g.js → studio-3c8pp7pb.js} +4 -4
  60. package/dist/chunks/{studio-x3ge4rts.js → studio-3v5cagy9.js} +4 -4
  61. package/dist/chunks/studio-3w35qvay.js +5 -0
  62. package/dist/chunks/studio-3zge48z4.js +7 -0
  63. package/dist/chunks/studio-45gyxpcq.js +5 -0
  64. package/dist/chunks/{studio-hw60n5dh.js → studio-4kzbrr5t.js} +3 -3
  65. package/dist/chunks/{studio-533py3yy.js → studio-5tzgq6fx.js} +4 -4
  66. package/dist/chunks/studio-5wzdy1jc.js +16 -0
  67. package/dist/chunks/studio-732mw0g8.js +5 -0
  68. package/dist/chunks/{studio-3q6n9qvq.js → studio-771k2n12.js} +4 -4
  69. package/dist/chunks/{studio-z42kysg7.js → studio-7dzyz9n8.js} +137 -137
  70. package/dist/chunks/{studio-fqs1a22a.js → studio-7xag4na8.js} +4 -4
  71. package/dist/chunks/{studio-jqrg9xxg.js → studio-881z1xrx.js} +4 -4
  72. package/dist/chunks/studio-88bmt9rh.js +5 -0
  73. package/dist/chunks/{studio-8nyw10dp.js → studio-9b7177jq.js} +4 -4
  74. package/dist/chunks/{studio-nm9qev4p.js → studio-9cxmp61c.js} +5 -5
  75. package/dist/chunks/{studio-4dsdnan2.js → studio-9dz76yqc.js} +5 -5
  76. package/dist/chunks/{studio-a6x058ew.js → studio-agyvzfm4.js} +4 -4
  77. package/dist/chunks/{studio-2b8443ms.js → studio-ajw4k8hp.js} +3 -3
  78. package/dist/chunks/studio-c1pxszat.js +5 -0
  79. package/dist/chunks/studio-cpkamjdn.js +14 -0
  80. package/dist/chunks/studio-d038kqbs.js +5 -0
  81. package/dist/chunks/studio-d83m0wyq.js +5 -0
  82. package/dist/chunks/studio-dbxv7w4s.js +5 -0
  83. package/dist/chunks/studio-de1s2x9v.js +5 -0
  84. package/dist/chunks/{studio-94ppxeqy.js → studio-dx73md4n.js} +8 -8
  85. package/dist/chunks/studio-ekgtrndg.js +5 -0
  86. package/dist/chunks/studio-epenqmvk.js +7 -0
  87. package/dist/chunks/studio-et2q4s8n.js +5 -0
  88. package/dist/chunks/{studio-txkx6586.js → studio-ewarnzm4.js} +7 -7
  89. package/dist/chunks/{studio-bf0rgyf7.js → studio-fy7jzj9j.js} +4 -4
  90. package/dist/chunks/{studio-332hesdk.js → studio-gr36vnte.js} +4 -4
  91. package/dist/chunks/{studio-rdnezzkd.js → studio-gwrhcrss.js} +5 -5
  92. package/dist/chunks/{studio-9wyqyfqy.js → studio-h310mbqm.js} +4 -4
  93. package/dist/chunks/{studio-zht0x0v3.js → studio-h5tpz5ke.js} +4 -4
  94. package/dist/chunks/studio-h7weqf2q.js +5 -0
  95. package/dist/chunks/{studio-1gdfsr49.js → studio-hcsba201.js} +7 -7
  96. package/dist/chunks/studio-hwejq03c.js +5 -0
  97. package/dist/chunks/{studio-wcakp5bt.js → studio-hy4ye0dx.js} +7 -7
  98. package/dist/chunks/{studio-02phtmqn.js → studio-jttyqrmh.js} +4 -4
  99. package/dist/chunks/{studio-j5jqvyn3.js → studio-jw0q5j6k.js} +4 -4
  100. package/dist/chunks/studio-k3a8rp3d.js +5 -0
  101. package/dist/chunks/{studio-hnx1h1pr.js → studio-k3bcy02e.js} +4 -4
  102. package/dist/chunks/studio-m48v13s3.js +4 -0
  103. package/dist/chunks/{studio-bqgs90r8.js → studio-ndskgqef.js} +4 -4
  104. package/dist/chunks/studio-p1n2xjda.js +5 -0
  105. package/dist/chunks/studio-pgm83a2f.js +6 -0
  106. package/dist/chunks/studio-pqqq6v85.js +5 -0
  107. package/dist/chunks/studio-psh8mwnw.js +4 -0
  108. package/dist/chunks/{studio-cpdzrdgp.js → studio-r17asgs0.js} +4 -4
  109. package/dist/chunks/{studio-6vrxzkba.js → studio-ry81j4q1.js} +4 -4
  110. package/dist/chunks/studio-rzg8hnkf.js +5 -0
  111. package/dist/chunks/{studio-nsv7g1qn.js → studio-s0fg2wav.js} +4 -4
  112. package/dist/chunks/{studio-v0gsjav0.js → studio-tj2s0tks.js} +4 -4
  113. package/dist/chunks/studio-tyw7tk0x.js +5 -0
  114. package/dist/chunks/studio-v9ywz2se.js +6 -0
  115. package/dist/chunks/{studio-dmn7gpba.js → studio-vswcpzk7.js} +5 -5
  116. package/dist/chunks/studio-vzgmyt0v.js +5 -0
  117. package/dist/chunks/{studio-2dtc90zs.js → studio-wxdcd42g.js} +6 -6
  118. package/dist/chunks/{studio-c5dj3dzk.js → studio-wxhy1yh7.js} +4 -4
  119. package/dist/chunks/studio-x08a3m0a.js +5 -0
  120. package/dist/chunks/studio-x3mjr5cc.js +5 -0
  121. package/dist/chunks/{studio-xddgbwhe.js → studio-x97ay0v4.js} +4 -4
  122. package/dist/chunks/{studio-mmqr9awv.js → studio-xdpz9rpe.js} +33 -33
  123. package/dist/chunks/studio-xh683qzy.js +5 -0
  124. package/dist/chunks/{studio-hxq1ebp0.js → studio-xkxetm65.js} +6 -6
  125. package/dist/chunks/{studio-73wz63nn.js → studio-y26hx9re.js} +4 -4
  126. package/dist/chunks/{studio-kdseew9m.js → studio-yg3djpnm.js} +4 -4
  127. package/dist/chunks/studio-yx8hzqja.js +5 -0
  128. package/dist/chunks/studio-zg16eeq1.js +5 -0
  129. package/dist/chunks/studio-zgx7svjd.js +9 -0
  130. package/dist/chunks/studio-zkq3r91p.js +5 -0
  131. package/dist/chunks/{toast-host-1yg5ar8d.js → toast-host-g3r2s0kn.js} +2 -2
  132. package/dist/chunks/toast-w11qpkyz.js +4 -0
  133. package/dist/chunks/{tsMode-vra2hh7y.js → tsMode-h6tkz0ea.js} +5 -5
  134. package/dist/chunks/workspace-home-zkkrxq3r.js +4 -0
  135. package/dist/chunks/workspace-tdqqjqaa.js +4 -0
  136. package/dist/chunks/{ws-client-72x5n99j.js → ws-client-e951my95.js} +3 -3
  137. package/dist/iframe-entry.js +13 -13
  138. package/dist/manifest.json +77 -77
  139. package/dist/studio.js +91 -92
  140. package/package.json +11 -12
  141. package/src/canvas/iframe-editable-root.ts +32 -2
  142. package/src/collab/collab-session.ts +6 -2
  143. package/src/commands/context.ts +4 -0
  144. package/src/commands/defaults.ts +2 -0
  145. package/src/commands/live-context.ts +2 -0
  146. package/src/files/files.ts +6 -3
  147. package/src/format/format-host.ts +1 -1
  148. package/src/github/github-auth-status.ts +2 -2
  149. package/src/media/media-library.ts +73 -3
  150. package/src/new-project/new-project-modal.ts +1 -4
  151. package/src/packages/ensure-deps.ts +5 -5
  152. package/src/packages/pull-package-sync.ts +4 -4
  153. package/src/panels/activity-panel.ts +1 -1
  154. package/src/panels/ai-panel.ts +27 -34
  155. package/src/platform.ts +20 -0
  156. package/src/platforms/cloud.ts +48 -34
  157. package/src/platforms/devserver.ts +2 -120
  158. package/src/services/ai-ask.ts +29 -171
  159. package/src/services/ai-command-tools.ts +40 -50
  160. package/src/services/ai-import-tools.ts +4 -5
  161. package/src/services/ai-project-tools.ts +13 -3
  162. package/src/services/ai-tools.ts +26 -564
  163. package/src/services/ai-writes.ts +84 -2
  164. package/src/services/document-assistant.ts +29 -58
  165. package/src/services/harness/assistant-tools.ts +68 -0
  166. package/src/services/harness/interaction-store.ts +118 -0
  167. package/src/services/harness/studio-documents.ts +105 -0
  168. package/src/services/harness/turn-hooks.ts +1 -6
  169. package/src/services/project-adoption.ts +8 -16
  170. package/src/services/tool-executor.ts +25 -22
  171. package/src/settings/extension-commands.ts +1 -1
  172. package/src/settings/extension-rows.ts +1 -6
  173. package/src/settings/extensions-section.ts +3 -6
  174. package/src/state.ts +3 -13
  175. package/src/surfaces/block-action-bar.json +1 -3
  176. package/src/surfaces/files-panel.json +12 -0
  177. package/src/surfaces/files-panel.ts +5 -2
  178. package/src/surfaces/media-library.json +35 -0
  179. package/src/surfaces/media-library.ts +2 -0
  180. package/src/surfaces/menu.ts +62 -1
  181. package/src/surfaces/settings-packages.ts +1 -1
  182. package/src/surfaces/welcome.ts +4 -1
  183. package/src/tabs/selection.ts +14 -109
  184. package/src/tabs/tab.ts +10 -1
  185. package/src/tabs/transact.ts +316 -124
  186. package/src/types.ts +23 -16
  187. package/src/ui/layers.ts +1 -1
  188. package/src/ui/progress-modal.ts +3 -3
  189. package/dist/chunks/adapter-tck81zft.js +0 -4
  190. package/dist/chunks/add-new-flow-a1w0bnm6.js +0 -4
  191. package/dist/chunks/cf-account-picker-ce3kp3x1.js +0 -4
  192. package/dist/chunks/cf-settings-7ye6x7g2.js +0 -4
  193. package/dist/chunks/color-4gj49xq6.js +0 -4
  194. package/dist/chunks/components-qce0v93f.js +0 -4
  195. package/dist/chunks/content-source-jjx8kgzr.js +0 -4
  196. package/dist/chunks/editor-bkja5dbv.js +0 -4
  197. package/dist/chunks/entry-commands-1wh0btvg.js +0 -4
  198. package/dist/chunks/file-ops-6pe4z7an.js +0 -4
  199. package/dist/chunks/files-ybpp3j38.js +0 -4
  200. package/dist/chunks/format-host-3mtbhxk1.js +0 -4
  201. package/dist/chunks/fs-events-xfanfxds.js +0 -4
  202. package/dist/chunks/grid-open-dg5qmnaj.js +0 -4
  203. package/dist/chunks/iframe-host-j2bk2xkc.js +0 -4
  204. package/dist/chunks/index-fkrm7pdk.js +0 -4
  205. package/dist/chunks/jx-validate-w7v32f6a.js +0 -4
  206. package/dist/chunks/layers-g7j732cm.js +0 -4
  207. package/dist/chunks/layers-panel-gjrsxcck.js +0 -4
  208. package/dist/chunks/lit-html-jdfz9rhc.js +0 -4
  209. package/dist/chunks/media-library-9j0v4wmk.js +0 -4
  210. package/dist/chunks/media-picker-cpyyt2m0.js +0 -4
  211. package/dist/chunks/monaco-buffer-9wcme176.js +0 -4
  212. package/dist/chunks/notify-rahnnnv4.js +0 -4
  213. package/dist/chunks/parse-v77xtwv7.js +0 -4
  214. package/dist/chunks/platform-b2aqv7v5.js +0 -4
  215. package/dist/chunks/publish-panel-4yk2461b.js +0 -4
  216. package/dist/chunks/publish-workspace-mxestzcf.js +0 -4
  217. package/dist/chunks/quick-search-9g6z65b0.js +0 -4
  218. package/dist/chunks/references-c26enpxs.js +0 -4
  219. package/dist/chunks/right-panel-9d0e0yg7.js +0 -4
  220. package/dist/chunks/source-control-xyn3a8ds.js +0 -4
  221. package/dist/chunks/studio-0khd0xdw.js +0 -5
  222. package/dist/chunks/studio-1fvzq9vs.js +0 -5
  223. package/dist/chunks/studio-1yhz0s8p.js +0 -5
  224. package/dist/chunks/studio-2c2mjyzj.js +0 -6
  225. package/dist/chunks/studio-3pndhat0.js +0 -9
  226. package/dist/chunks/studio-4fjqdzrb.js +0 -6
  227. package/dist/chunks/studio-5abge3wy.js +0 -5
  228. package/dist/chunks/studio-74nzv280.js +0 -5
  229. package/dist/chunks/studio-7cg75gtj.js +0 -14
  230. package/dist/chunks/studio-89cwmnae.js +0 -5
  231. package/dist/chunks/studio-8m1t83kn.js +0 -7
  232. package/dist/chunks/studio-a3evp7f6.js +0 -6
  233. package/dist/chunks/studio-ana67wax.js +0 -5
  234. package/dist/chunks/studio-b4xhpcwy.js +0 -4
  235. package/dist/chunks/studio-c4rcaftm.js +0 -5
  236. package/dist/chunks/studio-cgjt1zce.js +0 -5
  237. package/dist/chunks/studio-cq87hree.js +0 -5
  238. package/dist/chunks/studio-d140sy8e.js +0 -5
  239. package/dist/chunks/studio-e1g64q43.js +0 -5
  240. package/dist/chunks/studio-e8pzjjhf.js +0 -5
  241. package/dist/chunks/studio-f8kz3krj.js +0 -7
  242. package/dist/chunks/studio-fw3jp9m7.js +0 -5
  243. package/dist/chunks/studio-g6ysm05p.js +0 -7
  244. package/dist/chunks/studio-gmhd0j3f.js +0 -5
  245. package/dist/chunks/studio-gqpty626.js +0 -5
  246. package/dist/chunks/studio-grp684wx.js +0 -5
  247. package/dist/chunks/studio-h98520qr.js +0 -5
  248. package/dist/chunks/studio-hvxt8z79.js +0 -5
  249. package/dist/chunks/studio-j4e627w1.js +0 -5
  250. package/dist/chunks/studio-kt2j51sa.js +0 -5
  251. package/dist/chunks/studio-m4w6sygj.js +0 -16
  252. package/dist/chunks/studio-nqxqf8p2.js +0 -4
  253. package/dist/chunks/studio-p89w12s2.js +0 -7
  254. package/dist/chunks/studio-pbagym4j.js +0 -5
  255. package/dist/chunks/studio-pr23qhrw.js +0 -5
  256. package/dist/chunks/studio-r4ya3sb2.js +0 -5
  257. package/dist/chunks/studio-rm9m3z60.js +0 -5
  258. package/dist/chunks/studio-tpmnbgmt.js +0 -5
  259. package/dist/chunks/studio-ttxhq5yt.js +0 -5
  260. package/dist/chunks/studio-y1x65h5r.js +0 -5
  261. package/dist/chunks/studio-zf188hnd.js +0 -5
  262. package/dist/chunks/toast-1gv0h53h.js +0 -4
  263. package/dist/chunks/workspace-3nt93k48.js +0 -4
  264. package/dist/chunks/workspace-home-zbgyqqws.js +0 -4
  265. package/src/services/directory-picker.ts +0 -187
  266. package/src/services/gated-registry.ts +0 -77
@@ -1,116 +1,31 @@
1
1
  /**
2
2
  * Ai-tools.js — Jx document manipulation tools for the AI assistant
3
3
  *
4
- * Concrete `.jx` AST tools registered into a `@jxsuite/ai` ToolRegistry. Each tool wraps an
5
- * existing `transactDoc()` mutation helper so AI edits get the same undo/redo history as manual
6
- * edits (specs/ai.md §3.1 and §3.2 — optimistic apply, undo as the backstop).
4
+ * Concrete `.jx` AST tools registered into a `@jxsuite/ai` ToolRegistry. The tree tools are the
5
+ * portable producers of `@jxsuite/ai/jx-tools` over the active tab, so AI edits get the same
6
+ * undo/redo history as manual edits (specs/ai.md §3.1 and §3.2 — optimistic apply, undo as the
7
+ * backstop); `create_page` and `create_component` write new files.
7
8
  *
8
9
  * @license MIT
9
10
  */
10
11
 
11
12
  import { createToolDefinition } from "@jxsuite/ai/tools";
12
- import type { ToolRegistry, ToolResult, WriteLedger } from "@jxsuite/ai/tools";
13
- import type { JsonObject, JxMutableNode, JxPath } from "@jxsuite/schema/types";
14
- import { getNodeAtPath } from "../state";
13
+ import type { ToolRegistry, ToolResult } from "@jxsuite/ai/tools";
14
+ import { docToolEntries, JX_TOOL_FACTS } from "@jxsuite/ai/jx-tools";
15
+ import type { JsonObject } from "@jxsuite/schema/types";
15
16
  import type { Tab } from "../tabs/tab";
16
- import {
17
- mutateAddDef,
18
- mutateInsertNode,
19
- mutateMoveNode,
20
- mutateRemoveDef,
21
- mutateUpdateProperty,
22
- mutateUpdateStyle,
23
- transactDoc,
24
- } from "../tabs/transact";
25
- import type { JxNodeValue } from "../tabs/transact";
26
- import type { JsonValue } from "../types";
27
17
  import { validateDoc } from "./jx-validate";
28
18
  import { invalidPathResult, normalizeRelPath } from "./project-path";
29
19
  import { serializeJson } from "@jxsuite/schema/json-layout";
30
- import {
31
- reportDocumentWrite,
32
- snapshotBeforeWrite,
33
- translateValidationError,
34
- } from "./ai-write-report";
35
- import type { CandidateDocument, RenderVerdict } from "./ai-write-report";
20
+ import { translateValidationError } from "./ai-write-report";
21
+ import type { CandidateDocument } from "./ai-write-report";
22
+ import { studioDocumentHost, studioWriteReporter } from "./harness/studio-documents";
36
23
 
37
24
  /* Re-exported from its new home so `ai-project-tools.ts` and the tests keep their import: the
38
25
  translation moved with the verdict it belongs to (`ai-write-report.ts`), where every document
39
26
  write — hand-registered or projected from a command record — now reads it. */
40
27
  export { translateValidationError } from "./ai-write-report";
41
28
 
42
- const PATH_DESCRIPTION =
43
- "Path to a node in the document, as a JSON array of keys/indices from the root " +
44
- '(e.g. ["children", 0, "children", 1]). Use read_document to discover valid paths.';
45
-
46
- /**
47
- * The node at `path`, typed as what {@link getNodeAtPath} actually answers: a path the model made up
48
- * runs off the tree, and that is `undefined`, which every tool here reports as "No node exists".
49
- */
50
- function nodeAt(doc: JxMutableNode, path: JxPath): JxMutableNode | undefined {
51
- return getNodeAtPath(doc, path);
52
- }
53
-
54
- /** Steering error for document tools called with no active tab. */
55
- function noDocError(): ToolResult {
56
- return {
57
- success: false,
58
- error:
59
- "No document is open — use open_document(path) to open one on the canvas, or " +
60
- "read_file/write_file to work with project files directly.",
61
- };
62
- }
63
-
64
- /**
65
- * Apply a mutation, then hand the verdict to `ai-write-report.ts`: only the schema errors the edit
66
- * NEWLY introduced are reported, the render check runs when a renderCheck was provided and
67
- * rendering worked before, and a success carries the token hints. The change stays applied either
68
- * way (optimistic apply + undo); reporting the errors lets the agent loop self-correct on the next
69
- * round. Both rules are specs/ai.md §3.1.
70
- *
71
- * @param {WriteLedger} ledger - The call's ledger (`ctx.ledger`)
72
- * @param {import("../tabs/tab").Tab} tab
73
- * @param {(t: import("../tabs/tab").Tab) => void} mutationFn
74
- * @param {string} summary
75
- * @param {(doc: CandidateDocument) => Promise<string[]>} validate
76
- * @param {((doc: CandidateDocument) => Promise<RenderVerdict>) | undefined} renderCheck
77
- * @param {Record<string, string> | undefined} projectStyle
78
- * @returns {Promise<import("@jxsuite/ai/tools").ToolResult>}
79
- */
80
- async function applyAndValidate(
81
- ledger: WriteLedger,
82
- tab: Tab,
83
- mutationFn: (t: Tab) => void,
84
- summary: string,
85
- validate: (doc: CandidateDocument) => Promise<string[]>,
86
- renderCheck: ((doc: CandidateDocument) => Promise<RenderVerdict>) | undefined,
87
- projectStyle: Record<string, string> | undefined,
88
- ): Promise<ToolResult> {
89
- const deps = { getProjectStyle: () => projectStyle, renderCheck, validate };
90
- const before = await snapshotBeforeWrite(tab, deps);
91
-
92
- /* AND THE WRITE CAN BE REFUSED, in which case the model must be told rather than congratulated.
93
- The collab gate pauses structural editing while source is canonical, and this went on to
94
- record a ledger entry ("Changed N files") and validate the UNCHANGED document — which produces
95
- no new errors, so the tool answered `success: true`. The model then built its next three edits
96
- on a change that never existed. A refusal is an ordinary state of the room, so it is an error
97
- result with a reason the model can act on (wait, or ask the author), not a thrown exception. */
98
- if (!transactDoc(tab, mutationFn)) {
99
- return {
100
- success: false,
101
- error:
102
- "The document is frozen: a collaborator is editing its source, so structural edits are " +
103
- "paused. Nothing was changed. Try again once source editing ends.",
104
- };
105
- }
106
- /* One ledger entry per mutation, so the panel's "Changed N files" counts documents the model
107
- touched rather than sentences it wrote (`ai.md` §3.2). `disk: false` is the load-bearing half:
108
- this went through transactDoc, so the tab's history — and "Restore to here" — can reach it. */
109
- ledger.record({ disk: false, ok: true, path: tab.documentPath ?? "(untitled)", tool: summary });
110
-
111
- return reportDocumentWrite(tab, before, summary, deps);
112
- }
113
-
114
29
  /**
115
30
  * Register the document-manipulation tools into a tool registry.
116
31
  *
@@ -181,481 +96,26 @@ export function registerAiTools(
181
96
  }
182
97
  return `${summary} (Saved to disk; not undoable.)`;
183
98
  }
184
- registry.register(
185
- createToolDefinition({
186
- name: "read_document",
187
- description:
188
- "Read the current Jx document, or the subtree at a given path. Use this to discover " +
189
- "node paths before calling set_property, add_child, or delete_node.",
190
- parameters: {
191
- type: "object",
192
- properties: {
193
- path: {
194
- type: "array",
195
- description: `${PATH_DESCRIPTION} Omit to read the whole document.`,
196
- items: { type: ["string", "number"] },
197
- },
198
- },
199
- required: [],
200
- },
201
- execute(args) {
202
- const tab = getTab();
203
- if (!tab) {
204
- return noDocError();
205
- }
206
- /* SAFETY: a strict tool runs only after the registry's `validate()` passed, which refuses
207
- a `path` that is present but not an array. Its items are not checked; a key that does
208
- not resolve makes `nodeAt` answer undefined, reported just below. */
209
- const { path } = args as { path?: JxPath };
210
- const node = path && path.length > 0 ? nodeAt(tab.doc.document, path) : tab.doc.document;
211
- if (node === undefined) {
212
- return { success: false, error: `No node exists at path ${JSON.stringify(path)}.` };
213
- }
214
- return { success: true, data: node };
215
- },
216
- }),
217
- );
218
-
219
- registry.register(
220
- createToolDefinition({
221
- name: "set_property",
222
- description:
223
- "Set or remove a property on a node (e.g. tagName, textContent, className, style, " +
224
- "attributes, $props). Pass value: null to remove the property.",
225
- parameters: {
226
- type: "object",
227
- properties: {
228
- path: {
229
- type: "array",
230
- description: PATH_DESCRIPTION,
231
- items: { type: ["string", "number"] },
232
- },
233
- key: {
234
- type: "string",
235
- description: 'The property name to set, e.g. "textContent" or "className".',
236
- },
237
- value: {
238
- description: "The new value, or null to remove the property.",
239
- },
240
- },
241
- /*
242
- * "value" omitted from required: passing null / omitting it means "remove the property",
243
- * but the registry rejects null on required args (tools.js:181). See `ai.md` §3.6.
244
- */
245
- required: ["path", "key"],
246
- },
247
- async execute(args, ctx) {
248
- const tab = getTab();
249
- if (!tab) {
250
- return noDocError();
251
- }
252
- /* SAFETY: a strict tool runs only after the registry's `validate()` passed: `path` is a
253
- present array and `key` a present string. `value` declares no type, so it is any JSON
254
- value or absent, all of which `JxNodeValue` includes. A `path` whose items do not resolve
255
- is caught by `nodeAt` just below. */
256
- const { path, key, value } = args as { path: JxPath; key: string; value?: JxNodeValue };
257
- const node = nodeAt(tab.doc.document, path);
258
- if (node === undefined) {
259
- return { success: false, error: `No node exists at path ${JSON.stringify(path)}.` };
260
- }
261
- return applyAndValidate(
262
- ctx.ledger,
263
- tab,
264
- (t) => mutateUpdateProperty(t, path, key, value ?? undefined),
265
- `Set "${key}" at ${JSON.stringify(path)}.`,
266
- validate,
267
- renderCheck,
268
- styleOf(),
269
- );
270
- },
271
- }),
272
- );
273
-
274
- registry.register(
275
- createToolDefinition({
276
- name: "add_child",
277
- description:
278
- "Insert a new child node into the children array of the node at parentPath, at the " +
279
- "given index (0 = first child). The node definition follows the Jx document schema " +
280
- '(e.g. { "tagName": "p", "textContent": "Hello" }).',
281
- parameters: {
282
- type: "object",
283
- properties: {
284
- parentPath: {
285
- type: "array",
286
- description: `${PATH_DESCRIPTION} Must point at a node, not a children array.`,
287
- items: { type: ["string", "number"] },
288
- },
289
- index: { type: "integer", description: "Position in the children array to insert at." },
290
- node: {
291
- type: "object",
292
- description:
293
- 'The Jx node definition to insert, e.g. { "tagName": "div", "children": [] }.',
294
- },
295
- },
296
- required: ["parentPath", "index", "node"],
297
- },
298
- async execute(args, ctx) {
299
- const tab = getTab();
300
- if (!tab) {
301
- return noDocError();
302
- }
303
- /* SAFETY: a strict tool runs only after the registry's `validate()` passed: `parentPath` is
304
- a present array, `node` a present object, and `index` a present number — or a numeric
305
- string, which the validator also admits for an integer and `splice` coerces alike.
306
- `node` is a model-written tree whose shape the schema check after the write judges, and
307
- a `parentPath` that does not resolve is caught just below. */
308
- const {
309
- parentPath,
310
- index,
311
- node: childNode,
312
- } = args as {
313
- parentPath: JxPath;
314
- index: number;
315
- node: JxMutableNode;
316
- };
317
- const parent = nodeAt(tab.doc.document, parentPath);
318
- if (parent === undefined) {
319
- return { success: false, error: `No node exists at path ${JSON.stringify(parentPath)}.` };
320
- }
321
- /*
322
- * Guard against a parentPath that points at a children *array* rather than a node — the
323
- * common failure is a trailing "children" segment (e.g. ["children",0,"children"]).
324
- * add_child appends "children" + index itself, so an array-valued parentPath would splice
325
- * into a bogus `.children` property on the array (childArray() creates one) and the node
326
- * would be stored where nothing renders, yet the tool would report success. Reject it with
327
- * a precise message so the loop self-corrects.
328
- */
329
- if (Array.isArray(parent)) {
330
- return {
331
- success: false,
332
- error:
333
- `parentPath ${JSON.stringify(parentPath)} points at a children array, not a node. ` +
334
- `Drop the trailing "children" segment — add_child appends "children" and the index ` +
335
- `automatically. For example, to insert into the node at ["children",0,"children",1], ` +
336
- `pass parentPath: ["children",0,"children",1].`,
337
- };
338
- }
339
- if (parent.children !== undefined && !Array.isArray(parent.children)) {
340
- return {
341
- success: false,
342
- error: "Cannot insert into mapped-array children; edit the map template instead.",
343
- };
344
- }
345
- return applyAndValidate(
346
- ctx.ledger,
347
- tab,
348
- (t) => mutateInsertNode(t, parentPath, index, childNode),
349
- `Inserted node at ${JSON.stringify([...parentPath, "children", index])}.`,
350
- validate,
351
- renderCheck,
352
- styleOf(),
353
- );
354
- },
355
- }),
356
- );
357
-
358
- // ── set_style ──────────────────────────────────────────────────────────
359
-
360
- registry.register(
361
- createToolDefinition({
362
- name: "set_style",
363
- description:
364
- "Set or remove a CSS style property on a node. Style property names use camelCase " +
365
- '(e.g. "backgroundColor", "fontSize", "borderRadius"). Values are always strings ' +
366
- '(e.g. "10px", "center", "var(--color-accent)"). Pass value: null to remove.',
367
- parameters: {
368
- type: "object",
369
- properties: {
370
- path: {
371
- type: "array",
372
- description: PATH_DESCRIPTION,
373
- items: { type: ["string", "number"] },
374
- },
375
- property: {
376
- type: "string",
377
- description: 'CSS property name in camelCase, e.g. "backgroundColor".',
378
- },
379
- value: {
380
- description:
381
- 'CSS value as a string (e.g. "10px", "var(--color-accent)"), or null to remove.',
382
- },
383
- },
384
- /*
385
- * "value" omitted from required so null / omitted = remove (registry rejects null on
386
- * required args, tools.js:181). See `ai.md` §3.6.
387
- */
388
- required: ["path", "property"],
389
- },
390
- async execute(args, ctx) {
391
- const tab = getTab();
392
- if (!tab) {
393
- return noDocError();
394
- }
395
- /* SAFETY: a strict tool runs only after the registry's `validate()` passed: `path` is a
396
- present array and `property` a present string; a `path` that does not resolve is caught
397
- just below. `value` is read untyped and stringified. */
398
- const { path, property } = args as { path: JxPath; property: string };
399
- const { value } = args;
400
- if (nodeAt(tab.doc.document, path) === undefined) {
401
- return { success: false, error: `No node exists at path ${JSON.stringify(path)}.` };
402
- }
403
- const prop = property;
404
- const val = value == null ? undefined : String(value);
405
- return applyAndValidate(
406
- ctx.ledger,
407
- tab,
408
- (t) => mutateUpdateStyle(t, path, prop, val),
409
- `Set style "${prop}" at ${JSON.stringify(path)}.`,
410
- validate,
411
- renderCheck,
412
- styleOf(),
413
- );
414
- },
415
- }),
416
- );
417
-
418
- // ── set_text ───────────────────────────────────────────────────────────
419
-
420
- registry.register(
421
- createToolDefinition({
422
- name: "set_text",
423
- description:
424
- "Set the textContent of a node. Convenience alias for set_property with key: 'textContent'.",
425
- parameters: {
426
- type: "object",
427
- properties: {
428
- path: {
429
- type: "array",
430
- description: PATH_DESCRIPTION,
431
- items: { type: ["string", "number"] },
432
- },
433
- value: { type: "string", description: "Text content." },
434
- },
435
- required: ["path", "value"],
436
- },
437
- async execute(args, ctx) {
438
- const tab = getTab();
439
- if (!tab) {
440
- return noDocError();
441
- }
442
- /* SAFETY: a strict tool runs only after the registry's `validate()` passed: `path` is a
443
- present array and `value` a present string; a `path` that does not resolve is caught
444
- just below. */
445
- const { path, value } = args as { path: JxPath; value: string };
446
- if (nodeAt(tab.doc.document, path) === undefined) {
447
- return { success: false, error: `No node exists at path ${JSON.stringify(path)}.` };
448
- }
449
- return applyAndValidate(
450
- ctx.ledger,
451
- tab,
452
- /* Through the recording mutators, as two `set-key` ops, rather than by writing the node
453
- directly. An unrecorded write reaches the canvas only as a full re-render, history only
454
- as a whole-document snapshot, and collaborators only as a diff. */
455
- (t) => {
456
- mutateUpdateProperty(t, path, "textContent");
457
- mutateUpdateProperty(t, path, "children", [value]);
458
- },
459
- `Set text at ${JSON.stringify(path)}.`,
460
- validate,
461
- renderCheck,
462
- styleOf(),
463
- );
464
- },
465
- }),
466
- );
467
-
468
- // ── add_state ──────────────────────────────────────────────────────────
469
-
470
- registry.register(
471
- createToolDefinition({
472
- name: "add_state",
473
- description:
474
- "Add a new reactive state variable under the document's `state` object. The value can be a scalar " +
475
- '(e.g. 0, ""), a typed object ({ "type": "string", "default": "" }), a computed ' +
476
- 'string ("${state.other}"), a function ({ "$prototype": "Function", "body": "..." }), ' +
477
- 'or a data source ({ "$prototype": "Data", "$src": "./data.json" }).',
478
- parameters: {
479
- type: "object",
480
- properties: {
481
- key: { type: "string", description: 'State variable name, e.g. "count", "isOpen".' },
482
- value: {
483
- description:
484
- "Initial value or state shape object (scalar, typed, computed, function, or data source).",
485
- },
486
- },
487
- required: ["key", "value"],
488
- },
489
- async execute(args, ctx) {
490
- const tab = getTab();
491
- if (!tab) {
492
- return noDocError();
493
- }
494
- /* SAFETY: a strict tool runs only after the registry's `validate()` passed, so `key` is a
495
- present string. */
496
- const { key } = args as { key: string };
497
- const { value } = args;
498
- if (tab.doc.document.state && tab.doc.document.state[key] !== undefined) {
499
- return {
500
- success: false,
501
- error: `State key "${key}" already exists. Use update_state to change it, or remove it first.`,
502
- };
503
- }
504
- return applyAndValidate(
505
- ctx.ledger,
506
- tab,
507
- /* `mutateAddDef` rather than `mutateUpdateProperty`: the latter deletes on "", which is
508
- wrong for a state default (`"title": ""`). It also records the op, so the canvas, the
509
- history and collaborators each get the edit as an edit. */
510
- /* SAFETY: `value` is required, so the registry refused it missing or null; it is the
511
- model's state entry, possibly a scalar default (`0`, `""`) the schema admits.
512
- `mutateAddDef` only clones `def` and stores it under `state[key]`, never reading it
513
- as the object its parameter names. */
514
- (t) => mutateAddDef(t, key, value as Record<string, JsonValue>),
515
- `Added state "${key}".`,
516
- validate,
517
- renderCheck,
518
- styleOf(),
519
- );
520
- },
521
- }),
522
- );
523
-
524
- // ── update_state ───────────────────────────────────────────────────────
525
-
526
- registry.register(
527
- createToolDefinition({
528
- name: "update_state",
529
- description:
530
- "Update or remove an existing state variable at the document root. Pass value: null to remove.",
531
- parameters: {
532
- type: "object",
533
- properties: {
534
- key: { type: "string", description: 'State variable name to update, e.g. "count".' },
535
- value: {
536
- description:
537
- "New value (same shapes as add_state), or null to remove the state variable.",
538
- },
539
- },
540
- /*
541
- * "value" omitted from required so null = remove (registry rejects null on required
542
- * args, tools.js:181). See `ai.md` §3.6.
543
- */
544
- required: ["key"],
545
- },
546
- async execute(args, ctx) {
547
- const tab = getTab();
548
- if (!tab) {
549
- return noDocError();
550
- }
551
- /* SAFETY: a strict tool runs only after the registry's `validate()` passed, so `key` is a
552
- present string. */
553
- const { key } = args as { key: string };
554
- const { value } = args;
555
- if (!tab.doc.document.state || tab.doc.document.state[key] === undefined) {
556
- return {
557
- success: false,
558
- error: `State key "${key}" does not exist. Use add_state to create it, or check the name. Current state keys: ${Object.keys(tab.doc.document.state || {}).join(", ") || "(none)"}`,
559
- };
560
- }
561
- return applyAndValidate(
562
- ctx.ledger,
563
- tab,
564
- /* The recording def mutators, for the reason `add_state` gives. Removing the last key
565
- drops the empty `state` object, as the Inspector's own removal does. */
566
- (t) => {
567
- if (value == null) {
568
- mutateRemoveDef(t, key);
569
- } else {
570
- /* SAFETY: the model's state entry, possibly a scalar default the schema admits.
571
- `mutateAddDef` only clones `def` and stores it under `state[key]`, never reading
572
- it as the object its parameter names. */
573
- mutateAddDef(t, key, value as Record<string, JsonValue>);
574
- }
575
- },
576
- value == null ? `Removed state "${key}".` : `Updated state "${key}".`,
577
- validate,
578
- renderCheck,
579
- styleOf(),
580
- );
581
- },
582
- }),
583
- );
584
-
585
- // ── move_node ──────────────────────────────────────────────────────────
586
-
587
- registry.register(
588
- createToolDefinition({
589
- name: "move_node",
590
- description:
591
- "Move a node from one location to another in the document tree. The node is removed " +
592
- "from fromPath and inserted into toParentPath at toIndex.",
593
- parameters: {
594
- type: "object",
595
- properties: {
596
- fromPath: {
597
- type: "array",
598
- description: `${PATH_DESCRIPTION} The node to move.`,
599
- items: { type: ["string", "number"] },
600
- },
601
- toParentPath: {
602
- type: "array",
603
- description: `${PATH_DESCRIPTION} The target parent (a node, not a children array).`,
604
- items: { type: ["string", "number"] },
605
- },
606
- toIndex: {
607
- type: "integer",
608
- description: "Insertion index in the target parent's children array.",
609
- },
610
- },
611
- required: ["fromPath", "toParentPath", "toIndex"],
612
- },
613
- async execute(args, ctx) {
614
- const tab = getTab();
615
- if (!tab) {
616
- return noDocError();
617
- }
618
- /* SAFETY: a strict tool runs only after the registry's `validate()` passed: both paths are
619
- present arrays, and `toIndex` a present number or numeric string (the validator admits
620
- both for an integer). Paths that do not resolve are caught just below. */
621
- const { fromPath, toParentPath, toIndex } = args as {
622
- fromPath: JxPath;
623
- toParentPath: JxPath;
624
- toIndex: number;
625
- };
626
- if (fromPath.length < 2) {
627
- return { success: false, error: "Cannot move the document root." };
628
- }
629
- if (nodeAt(tab.doc.document, fromPath) === undefined) {
630
- return {
631
- success: false,
632
- error: `No node exists at fromPath ${JSON.stringify(fromPath)}.`,
633
- };
634
- }
635
- if (nodeAt(tab.doc.document, toParentPath) === undefined) {
636
- return {
637
- success: false,
638
- error: `No node exists at toParentPath ${JSON.stringify(toParentPath)}.`,
639
- };
640
- }
641
- return applyAndValidate(
642
- ctx.ledger,
643
- tab,
644
- (t) => mutateMoveNode(t, fromPath, toParentPath, toIndex),
645
- `Moved node from ${JSON.stringify(fromPath)} to ${JSON.stringify([...toParentPath, "children", toIndex])}.`,
646
- validate,
647
- renderCheck,
648
- styleOf(),
649
- );
650
- },
651
- }),
652
- );
99
+ /* `read_document` and the seven tree tools are the portable producers of `@jxsuite/ai/jx-tools`,
100
+ run over the active tab: each plans its edit as doc ops, and the tab applies them as one
101
+ transaction the turn's batch lane, history and collaborators all see (specs/ai.md §3.9). */
102
+ const host = studioDocumentHost({ getTab });
103
+ const reporter = studioWriteReporter(host, {
104
+ getProjectStyle: styleOf,
105
+ renderCheck,
106
+ validate,
107
+ });
108
+ for (const entry of docToolEntries(host, { reporter })) {
109
+ registry.register(entry);
110
+ }
653
111
 
654
112
  // ── create_component ───────────────────────────────────────────────────
655
113
 
656
114
  registry.register(
657
115
  createToolDefinition({
658
116
  name: "create_component",
117
+ title: "Create a component",
118
+ annotations: JX_TOOL_FACTS.create_component.annotations,
659
119
  description:
660
120
  "Create a new Jx component file on disk. Writes the component JSON to the given " +
661
121
  "relative path within the project. The content must be a valid Jx component document.",
@@ -744,6 +204,8 @@ export function registerAiTools(
744
204
  registry.register(
745
205
  createToolDefinition({
746
206
  name: "create_page",
207
+ title: "Create a page",
208
+ annotations: JX_TOOL_FACTS.create_page.annotations,
747
209
  description:
748
210
  "Create a new Jx page file on disk. A page typically includes a layout component and " +
749
211
  "section children. The content must be a valid Jx page document.",