@jxsuite/studio 8.0.1 → 9.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 (282) hide show
  1. package/dist/chunks/{2020-5wtzce5g.js → 2020-89fz3pfy.js} +3 -3
  2. package/dist/chunks/adapter-ay13gema.js +4 -0
  3. package/dist/chunks/add-new-flow-nmkqpjym.js +4 -0
  4. package/dist/chunks/cf-account-picker-2e06qxey.js +4 -0
  5. package/dist/chunks/cf-settings-5y04810c.js +4 -0
  6. package/dist/chunks/color-04361ttk.js +4 -0
  7. package/dist/chunks/{color-area-6rbra5dy.js → color-area-h8dbasp1.js} +2 -2
  8. package/dist/chunks/{color-field-gy56c94g.js → color-field-qv5r2b16.js} +3 -3
  9. package/dist/chunks/{color-slider-sqqdyf6c.js → color-slider-nz2mrzky.js} +2 -2
  10. package/dist/chunks/{combobox-72h32ejk.js → combobox-6hs00996.js} +2 -2
  11. package/dist/chunks/components-3vf75n83.js +4 -0
  12. package/dist/chunks/content-source-4f2raf4a.js +4 -0
  13. package/dist/chunks/{convert-to-repeater-he70fc2b.js → convert-to-repeater-j9hfcj4f.js} +3 -3
  14. package/dist/chunks/editor-645krvtz.js +4 -0
  15. package/dist/chunks/entry-commands-3qmv8wkr.js +4 -0
  16. package/dist/chunks/{extension-sections-0egb88dm.js → extension-sections-thfke054.js} +3 -3
  17. package/dist/chunks/file-ops-kkhp9ma7.js +4 -0
  18. package/dist/chunks/files-yswdhxrf.js +4 -0
  19. package/dist/chunks/format-host-4g8ex6bz.js +4 -0
  20. package/dist/chunks/fs-events-z9gpvc6t.js +4 -0
  21. package/dist/chunks/grid-open-fm4j183c.js +4 -0
  22. package/dist/chunks/{grid-view-qg1b5eqx.js → grid-view-7d2nx83y.js} +16 -15
  23. package/dist/chunks/{icons-63bjtbs7.js → icons-853n6qt3.js} +2 -2
  24. package/dist/chunks/{iframe-entry-pkgmcgp0.js → iframe-entry-2x9bn5ng.js} +4 -4
  25. package/dist/chunks/{iframe-entry-99nr5zfb.js → iframe-entry-42hpmwz5.js} +4 -4
  26. package/dist/chunks/{iframe-entry-76j8vwwx.js → iframe-entry-b5qgggyp.js} +2 -2
  27. package/dist/chunks/{iframe-entry-2zggzajv.js → iframe-entry-btck0e9s.js} +4 -4
  28. package/dist/chunks/iframe-host-q7fedsmr.js +4 -0
  29. package/dist/chunks/{index-32eqzv1k.js → index-jxmt7vh8.js} +3 -3
  30. package/dist/chunks/index-pt4wh98x.js +4 -0
  31. package/dist/chunks/{javascript-pjp734q4.js → javascript-vmqp56ke.js} +2 -2
  32. package/dist/chunks/{jsonMode-njkvsa02.js → jsonMode-n3wmpant.js} +5 -5
  33. package/dist/chunks/jx-validate-19y62knt.js +4 -0
  34. package/dist/chunks/layers-panel-1qvc83y9.js +4 -0
  35. package/dist/chunks/layers-qf0bc7ze.js +4 -0
  36. package/dist/chunks/{listbox-c421cf8c.js → listbox-7gka1h86.js} +2 -2
  37. package/dist/chunks/lit-html-wyqw04y2.js +4 -0
  38. package/dist/chunks/media-library-f9d5qcjb.js +4 -0
  39. package/dist/chunks/media-picker-rsb1fmk2.js +4 -0
  40. package/dist/chunks/{menu-bdpzbfvs.js → menu-d2jxpgma.js} +2 -2
  41. package/dist/chunks/{monaco-binding-vq1hgnn0.js → monaco-binding-wfhmz89d.js} +3 -3
  42. package/dist/chunks/monaco-buffer-ak0raadw.js +4 -0
  43. package/dist/chunks/{monaco-setup-xrd2cbkp.js → monaco-setup-fty7a8vg.js} +3 -3
  44. package/dist/chunks/notify-4qj70q0x.js +4 -0
  45. package/dist/chunks/parse-n8dfxnqf.js +4 -0
  46. package/dist/chunks/platform-ehg6t9vd.js +4 -0
  47. package/dist/chunks/{popover-b89sfej7.js → popover-9tf4cjrk.js} +2 -2
  48. package/dist/chunks/publish-panel-yhmda3cd.js +4 -0
  49. package/dist/chunks/{publish-quick-qx2455cj.js → publish-quick-12hh8926.js} +3 -3
  50. package/dist/chunks/publish-workspace-hx8qdv9p.js +4 -0
  51. package/dist/chunks/quick-search-r955m3q1.js +4 -0
  52. package/dist/chunks/references-8gxv0x7t.js +4 -0
  53. package/dist/chunks/right-panel-jarwqjpy.js +4 -0
  54. package/dist/chunks/source-control-84qt25m9.js +4 -0
  55. package/dist/chunks/{split-c9zfdgxf.js → split-m5rwn31z.js} +2 -2
  56. package/dist/chunks/studio-0nefxhar.js +7 -0
  57. package/dist/chunks/studio-0rg30pvs.js +5 -0
  58. package/dist/chunks/{studio-s32578pn.js → studio-14hh60j8.js} +4 -4
  59. package/dist/chunks/studio-1cfyd1ys.js +5 -0
  60. package/dist/chunks/studio-2bq7hheq.js +5 -0
  61. package/dist/chunks/{studio-zz9qe3rw.js → studio-32d8wyxq.js} +37 -37
  62. package/dist/chunks/studio-3hpcawh8.js +4 -0
  63. package/dist/chunks/{studio-svb7yyve.js → studio-4j35hgsw.js} +4 -4
  64. package/dist/chunks/{studio-x1x167jh.js → studio-4qg61g9j.js} +4 -4
  65. package/dist/chunks/studio-4rgem12q.js +5 -0
  66. package/dist/chunks/studio-6k9djnxw.js +5 -0
  67. package/dist/chunks/studio-6rbaedw2.js +9 -0
  68. package/dist/chunks/studio-732mw0g8.js +5 -0
  69. package/dist/chunks/studio-79r8msnr.js +7 -0
  70. package/dist/chunks/studio-7q6jdd4h.js +5 -0
  71. package/dist/chunks/{studio-r5qy30vx.js → studio-8hf8qcm1.js} +4 -4
  72. package/dist/chunks/{studio-8tr6cfrc.js → studio-8pne0rx4.js} +4 -4
  73. package/dist/chunks/{studio-y0n7yxea.js → studio-9179bsm7.js} +7 -7
  74. package/dist/chunks/{studio-pbxa5fpc.js → studio-9ff0vqff.js} +3 -3
  75. package/dist/chunks/studio-9gzvypw4.js +5 -0
  76. package/dist/chunks/studio-9ndgd55w.js +5 -0
  77. package/dist/chunks/studio-9ra9e7m4.js +5 -0
  78. package/dist/chunks/studio-a6e5fzve.js +5 -0
  79. package/dist/chunks/studio-a9k86nq6.js +5 -0
  80. package/dist/chunks/studio-ae7a69jc.js +6 -0
  81. package/dist/chunks/studio-aghsg0ds.js +7 -0
  82. package/dist/chunks/{studio-k3xxbagr.js → studio-ajw4k8hp.js} +3 -3
  83. package/dist/chunks/{studio-qmn1xc24.js → studio-apapy17g.js} +4 -4
  84. package/dist/chunks/{studio-8m0y2qe7.js → studio-by8x0jvc.js} +4 -4
  85. package/dist/chunks/{studio-wbcjbhy1.js → studio-c88gq88j.js} +5 -5
  86. package/dist/chunks/{studio-n0c5h6mf.js → studio-cg54vtet.js} +4 -4
  87. package/dist/chunks/studio-dvhxxxbd.js +5 -0
  88. package/dist/chunks/{studio-3q40e3rj.js → studio-e4rq6x0q.js} +4 -4
  89. package/dist/chunks/{studio-fbhxxc5f.js → studio-ekzbgnjp.js} +4 -4
  90. package/dist/chunks/studio-fg4fj9ec.js +10 -0
  91. package/dist/chunks/studio-fj6jzgxw.js +5 -0
  92. package/dist/chunks/{studio-nh6d342n.js → studio-g45r5vm8.js} +7 -7
  93. package/dist/chunks/{studio-a2bvaptz.js → studio-gczve8zf.js} +4 -4
  94. package/dist/chunks/studio-ge08zwpv.js +5 -0
  95. package/dist/chunks/studio-gk9yk8dj.js +12 -0
  96. package/dist/chunks/{studio-bbe4c90a.js → studio-gskkgfe3.js} +4 -4
  97. package/dist/chunks/{studio-83mk4bgg.js → studio-h4jbgne2.js} +8 -8
  98. package/dist/chunks/{studio-b7y5mr30.js → studio-hcy9zeac.js} +4 -4
  99. package/dist/chunks/studio-ht81xjkr.js +5 -0
  100. package/dist/chunks/{studio-jaaz8ce9.js → studio-jr30g4ae.js} +4 -4
  101. package/dist/chunks/{studio-qgh13jtz.js → studio-k5n4es74.js} +4 -4
  102. package/dist/chunks/{studio-ccv2svc6.js → studio-k6hbx2fc.js} +4 -4
  103. package/dist/chunks/{studio-9tmebf0s.js → studio-kqh7fxn6.js} +4 -4
  104. package/dist/chunks/{studio-q0mvbaq6.js → studio-kyw47pet.js} +4 -4
  105. package/dist/chunks/studio-mkw17ngg.js +5 -0
  106. package/dist/chunks/{studio-x9hsze61.js → studio-myb4mf0h.js} +4 -4
  107. package/dist/chunks/studio-n4qq1n57.js +5 -0
  108. package/dist/chunks/studio-nfmpmkqb.js +5 -0
  109. package/dist/chunks/studio-nfwacw31.js +5 -0
  110. package/dist/chunks/{studio-rthyg42z.js → studio-nm86874b.js} +172 -172
  111. package/dist/chunks/studio-nqexty8x.js +16 -0
  112. package/dist/chunks/studio-nr0kcqc6.js +5 -0
  113. package/dist/chunks/{studio-jsgf2q5k.js → studio-nvfa137z.js} +5 -5
  114. package/dist/chunks/studio-psh8mwnw.js +4 -0
  115. package/dist/chunks/studio-qd0cycm4.js +9 -0
  116. package/dist/chunks/{studio-tfkznv39.js → studio-qevf5z9d.js} +4 -4
  117. package/dist/chunks/studio-qs5fmhtj.js +14 -0
  118. package/dist/chunks/studio-ry4789ng.js +7 -0
  119. package/dist/chunks/studio-s4fq9jht.js +5 -0
  120. package/dist/chunks/studio-sc35ttdk.js +5 -0
  121. package/dist/chunks/studio-sm2f0sc6.js +6 -0
  122. package/dist/chunks/{studio-3qeh9t0h.js → studio-spza69tb.js} +5 -5
  123. package/dist/chunks/studio-t6dqnnft.js +5 -0
  124. package/dist/chunks/{studio-43sd2mam.js → studio-tfmhnwyr.js} +3 -3
  125. package/dist/chunks/studio-tn7v8gey.js +6 -0
  126. package/dist/chunks/{studio-tft6r105.js → studio-trcfer2b.js} +4 -4
  127. package/dist/chunks/studio-tyw7tk0x.js +5 -0
  128. package/dist/chunks/{studio-jcnsdaep.js → studio-tyzgte21.js} +4 -4
  129. package/dist/chunks/studio-vtey9w98.js +5 -0
  130. package/dist/chunks/studio-ws2r1d7e.js +5 -0
  131. package/dist/chunks/studio-x2trkhk5.js +5 -0
  132. package/dist/chunks/{studio-mn0476ag.js → studio-xzgp0cp7.js} +4 -4
  133. package/dist/chunks/studio-y47090vq.js +5 -0
  134. package/dist/chunks/{studio-5g0rprdt.js → studio-yjh9gddd.js} +5 -5
  135. package/dist/chunks/studio-ywf9y1e2.js +5 -0
  136. package/dist/chunks/{studio-k7evxqh4.js → studio-z1p91z5a.js} +4 -4
  137. package/dist/chunks/studio-ztppbam0.js +5 -0
  138. package/dist/chunks/{toast-host-1yg5ar8d.js → toast-host-g3r2s0kn.js} +2 -2
  139. package/dist/chunks/toast-w11qpkyz.js +4 -0
  140. package/dist/chunks/{tooltip-yypz28gq.js → tooltip-dj1pvgz7.js} +2 -2
  141. package/dist/chunks/{tree-9xa4hm7n.js → tree-j0ebwr5s.js} +2 -2
  142. package/dist/chunks/{tsMode-epyfz1hv.js → tsMode-byje4a01.js} +5 -5
  143. package/dist/chunks/workspace-7q4arnt3.js +4 -0
  144. package/dist/chunks/workspace-home-ge027kr6.js +4 -0
  145. package/dist/chunks/{ws-client-bv552sfx.js → ws-client-h09h6sqg.js} +3 -3
  146. package/dist/iframe-entry.js +14 -14
  147. package/dist/manifest.json +76 -76
  148. package/dist/studio.js +88 -88
  149. package/package.json +15 -16
  150. package/src/browse/component-examples.ts +15 -224
  151. package/src/browse/library-preview.ts +6 -13
  152. package/src/browse/preview-root.ts +7 -4
  153. package/src/canvas/canvas-utils.ts +18 -4
  154. package/src/canvas/iframe-editable-root.ts +13 -4
  155. package/src/canvas/iframe-entry.ts +3 -1
  156. package/src/canvas/iframe-interaction.ts +11 -2
  157. package/src/canvas/iframe-render.ts +25 -3
  158. package/src/canvas/preview-placeholder.ts +120 -0
  159. package/src/collab/collab-session.ts +6 -2
  160. package/src/commands/context.ts +4 -0
  161. package/src/commands/defaults.ts +2 -0
  162. package/src/commands/live-context.ts +2 -0
  163. package/src/editor/shortcuts.ts +3 -0
  164. package/src/files/files.ts +6 -3
  165. package/src/new-project/new-project-modal.ts +1 -4
  166. package/src/panels/ai-panel.ts +27 -34
  167. package/src/panels/component-preview.ts +5 -0
  168. package/src/panels/pane-context.ts +43 -18
  169. package/src/panels/site-model.ts +7 -19
  170. package/src/panels/site-panel.ts +2 -25
  171. package/src/panels/stylebook-doc.ts +2 -53
  172. package/src/platform.ts +20 -0
  173. package/src/platforms/devserver.ts +2 -120
  174. package/src/services/ai-ask.ts +29 -171
  175. package/src/services/ai-command-tools.ts +40 -50
  176. package/src/services/ai-import-tools.ts +4 -5
  177. package/src/services/ai-project-tools.ts +13 -3
  178. package/src/services/ai-tools.ts +26 -564
  179. package/src/services/ai-writes.ts +84 -2
  180. package/src/services/document-assistant.ts +29 -58
  181. package/src/services/harness/assistant-tools.ts +68 -0
  182. package/src/services/harness/interaction-store.ts +118 -0
  183. package/src/services/harness/studio-documents.ts +105 -0
  184. package/src/services/harness/turn-hooks.ts +1 -6
  185. package/src/services/project-adoption.ts +8 -16
  186. package/src/services/references.ts +0 -0
  187. package/src/services/tool-executor.ts +25 -22
  188. package/src/state.ts +3 -13
  189. package/src/style/stylebook-specimens.ts +11 -15
  190. package/src/surfaces/files-panel.json +12 -0
  191. package/src/surfaces/files-panel.ts +5 -2
  192. package/src/surfaces/pane-context.json +286 -265
  193. package/src/surfaces/pane-context.ts +17 -8
  194. package/src/surfaces/welcome.ts +4 -1
  195. package/src/tabs/selection.ts +14 -109
  196. package/src/tabs/tab.ts +15 -7
  197. package/src/tabs/transact.ts +316 -124
  198. package/src/types.ts +13 -8
  199. package/src/utils/geometry.ts +49 -2
  200. package/dist/chunks/adapter-5tphg7xe.js +0 -4
  201. package/dist/chunks/add-new-flow-psym14sn.js +0 -4
  202. package/dist/chunks/cf-account-picker-haqazq5r.js +0 -4
  203. package/dist/chunks/cf-settings-h2wrh6hq.js +0 -4
  204. package/dist/chunks/color-m7r1vk54.js +0 -4
  205. package/dist/chunks/components-r22xca09.js +0 -4
  206. package/dist/chunks/content-source-mmppmcyg.js +0 -4
  207. package/dist/chunks/editor-ccd9jwsa.js +0 -4
  208. package/dist/chunks/entry-commands-6qxz9e8p.js +0 -4
  209. package/dist/chunks/file-ops-qnab36ek.js +0 -4
  210. package/dist/chunks/files-rcn56qvm.js +0 -4
  211. package/dist/chunks/format-host-yxz0kjjs.js +0 -4
  212. package/dist/chunks/fs-events-d1qxq4z4.js +0 -4
  213. package/dist/chunks/grid-open-4z1g062t.js +0 -4
  214. package/dist/chunks/iframe-host-ssv7kyj0.js +0 -4
  215. package/dist/chunks/index-f0jn7nnp.js +0 -4
  216. package/dist/chunks/jx-validate-ttasrbh7.js +0 -4
  217. package/dist/chunks/layers-bgyr11tr.js +0 -4
  218. package/dist/chunks/layers-panel-7ez66zeq.js +0 -4
  219. package/dist/chunks/lit-html-v9gh687x.js +0 -4
  220. package/dist/chunks/media-library-sq468wts.js +0 -4
  221. package/dist/chunks/media-picker-shfe59pt.js +0 -4
  222. package/dist/chunks/monaco-buffer-3afgwvbp.js +0 -4
  223. package/dist/chunks/notify-kzrjr3fv.js +0 -4
  224. package/dist/chunks/parse-v77xtwv7.js +0 -4
  225. package/dist/chunks/platform-7v4rtd7m.js +0 -4
  226. package/dist/chunks/publish-panel-2z9gw8ee.js +0 -4
  227. package/dist/chunks/publish-workspace-wrc1dgwx.js +0 -4
  228. package/dist/chunks/quick-search-n0t0eqxb.js +0 -4
  229. package/dist/chunks/references-9547rg5s.js +0 -4
  230. package/dist/chunks/right-panel-wsgnr9pn.js +0 -4
  231. package/dist/chunks/source-control-kwktr9y6.js +0 -4
  232. package/dist/chunks/studio-03ct6yfv.js +0 -9
  233. package/dist/chunks/studio-07rcfje3.js +0 -5
  234. package/dist/chunks/studio-3e54qh39.js +0 -7
  235. package/dist/chunks/studio-3exxhz5g.js +0 -5
  236. package/dist/chunks/studio-3qqjeep5.js +0 -6
  237. package/dist/chunks/studio-4hp8pkyt.js +0 -5
  238. package/dist/chunks/studio-58xg6j2q.js +0 -9
  239. package/dist/chunks/studio-6m380wp7.js +0 -5
  240. package/dist/chunks/studio-80jyh1xh.js +0 -4
  241. package/dist/chunks/studio-8vcvfs7r.js +0 -6
  242. package/dist/chunks/studio-bavb31r0.js +0 -5
  243. package/dist/chunks/studio-c05wdnt3.js +0 -5
  244. package/dist/chunks/studio-d5grtb9e.js +0 -16
  245. package/dist/chunks/studio-df0baea9.js +0 -5
  246. package/dist/chunks/studio-dgk4dqtx.js +0 -7
  247. package/dist/chunks/studio-e1g64q43.js +0 -5
  248. package/dist/chunks/studio-ed1hp6zf.js +0 -6
  249. package/dist/chunks/studio-ed3xx40h.js +0 -7
  250. package/dist/chunks/studio-f14e1zdj.js +0 -5
  251. package/dist/chunks/studio-fyh0jc96.js +0 -5
  252. package/dist/chunks/studio-gcxk20n7.js +0 -5
  253. package/dist/chunks/studio-kt3pjkwd.js +0 -5
  254. package/dist/chunks/studio-m8g16bgz.js +0 -5
  255. package/dist/chunks/studio-mx1smyq0.js +0 -5
  256. package/dist/chunks/studio-nqxqf8p2.js +0 -4
  257. package/dist/chunks/studio-p4nrkvqr.js +0 -5
  258. package/dist/chunks/studio-pgmvdv7z.js +0 -7
  259. package/dist/chunks/studio-ph84avkf.js +0 -5
  260. package/dist/chunks/studio-q0tccd43.js +0 -5
  261. package/dist/chunks/studio-q7rtx6zq.js +0 -14
  262. package/dist/chunks/studio-qwdqwkj0.js +0 -5
  263. package/dist/chunks/studio-rj9eamf8.js +0 -5
  264. package/dist/chunks/studio-rptgsk96.js +0 -5
  265. package/dist/chunks/studio-s19tp1xr.js +0 -5
  266. package/dist/chunks/studio-syreb644.js +0 -5
  267. package/dist/chunks/studio-tf9jryb1.js +0 -10
  268. package/dist/chunks/studio-txf2kysx.js +0 -5
  269. package/dist/chunks/studio-v8867ddv.js +0 -12
  270. package/dist/chunks/studio-vxq7fcvv.js +0 -5
  271. package/dist/chunks/studio-w4rz9hdk.js +0 -5
  272. package/dist/chunks/studio-wc649wss.js +0 -5
  273. package/dist/chunks/studio-wys23mxz.js +0 -5
  274. package/dist/chunks/studio-y3ahhdpx.js +0 -5
  275. package/dist/chunks/studio-z97c48fk.js +0 -5
  276. package/dist/chunks/studio-zat62m0c.js +0 -5
  277. package/dist/chunks/studio-zcd2e7yz.js +0 -5
  278. package/dist/chunks/toast-1gv0h53h.js +0 -4
  279. package/dist/chunks/workspace-dz7nxfhv.js +0 -4
  280. package/dist/chunks/workspace-home-xsd7hkjw.js +0 -4
  281. package/src/services/directory-picker.ts +0 -187
  282. package/src/services/gated-registry.ts +0 -77
