@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.
- package/dist/chunks/{2020-5wtzce5g.js → 2020-89fz3pfy.js} +3 -3
- package/dist/chunks/adapter-ay13gema.js +4 -0
- package/dist/chunks/add-new-flow-nmkqpjym.js +4 -0
- package/dist/chunks/cf-account-picker-2e06qxey.js +4 -0
- package/dist/chunks/cf-settings-5y04810c.js +4 -0
- package/dist/chunks/color-04361ttk.js +4 -0
- package/dist/chunks/{color-area-6rbra5dy.js → color-area-h8dbasp1.js} +2 -2
- package/dist/chunks/{color-field-gy56c94g.js → color-field-qv5r2b16.js} +3 -3
- package/dist/chunks/{color-slider-sqqdyf6c.js → color-slider-nz2mrzky.js} +2 -2
- package/dist/chunks/{combobox-72h32ejk.js → combobox-6hs00996.js} +2 -2
- package/dist/chunks/components-3vf75n83.js +4 -0
- package/dist/chunks/content-source-4f2raf4a.js +4 -0
- package/dist/chunks/{convert-to-repeater-he70fc2b.js → convert-to-repeater-j9hfcj4f.js} +3 -3
- package/dist/chunks/editor-645krvtz.js +4 -0
- package/dist/chunks/entry-commands-3qmv8wkr.js +4 -0
- package/dist/chunks/{extension-sections-0egb88dm.js → extension-sections-thfke054.js} +3 -3
- package/dist/chunks/file-ops-kkhp9ma7.js +4 -0
- package/dist/chunks/files-yswdhxrf.js +4 -0
- package/dist/chunks/format-host-4g8ex6bz.js +4 -0
- package/dist/chunks/fs-events-z9gpvc6t.js +4 -0
- package/dist/chunks/grid-open-fm4j183c.js +4 -0
- package/dist/chunks/{grid-view-qg1b5eqx.js → grid-view-7d2nx83y.js} +16 -15
- package/dist/chunks/{icons-63bjtbs7.js → icons-853n6qt3.js} +2 -2
- package/dist/chunks/{iframe-entry-pkgmcgp0.js → iframe-entry-2x9bn5ng.js} +4 -4
- package/dist/chunks/{iframe-entry-99nr5zfb.js → iframe-entry-42hpmwz5.js} +4 -4
- package/dist/chunks/{iframe-entry-76j8vwwx.js → iframe-entry-b5qgggyp.js} +2 -2
- package/dist/chunks/{iframe-entry-2zggzajv.js → iframe-entry-btck0e9s.js} +4 -4
- package/dist/chunks/iframe-host-q7fedsmr.js +4 -0
- package/dist/chunks/{index-32eqzv1k.js → index-jxmt7vh8.js} +3 -3
- package/dist/chunks/index-pt4wh98x.js +4 -0
- package/dist/chunks/{javascript-pjp734q4.js → javascript-vmqp56ke.js} +2 -2
- package/dist/chunks/{jsonMode-njkvsa02.js → jsonMode-n3wmpant.js} +5 -5
- package/dist/chunks/jx-validate-19y62knt.js +4 -0
- package/dist/chunks/layers-panel-1qvc83y9.js +4 -0
- package/dist/chunks/layers-qf0bc7ze.js +4 -0
- package/dist/chunks/{listbox-c421cf8c.js → listbox-7gka1h86.js} +2 -2
- package/dist/chunks/lit-html-wyqw04y2.js +4 -0
- package/dist/chunks/media-library-f9d5qcjb.js +4 -0
- package/dist/chunks/media-picker-rsb1fmk2.js +4 -0
- package/dist/chunks/{menu-bdpzbfvs.js → menu-d2jxpgma.js} +2 -2
- package/dist/chunks/{monaco-binding-vq1hgnn0.js → monaco-binding-wfhmz89d.js} +3 -3
- package/dist/chunks/monaco-buffer-ak0raadw.js +4 -0
- package/dist/chunks/{monaco-setup-xrd2cbkp.js → monaco-setup-fty7a8vg.js} +3 -3
- package/dist/chunks/notify-4qj70q0x.js +4 -0
- package/dist/chunks/parse-n8dfxnqf.js +4 -0
- package/dist/chunks/platform-ehg6t9vd.js +4 -0
- package/dist/chunks/{popover-b89sfej7.js → popover-9tf4cjrk.js} +2 -2
- package/dist/chunks/publish-panel-yhmda3cd.js +4 -0
- package/dist/chunks/{publish-quick-qx2455cj.js → publish-quick-12hh8926.js} +3 -3
- package/dist/chunks/publish-workspace-hx8qdv9p.js +4 -0
- package/dist/chunks/quick-search-r955m3q1.js +4 -0
- package/dist/chunks/references-8gxv0x7t.js +4 -0
- package/dist/chunks/right-panel-jarwqjpy.js +4 -0
- package/dist/chunks/source-control-84qt25m9.js +4 -0
- package/dist/chunks/{split-c9zfdgxf.js → split-m5rwn31z.js} +2 -2
- package/dist/chunks/studio-0nefxhar.js +7 -0
- package/dist/chunks/studio-0rg30pvs.js +5 -0
- package/dist/chunks/{studio-s32578pn.js → studio-14hh60j8.js} +4 -4
- package/dist/chunks/studio-1cfyd1ys.js +5 -0
- package/dist/chunks/studio-2bq7hheq.js +5 -0
- package/dist/chunks/{studio-zz9qe3rw.js → studio-32d8wyxq.js} +37 -37
- package/dist/chunks/studio-3hpcawh8.js +4 -0
- package/dist/chunks/{studio-svb7yyve.js → studio-4j35hgsw.js} +4 -4
- package/dist/chunks/{studio-x1x167jh.js → studio-4qg61g9j.js} +4 -4
- package/dist/chunks/studio-4rgem12q.js +5 -0
- package/dist/chunks/studio-6k9djnxw.js +5 -0
- package/dist/chunks/studio-6rbaedw2.js +9 -0
- package/dist/chunks/studio-732mw0g8.js +5 -0
- package/dist/chunks/studio-79r8msnr.js +7 -0
- package/dist/chunks/studio-7q6jdd4h.js +5 -0
- package/dist/chunks/{studio-r5qy30vx.js → studio-8hf8qcm1.js} +4 -4
- package/dist/chunks/{studio-8tr6cfrc.js → studio-8pne0rx4.js} +4 -4
- package/dist/chunks/{studio-y0n7yxea.js → studio-9179bsm7.js} +7 -7
- package/dist/chunks/{studio-pbxa5fpc.js → studio-9ff0vqff.js} +3 -3
- package/dist/chunks/studio-9gzvypw4.js +5 -0
- package/dist/chunks/studio-9ndgd55w.js +5 -0
- package/dist/chunks/studio-9ra9e7m4.js +5 -0
- package/dist/chunks/studio-a6e5fzve.js +5 -0
- package/dist/chunks/studio-a9k86nq6.js +5 -0
- package/dist/chunks/studio-ae7a69jc.js +6 -0
- package/dist/chunks/studio-aghsg0ds.js +7 -0
- package/dist/chunks/{studio-k3xxbagr.js → studio-ajw4k8hp.js} +3 -3
- package/dist/chunks/{studio-qmn1xc24.js → studio-apapy17g.js} +4 -4
- package/dist/chunks/{studio-8m0y2qe7.js → studio-by8x0jvc.js} +4 -4
- package/dist/chunks/{studio-wbcjbhy1.js → studio-c88gq88j.js} +5 -5
- package/dist/chunks/{studio-n0c5h6mf.js → studio-cg54vtet.js} +4 -4
- package/dist/chunks/studio-dvhxxxbd.js +5 -0
- package/dist/chunks/{studio-3q40e3rj.js → studio-e4rq6x0q.js} +4 -4
- package/dist/chunks/{studio-fbhxxc5f.js → studio-ekzbgnjp.js} +4 -4
- package/dist/chunks/studio-fg4fj9ec.js +10 -0
- package/dist/chunks/studio-fj6jzgxw.js +5 -0
- package/dist/chunks/{studio-nh6d342n.js → studio-g45r5vm8.js} +7 -7
- package/dist/chunks/{studio-a2bvaptz.js → studio-gczve8zf.js} +4 -4
- package/dist/chunks/studio-ge08zwpv.js +5 -0
- package/dist/chunks/studio-gk9yk8dj.js +12 -0
- package/dist/chunks/{studio-bbe4c90a.js → studio-gskkgfe3.js} +4 -4
- package/dist/chunks/{studio-83mk4bgg.js → studio-h4jbgne2.js} +8 -8
- package/dist/chunks/{studio-b7y5mr30.js → studio-hcy9zeac.js} +4 -4
- package/dist/chunks/studio-ht81xjkr.js +5 -0
- package/dist/chunks/{studio-jaaz8ce9.js → studio-jr30g4ae.js} +4 -4
- package/dist/chunks/{studio-qgh13jtz.js → studio-k5n4es74.js} +4 -4
- package/dist/chunks/{studio-ccv2svc6.js → studio-k6hbx2fc.js} +4 -4
- package/dist/chunks/{studio-9tmebf0s.js → studio-kqh7fxn6.js} +4 -4
- package/dist/chunks/{studio-q0mvbaq6.js → studio-kyw47pet.js} +4 -4
- package/dist/chunks/studio-mkw17ngg.js +5 -0
- package/dist/chunks/{studio-x9hsze61.js → studio-myb4mf0h.js} +4 -4
- package/dist/chunks/studio-n4qq1n57.js +5 -0
- package/dist/chunks/studio-nfmpmkqb.js +5 -0
- package/dist/chunks/studio-nfwacw31.js +5 -0
- package/dist/chunks/{studio-rthyg42z.js → studio-nm86874b.js} +172 -172
- package/dist/chunks/studio-nqexty8x.js +16 -0
- package/dist/chunks/studio-nr0kcqc6.js +5 -0
- package/dist/chunks/{studio-jsgf2q5k.js → studio-nvfa137z.js} +5 -5
- package/dist/chunks/studio-psh8mwnw.js +4 -0
- package/dist/chunks/studio-qd0cycm4.js +9 -0
- package/dist/chunks/{studio-tfkznv39.js → studio-qevf5z9d.js} +4 -4
- package/dist/chunks/studio-qs5fmhtj.js +14 -0
- package/dist/chunks/studio-ry4789ng.js +7 -0
- package/dist/chunks/studio-s4fq9jht.js +5 -0
- package/dist/chunks/studio-sc35ttdk.js +5 -0
- package/dist/chunks/studio-sm2f0sc6.js +6 -0
- package/dist/chunks/{studio-3qeh9t0h.js → studio-spza69tb.js} +5 -5
- package/dist/chunks/studio-t6dqnnft.js +5 -0
- package/dist/chunks/{studio-43sd2mam.js → studio-tfmhnwyr.js} +3 -3
- package/dist/chunks/studio-tn7v8gey.js +6 -0
- package/dist/chunks/{studio-tft6r105.js → studio-trcfer2b.js} +4 -4
- package/dist/chunks/studio-tyw7tk0x.js +5 -0
- package/dist/chunks/{studio-jcnsdaep.js → studio-tyzgte21.js} +4 -4
- package/dist/chunks/studio-vtey9w98.js +5 -0
- package/dist/chunks/studio-ws2r1d7e.js +5 -0
- package/dist/chunks/studio-x2trkhk5.js +5 -0
- package/dist/chunks/{studio-mn0476ag.js → studio-xzgp0cp7.js} +4 -4
- package/dist/chunks/studio-y47090vq.js +5 -0
- package/dist/chunks/{studio-5g0rprdt.js → studio-yjh9gddd.js} +5 -5
- package/dist/chunks/studio-ywf9y1e2.js +5 -0
- package/dist/chunks/{studio-k7evxqh4.js → studio-z1p91z5a.js} +4 -4
- package/dist/chunks/studio-ztppbam0.js +5 -0
- package/dist/chunks/{toast-host-1yg5ar8d.js → toast-host-g3r2s0kn.js} +2 -2
- package/dist/chunks/toast-w11qpkyz.js +4 -0
- package/dist/chunks/{tooltip-yypz28gq.js → tooltip-dj1pvgz7.js} +2 -2
- package/dist/chunks/{tree-9xa4hm7n.js → tree-j0ebwr5s.js} +2 -2
- package/dist/chunks/{tsMode-epyfz1hv.js → tsMode-byje4a01.js} +5 -5
- package/dist/chunks/workspace-7q4arnt3.js +4 -0
- package/dist/chunks/workspace-home-ge027kr6.js +4 -0
- package/dist/chunks/{ws-client-bv552sfx.js → ws-client-h09h6sqg.js} +3 -3
- package/dist/iframe-entry.js +14 -14
- package/dist/manifest.json +76 -76
- package/dist/studio.js +88 -88
- package/package.json +15 -16
- package/src/browse/component-examples.ts +15 -224
- package/src/browse/library-preview.ts +6 -13
- package/src/browse/preview-root.ts +7 -4
- package/src/canvas/canvas-utils.ts +18 -4
- package/src/canvas/iframe-editable-root.ts +13 -4
- package/src/canvas/iframe-entry.ts +3 -1
- package/src/canvas/iframe-interaction.ts +11 -2
- package/src/canvas/iframe-render.ts +25 -3
- package/src/canvas/preview-placeholder.ts +120 -0
- package/src/collab/collab-session.ts +6 -2
- package/src/commands/context.ts +4 -0
- package/src/commands/defaults.ts +2 -0
- package/src/commands/live-context.ts +2 -0
- package/src/editor/shortcuts.ts +3 -0
- package/src/files/files.ts +6 -3
- package/src/new-project/new-project-modal.ts +1 -4
- package/src/panels/ai-panel.ts +27 -34
- package/src/panels/component-preview.ts +5 -0
- package/src/panels/pane-context.ts +43 -18
- package/src/panels/site-model.ts +7 -19
- package/src/panels/site-panel.ts +2 -25
- package/src/panels/stylebook-doc.ts +2 -53
- package/src/platform.ts +20 -0
- package/src/platforms/devserver.ts +2 -120
- package/src/services/ai-ask.ts +29 -171
- package/src/services/ai-command-tools.ts +40 -50
- package/src/services/ai-import-tools.ts +4 -5
- package/src/services/ai-project-tools.ts +13 -3
- package/src/services/ai-tools.ts +26 -564
- package/src/services/ai-writes.ts +84 -2
- package/src/services/document-assistant.ts +29 -58
- package/src/services/harness/assistant-tools.ts +68 -0
- package/src/services/harness/interaction-store.ts +118 -0
- package/src/services/harness/studio-documents.ts +105 -0
- package/src/services/harness/turn-hooks.ts +1 -6
- package/src/services/project-adoption.ts +8 -16
- package/src/services/references.ts +0 -0
- package/src/services/tool-executor.ts +25 -22
- package/src/state.ts +3 -13
- package/src/style/stylebook-specimens.ts +11 -15
- package/src/surfaces/files-panel.json +12 -0
- package/src/surfaces/files-panel.ts +5 -2
- package/src/surfaces/pane-context.json +286 -265
- package/src/surfaces/pane-context.ts +17 -8
- package/src/surfaces/welcome.ts +4 -1
- package/src/tabs/selection.ts +14 -109
- package/src/tabs/tab.ts +15 -7
- package/src/tabs/transact.ts +316 -124
- package/src/types.ts +13 -8
- package/src/utils/geometry.ts +49 -2
- package/dist/chunks/adapter-5tphg7xe.js +0 -4
- package/dist/chunks/add-new-flow-psym14sn.js +0 -4
- package/dist/chunks/cf-account-picker-haqazq5r.js +0 -4
- package/dist/chunks/cf-settings-h2wrh6hq.js +0 -4
- package/dist/chunks/color-m7r1vk54.js +0 -4
- package/dist/chunks/components-r22xca09.js +0 -4
- package/dist/chunks/content-source-mmppmcyg.js +0 -4
- package/dist/chunks/editor-ccd9jwsa.js +0 -4
- package/dist/chunks/entry-commands-6qxz9e8p.js +0 -4
- package/dist/chunks/file-ops-qnab36ek.js +0 -4
- package/dist/chunks/files-rcn56qvm.js +0 -4
- package/dist/chunks/format-host-yxz0kjjs.js +0 -4
- package/dist/chunks/fs-events-d1qxq4z4.js +0 -4
- package/dist/chunks/grid-open-4z1g062t.js +0 -4
- package/dist/chunks/iframe-host-ssv7kyj0.js +0 -4
- package/dist/chunks/index-f0jn7nnp.js +0 -4
- package/dist/chunks/jx-validate-ttasrbh7.js +0 -4
- package/dist/chunks/layers-bgyr11tr.js +0 -4
- package/dist/chunks/layers-panel-7ez66zeq.js +0 -4
- package/dist/chunks/lit-html-v9gh687x.js +0 -4
- package/dist/chunks/media-library-sq468wts.js +0 -4
- package/dist/chunks/media-picker-shfe59pt.js +0 -4
- package/dist/chunks/monaco-buffer-3afgwvbp.js +0 -4
- package/dist/chunks/notify-kzrjr3fv.js +0 -4
- package/dist/chunks/parse-v77xtwv7.js +0 -4
- package/dist/chunks/platform-7v4rtd7m.js +0 -4
- package/dist/chunks/publish-panel-2z9gw8ee.js +0 -4
- package/dist/chunks/publish-workspace-wrc1dgwx.js +0 -4
- package/dist/chunks/quick-search-n0t0eqxb.js +0 -4
- package/dist/chunks/references-9547rg5s.js +0 -4
- package/dist/chunks/right-panel-wsgnr9pn.js +0 -4
- package/dist/chunks/source-control-kwktr9y6.js +0 -4
- package/dist/chunks/studio-03ct6yfv.js +0 -9
- package/dist/chunks/studio-07rcfje3.js +0 -5
- package/dist/chunks/studio-3e54qh39.js +0 -7
- package/dist/chunks/studio-3exxhz5g.js +0 -5
- package/dist/chunks/studio-3qqjeep5.js +0 -6
- package/dist/chunks/studio-4hp8pkyt.js +0 -5
- package/dist/chunks/studio-58xg6j2q.js +0 -9
- package/dist/chunks/studio-6m380wp7.js +0 -5
- package/dist/chunks/studio-80jyh1xh.js +0 -4
- package/dist/chunks/studio-8vcvfs7r.js +0 -6
- package/dist/chunks/studio-bavb31r0.js +0 -5
- package/dist/chunks/studio-c05wdnt3.js +0 -5
- package/dist/chunks/studio-d5grtb9e.js +0 -16
- package/dist/chunks/studio-df0baea9.js +0 -5
- package/dist/chunks/studio-dgk4dqtx.js +0 -7
- package/dist/chunks/studio-e1g64q43.js +0 -5
- package/dist/chunks/studio-ed1hp6zf.js +0 -6
- package/dist/chunks/studio-ed3xx40h.js +0 -7
- package/dist/chunks/studio-f14e1zdj.js +0 -5
- package/dist/chunks/studio-fyh0jc96.js +0 -5
- package/dist/chunks/studio-gcxk20n7.js +0 -5
- package/dist/chunks/studio-kt3pjkwd.js +0 -5
- package/dist/chunks/studio-m8g16bgz.js +0 -5
- package/dist/chunks/studio-mx1smyq0.js +0 -5
- package/dist/chunks/studio-nqxqf8p2.js +0 -4
- package/dist/chunks/studio-p4nrkvqr.js +0 -5
- package/dist/chunks/studio-pgmvdv7z.js +0 -7
- package/dist/chunks/studio-ph84avkf.js +0 -5
- package/dist/chunks/studio-q0tccd43.js +0 -5
- package/dist/chunks/studio-q7rtx6zq.js +0 -14
- package/dist/chunks/studio-qwdqwkj0.js +0 -5
- package/dist/chunks/studio-rj9eamf8.js +0 -5
- package/dist/chunks/studio-rptgsk96.js +0 -5
- package/dist/chunks/studio-s19tp1xr.js +0 -5
- package/dist/chunks/studio-syreb644.js +0 -5
- package/dist/chunks/studio-tf9jryb1.js +0 -10
- package/dist/chunks/studio-txf2kysx.js +0 -5
- package/dist/chunks/studio-v8867ddv.js +0 -12
- package/dist/chunks/studio-vxq7fcvv.js +0 -5
- package/dist/chunks/studio-w4rz9hdk.js +0 -5
- package/dist/chunks/studio-wc649wss.js +0 -5
- package/dist/chunks/studio-wys23mxz.js +0 -5
- package/dist/chunks/studio-y3ahhdpx.js +0 -5
- package/dist/chunks/studio-z97c48fk.js +0 -5
- package/dist/chunks/studio-zat62m0c.js +0 -5
- package/dist/chunks/studio-zcd2e7yz.js +0 -5
- package/dist/chunks/toast-1gv0h53h.js +0 -4
- package/dist/chunks/workspace-dz7nxfhv.js +0 -4
- package/dist/chunks/workspace-home-xsd7hkjw.js +0 -4
- package/src/services/directory-picker.ts +0 -187
- package/src/services/gated-registry.ts +0 -77
package/src/services/ai-tools.ts
CHANGED
|
@@ -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.
|
|
5
|
-
*
|
|
6
|
-
* edits (specs/ai.md §3.1 and §3.2 — optimistic apply, undo as the
|
|
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
|
|
13
|
-
import
|
|
14
|
-
import {
|
|
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
|
-
|
|
32
|
-
|
|
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
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
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.",
|