@@ -1,187 +0,0 @@
1
- /// <reference lib="dom" />
2
- /**
3
- * Browser-side folder picking for the New Project modal's **Browse…** button.
4
- *
5
- * `showDirectoryPicker()` is the only native folder chooser a web page gets, but it hands back a
6
- * `FileSystemDirectoryHandle` that exposes `.name` and nothing else — never a filesystem path
7
- * (specs/desktop.md §8.2). Opening a project works around that by searching for the `project.json`
8
- * the user pointed at, which is no help when choosing a _destination_: that folder is empty by
9
- * definition, and often does not exist as a project at all.
10
- *
11
- * So the handle is made to identify itself. Using the readwrite grant the picker just issued, we
12
- * drop a hidden {@link LOCATION_ID_FILE} holding a freshly generated id and ask the backend which
13
- * directory holds that exact id. A fixed filename with the identity in its _contents_ is what makes
14
- * the match unambiguous: two folders may share a basename, and a stale file from a crashed session
15
- * may still be lying around, but only one file anywhere holds this id. The file lives in the chosen
16
- * **Location** (the parent the project will be created in), never in the new project folder. The
17
- * backend deletes it as soon as it matches, and this module removes it on every path the page
18
- * survives, including a write that failed after the empty file was created. Only a tab closed
19
- * mid-lookup can leave one behind, and a stale id can never match a later pick.
20
- *
21
- * **`null` means the user dismissed the chooser, and nothing else does.** Every other failure
22
- * rejects with a sentence fit to show under the Location field: the chooser could not open, the
23
- * folder cannot be written, the backend could not be reached, or it found no folder holding the id.
24
- * This used to resolve `null` for all of them, which made a broken lookup indistinguishable from a
25
- * cancel, and Browse… a button that silently did nothing.
26
- *
27
- * Only the plain dev-server browser session needs this. Every packaged build has a real native
28
- * dialog that returns a path directly and keeps it — the Electron app's native folder dialog, and
29
- * the NixOS chromium build's XDG desktop portal.
30
- *
31
- * @docs studio/projects/create
32
- */
33
-
34
- import { LOCATION_ID_FILE } from "@jxsuite/protocol/routes";
35
- import { isNonNullObject, isString } from "@jxsuite/schema/guards";
36
- import { errorMessage } from "@jxsuite/schema/parse";
37
-
38
- /** Re-exported so a caller wiring up `locate` need not also reach into the protocol package. */
39
- export { LOCATION_ID_FILE } from "@jxsuite/protocol/routes";
40
-
41
- /** The subset of the File System Access API this module uses. */
42
- interface WritableHandle {
43
- createWritable: () => Promise<{
44
- write: (data: string) => Promise<void>;
45
- close: () => Promise<void>;
46
- }>;
47
- }
48
-
49
- interface DirectoryHandle {
50
- name: string;
51
- getFileHandle: (name: string, opts?: { create?: boolean }) => Promise<WritableHandle>;
52
- removeEntry: (name: string, opts?: { recursive?: boolean }) => Promise<void>;
53
- }
54
-
55
- type ShowDirectoryPicker = (opts?: {
56
- id?: string;
57
- mode?: "read" | "readwrite";
58
- startIn?: string;
59
- }) => Promise<DirectoryHandle>;
60
-
61
- /** A realm that exposes the File System Access API's folder chooser. */
62
- type DirectoryPickerRealm = typeof globalThis & { showDirectoryPicker: ShowDirectoryPicker };
63
-
64
- /**
65
- * Whether a realm carries `showDirectoryPicker`. The DOM lib this package compiles against does not
66
- * declare it (the API is Chromium-only), so the global is probed rather than read.
67
- */
68
- function hasDirectoryPicker(realm: typeof globalThis): realm is DirectoryPickerRealm {
69
- return "showDirectoryPicker" in realm && typeof realm.showDirectoryPicker === "function";
70
- }
71
-
72
- /** Whether this browser can open a native folder chooser at all. */
73
- export function canPickDirectory(): boolean {
74
- return hasDirectoryPicker(globalThis);
75
- }
76
-
77
- /** A location id: URL-safe, unguessable enough that a concurrent pick cannot collide. */
78
- function newLocationId(): string {
79
- const bytes = new Uint8Array(16);
80
- globalThis.crypto.getRandomValues(bytes);
81
- return [...bytes].map((b) => b.toString(16).padStart(2, "0")).join("");
82
- }
83
-
84
- /**
85
- * A thrown value's `name`, read structurally. A DOMException from another realm (an iframe's
86
- * `showDirectoryPicker`, a test double) is not `instanceof Error` here, but it still says what it
87
- * is.
88
- */
89
- function nameOf(cause: unknown): string {
90
- return isNonNullObject(cause) && "name" in cause && isString(cause.name) ? cause.name : "";
91
- }
92
-
93
- /**
94
- * Open the browser's native folder chooser and resolve the chosen folder's absolute path.
95
- *
96
- * Resolves `null` only when the user dismisses the chooser (an `AbortError`, which is also what a
97
- * declined readwrite grant reaches the page as: a user choice either way). Everything else rejects
98
- * with an Error whose message is a plain sentence the caller can show as is:
99
- *
100
- * - No `showDirectoryPicker` at all (the dev-server adapter never offers Browse… then, so only an
101
- * embedder wiring this helper by hand reaches it);
102
- * - The chooser refused to open (a `SecurityError` included: this runs synchronously from the click,
103
- * so a lost gesture means the environment is wrong, and silence would be a dead button);
104
- * - The folder cannot be written, so it cannot be tagged, and a project could not be created there
105
- * either;
106
- * - `locate` threw (backend unreachable, non-ok), or answered `null` (no folder holds the id).
107
- *
108
- * MUST be called synchronously from a user gesture; `showDirectoryPicker()` is invoked before this
109
- * function awaits anything so a click handler that calls it directly keeps the gesture.
110
- *
111
- * @param locate Ask the backend which directory holds `id`, given the handle's `name`. Resolve the
112
- * path, resolve `null` for "no directory holds this id", and throw on a transport or backend
113
- * failure. The not-found sentence assumes a backend that searches the home folder, as the dev
114
- * server does.
115
- */
116
- export async function pickDirectoryPath(
117
- locate: (query: { name: string; id: string }) => Promise<string | null>,
118
- ): Promise<string | null> {
119
- const realm = globalThis;
120
- if (!hasDirectoryPicker(realm)) {
121
- throw new TypeError(
122
- "This browser has no folder chooser. Type the folder's path into Location instead.",
123
- );
124
- }
125
- const show = realm.showDirectoryPicker;
126
-
127
- let handle: DirectoryHandle;
128
- try {
129
- // `id` makes Chrome reopen at the last folder chosen for this purpose across sessions.
130
- handle = await show({ id: "jx-new-project-location", mode: "readwrite", startIn: "documents" });
131
- } catch (error) {
132
- if (nameOf(error) === "AbortError") {
133
- return null;
134
- }
135
- throw new Error(
136
- `The folder chooser could not open (${errorMessage(error)}). Type the folder's path into Location instead.`,
137
- { cause: error },
138
- );
139
- }
140
-
141
- const { name } = handle;
142
- const id = newLocationId();
143
- /* The `getFileHandle({ create: true })` call puts an empty file on disk before anything is
144
- written, so a write that fails after it still leaves a tag to clean up. Only remove what this
145
- call created. */
146
- let tagged = false;
147
- try {
148
- try {
149
- const file = await handle.getFileHandle(LOCATION_ID_FILE, { create: true });
150
- tagged = true;
151
- const writable = await file.createWritable();
152
- await writable.write(id);
153
- await writable.close();
154
- } catch (error) {
155
- /* The readwrite grant was just issued, so this is the filesystem refusing: a read-only
156
- mount, OS permissions, a full disk. Creating the project there would fail the same way. */
157
- throw new Error(
158
- `Jx Studio can't write to "${name}" (${errorMessage(error)}), so it can't create a project there. Choose a folder you can write to.`,
159
- { cause: error },
160
- );
161
- }
162
-
163
- let path: string | null;
164
- try {
165
- path = await locate({ id, name });
166
- } catch (error) {
167
- throw new Error(
168
- `Jx Studio could not look up where "${name}" is: ${errorMessage(error)}. Type the folder's path into Location instead.`,
169
- { cause: error },
170
- );
171
- }
172
- if (!path) {
173
- throw new Error(
174
- `Jx Studio could not find where "${name}" is. From a browser it can only find folders inside your home folder. Type its full path into Location instead.`,
175
- );
176
- }
177
- return path;
178
- } finally {
179
- // The backend removes the file the moment it matches; this covers every path where it did not
180
- // (no match, a network failure, a backend that never ran the lookup, a half-finished write).
181
- if (tagged) {
182
- await handle.removeEntry(LOCATION_ID_FILE).catch(() => {
183
- /* Already gone — the backend beat us to it. */
184
- });
185
- }
186
- }
187
- }
@@ -1,77 +0,0 @@
1
- /**
2
- * Gated-registry.ts — state-aware tool advertisement for the AI assistant.
3
- *
4
- * Wraps a `@jxsuite/ai` ToolRegistry so each tool is only advertised to the LLM while its
5
- * availability predicate holds (no project open → bootstrap tools only; project open → file tools;
6
- * document open → document tools). `runAgentLoop` calls `listForLLM()` every round, so availability
7
- * flips mid-loop: after `create_project` succeeds, the very next round advertises the file and
8
- * document tools without any extra plumbing.
9
- *
10
- * Executing a tool whose predicate currently fails returns a tool error naming the missing state
11
- * instead of throwing, so the agent loop can self-correct.
12
- *
13
- * @license MIT
14
- */
15
-
16
- import type { ToolArguments, ToolContext, ToolDefinition, ToolRegistry } from "@jxsuite/ai/tools";
17
-
18
- export interface ToolAvailability {
19
- /** Whether the tool is currently advertised/executable. */
20
- when: () => boolean;
21
- /** Human-readable requirement, surfaced when execution is refused (e.g. "a project is open"). */
22
- requires: string;
23
- }
24
-
25
- /**
26
- * Wrap `inner` so tools listed in `availability` are only advertised/executable while their
27
- * predicate holds. Tools without an entry are always available.
28
- *
29
- * @param {ToolRegistry} inner
30
- * @param {Map<string, ToolAvailability>} availability
31
- * @returns {ToolRegistry}
32
- */
33
- export function createGatedToolRegistry(
34
- inner: ToolRegistry,
35
- availability: Map<string, ToolAvailability>,
36
- ): ToolRegistry {
37
- function isAvailable(name: string): boolean {
38
- const gate = availability.get(name);
39
- return gate ? gate.when() : true;
40
- }
41
-
42
- return {
43
- register(tool: ToolDefinition) {
44
- inner.register(tool);
45
- },
46
- list() {
47
- return inner.list().filter((t) => isAvailable(t.name));
48
- },
49
- listForLLM() {
50
- const available = new Set(this.list().map((t) => t.name));
51
- return inner.listForLLM().filter((t) => {
52
- /* SAFETY: a @jxsuite/ai registry lists each tool in OpenAI function-calling form,
53
- `{ type: "function", function: { name, … } }`; the registry interface types the entries
54
- as bare objects, and the optional chain still answers "" for any other shape. */
55
- const tool = t as { function?: { name?: string } };
56
- return available.has(tool.function?.name ?? "");
57
- });
58
- },
59
- getDefinition(toolName: string) {
60
- return inner.getDefinition(toolName);
61
- },
62
- validate(toolName: string, args: ToolArguments) {
63
- return inner.validate(toolName, args);
64
- },
65
- async execute(toolName: string, args: ToolArguments, ctx?: ToolContext) {
66
- if (inner.getDefinition(toolName) && !isAvailable(toolName)) {
67
- const gate = availability.get(toolName);
68
- return {
69
- success: false,
70
- error: `Tool "${toolName}" is not available right now — it requires ${gate?.requires ?? "a different studio state"}.`,
71
- };
72
- }
73
- // The call's context reaches the tool unchanged: its signal, id, ledger and session.
74
- return inner.execute(toolName, args, ctx);
75
- },
76
- };
77
- }