@jxsuite/studio 8.1.0 → 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-dg097k3e.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-vh9cew51.js → color-area-h8dbasp1.js} +2 -2
- package/dist/chunks/{color-field-e78f16bs.js → color-field-qv5r2b16.js} +3 -3
- package/dist/chunks/{color-slider-fg4yhvk6.js → color-slider-nz2mrzky.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-grnmea6c.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-rp7fkega.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-x26rfpz1.js → grid-view-7d2nx83y.js} +10 -10
- package/dist/chunks/{iframe-entry-pkgmcgp0.js → iframe-entry-2x9bn5ng.js} +4 -4
- package/dist/chunks/{iframe-entry-02wzcyvy.js → iframe-entry-btck0e9s.js} +4 -4
- package/dist/chunks/iframe-host-q7fedsmr.js +4 -0
- package/dist/chunks/{index-203h71wy.js → index-jxmt7vh8.js} +3 -3
- package/dist/chunks/index-pt4wh98x.js +4 -0
- package/dist/chunks/{javascript-ww5pv5ae.js → javascript-vmqp56ke.js} +2 -2
- package/dist/chunks/{jsonMode-vwbdwwq3.js → jsonMode-n3wmpant.js} +6 -6
- 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/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/{monaco-binding-63tqze27.js → monaco-binding-wfhmz89d.js} +3 -3
- package/dist/chunks/monaco-buffer-ak0raadw.js +4 -0
- package/dist/chunks/{monaco-setup-zyyynnj8.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/publish-panel-yhmda3cd.js +4 -0
- package/dist/chunks/{publish-quick-ajzdhhs6.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/studio-0nefxhar.js +7 -0
- package/dist/chunks/studio-0rg30pvs.js +5 -0
- package/dist/chunks/{studio-3q6n9qvq.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-mmqr9awv.js → studio-32d8wyxq.js} +33 -33
- package/dist/chunks/studio-3hpcawh8.js +4 -0
- package/dist/chunks/{studio-zm7dnr9g.js → studio-4j35hgsw.js} +4 -4
- package/dist/chunks/{studio-bqgs90r8.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-jqrg9xxg.js → studio-881z1xrx.js} +4 -4
- package/dist/chunks/{studio-9wyqyfqy.js → studio-8hf8qcm1.js} +4 -4
- package/dist/chunks/{studio-6vrxzkba.js → studio-8pne0rx4.js} +4 -4
- package/dist/chunks/{studio-wcakp5bt.js → studio-9179bsm7.js} +7 -7
- package/dist/chunks/{studio-xddgbwhe.js → studio-9ff0vqff.js} +3 -3
- package/dist/chunks/{studio-25a5by3h.js → studio-9gzvypw4.js} +4 -4
- 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-2b8443ms.js → studio-ajw4k8hp.js} +3 -3
- package/dist/chunks/{studio-957jg8g3.js → studio-apapy17g.js} +4 -4
- package/dist/chunks/{studio-hnx1h1pr.js → studio-by8x0jvc.js} +4 -4
- package/dist/chunks/{studio-hxq1ebp0.js → studio-c88gq88j.js} +6 -6
- package/dist/chunks/{studio-a6x058ew.js → studio-cg54vtet.js} +4 -4
- package/dist/chunks/studio-dvhxxxbd.js +5 -0
- package/dist/chunks/{studio-1meqbxns.js → studio-e4rq6x0q.js} +4 -4
- package/dist/chunks/{studio-v2ek69cb.js → studio-ekzbgnjp.js} +4 -4
- package/dist/chunks/{studio-2dtc90zs.js → studio-fg4fj9ec.js} +6 -6
- package/dist/chunks/studio-fj6jzgxw.js +5 -0
- package/dist/chunks/{studio-txkx6586.js → studio-g45r5vm8.js} +7 -7
- package/dist/chunks/{studio-v0gsjav0.js → studio-gczve8zf.js} +4 -4
- package/dist/chunks/studio-ge08zwpv.js +5 -0
- package/dist/chunks/{studio-1gdfsr49.js → studio-gk9yk8dj.js} +7 -7
- package/dist/chunks/{studio-cpdzrdgp.js → studio-gskkgfe3.js} +4 -4
- package/dist/chunks/{studio-94ppxeqy.js → studio-h4jbgne2.js} +8 -8
- package/dist/chunks/{studio-kdseew9m.js → studio-hcy9zeac.js} +4 -4
- package/dist/chunks/studio-ht81xjkr.js +5 -0
- package/dist/chunks/{studio-73wz63nn.js → studio-jr30g4ae.js} +4 -4
- package/dist/chunks/{studio-fqs1a22a.js → studio-k5n4es74.js} +4 -4
- package/dist/chunks/{studio-j5jqvyn3.js → studio-k6hbx2fc.js} +4 -4
- package/dist/chunks/{studio-332hesdk.js → studio-kqh7fxn6.js} +4 -4
- package/dist/chunks/{studio-8nyw10dp.js → studio-kyw47pet.js} +4 -4
- package/dist/chunks/studio-mkw17ngg.js +5 -0
- package/dist/chunks/{studio-02phtmqn.js → studio-myb4mf0h.js} +4 -4
- package/dist/chunks/{studio-d140sy8e.js → studio-n4qq1n57.js} +4 -4
- package/dist/chunks/{studio-bf0rgyf7.js → studio-nfmpmkqb.js} +4 -4
- package/dist/chunks/studio-nfwacw31.js +5 -0
- package/dist/chunks/{studio-z42kysg7.js → studio-nm86874b.js} +136 -136
- package/dist/chunks/studio-nqexty8x.js +16 -0
- package/dist/chunks/studio-nr0kcqc6.js +5 -0
- package/dist/chunks/{studio-dmn7gpba.js → studio-nvfa137z.js} +5 -5
- package/dist/chunks/studio-psh8mwnw.js +4 -0
- package/dist/chunks/{studio-4dsdnan2.js → studio-qd0cycm4.js} +5 -5
- package/dist/chunks/{studio-zht0x0v3.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-rdnezzkd.js → studio-spza69tb.js} +5 -5
- package/dist/chunks/studio-t6dqnnft.js +5 -0
- package/dist/chunks/{studio-hw60n5dh.js → studio-tfmhnwyr.js} +3 -3
- package/dist/chunks/studio-tn7v8gey.js +6 -0
- package/dist/chunks/{studio-nsv7g1qn.js → studio-trcfer2b.js} +4 -4
- package/dist/chunks/studio-tyw7tk0x.js +5 -0
- package/dist/chunks/{studio-533py3yy.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-c5dj3dzk.js → studio-xzgp0cp7.js} +4 -4
- package/dist/chunks/studio-y47090vq.js +5 -0
- package/dist/chunks/{studio-nm9qev4p.js → studio-yjh9gddd.js} +5 -5
- package/dist/chunks/studio-ywf9y1e2.js +5 -0
- package/dist/chunks/{studio-x3ge4rts.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/{tsMode-vra2hh7y.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-72x5n99j.js → ws-client-h09h6sqg.js} +3 -3
- package/dist/iframe-entry.js +13 -13
- package/dist/manifest.json +77 -77
- package/dist/studio.js +89 -89
- package/package.json +11 -12
- 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/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/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/tool-executor.ts +25 -22
- package/src/state.ts +3 -13
- package/src/surfaces/files-panel.json +12 -0
- package/src/surfaces/files-panel.ts +5 -2
- package/src/surfaces/welcome.ts +4 -1
- package/src/tabs/selection.ts +14 -109
- package/src/tabs/tab.ts +10 -1
- package/src/tabs/transact.ts +316 -124
- package/src/types.ts +13 -8
- package/dist/chunks/adapter-tck81zft.js +0 -4
- package/dist/chunks/add-new-flow-a1w0bnm6.js +0 -4
- package/dist/chunks/cf-account-picker-ce3kp3x1.js +0 -4
- package/dist/chunks/cf-settings-7ye6x7g2.js +0 -4
- package/dist/chunks/color-4gj49xq6.js +0 -4
- package/dist/chunks/components-qce0v93f.js +0 -4
- package/dist/chunks/content-source-jjx8kgzr.js +0 -4
- package/dist/chunks/editor-bkja5dbv.js +0 -4
- package/dist/chunks/entry-commands-1wh0btvg.js +0 -4
- package/dist/chunks/file-ops-6pe4z7an.js +0 -4
- package/dist/chunks/files-ybpp3j38.js +0 -4
- package/dist/chunks/format-host-3mtbhxk1.js +0 -4
- package/dist/chunks/fs-events-xfanfxds.js +0 -4
- package/dist/chunks/grid-open-dg5qmnaj.js +0 -4
- package/dist/chunks/iframe-host-j2bk2xkc.js +0 -4
- package/dist/chunks/index-fkrm7pdk.js +0 -4
- package/dist/chunks/jx-validate-w7v32f6a.js +0 -4
- package/dist/chunks/layers-g7j732cm.js +0 -4
- package/dist/chunks/layers-panel-gjrsxcck.js +0 -4
- package/dist/chunks/lit-html-jdfz9rhc.js +0 -4
- package/dist/chunks/media-library-9j0v4wmk.js +0 -4
- package/dist/chunks/media-picker-cpyyt2m0.js +0 -4
- package/dist/chunks/monaco-buffer-9wcme176.js +0 -4
- package/dist/chunks/notify-rahnnnv4.js +0 -4
- package/dist/chunks/parse-v77xtwv7.js +0 -4
- package/dist/chunks/platform-b2aqv7v5.js +0 -4
- package/dist/chunks/publish-panel-4yk2461b.js +0 -4
- package/dist/chunks/publish-workspace-mxestzcf.js +0 -4
- package/dist/chunks/quick-search-9g6z65b0.js +0 -4
- package/dist/chunks/references-c26enpxs.js +0 -4
- package/dist/chunks/right-panel-9d0e0yg7.js +0 -4
- package/dist/chunks/source-control-xyn3a8ds.js +0 -4
- package/dist/chunks/studio-0khd0xdw.js +0 -5
- package/dist/chunks/studio-1fvzq9vs.js +0 -5
- package/dist/chunks/studio-1yhz0s8p.js +0 -5
- package/dist/chunks/studio-2c2mjyzj.js +0 -6
- package/dist/chunks/studio-3pndhat0.js +0 -9
- package/dist/chunks/studio-4fjqdzrb.js +0 -6
- package/dist/chunks/studio-5abge3wy.js +0 -5
- package/dist/chunks/studio-74nzv280.js +0 -5
- package/dist/chunks/studio-7cg75gtj.js +0 -14
- package/dist/chunks/studio-89cwmnae.js +0 -5
- package/dist/chunks/studio-8m1t83kn.js +0 -7
- package/dist/chunks/studio-a3evp7f6.js +0 -6
- package/dist/chunks/studio-ana67wax.js +0 -5
- package/dist/chunks/studio-b4xhpcwy.js +0 -4
- package/dist/chunks/studio-c4rcaftm.js +0 -5
- package/dist/chunks/studio-cgjt1zce.js +0 -5
- package/dist/chunks/studio-cq87hree.js +0 -5
- package/dist/chunks/studio-e1g64q43.js +0 -5
- package/dist/chunks/studio-e8pzjjhf.js +0 -5
- package/dist/chunks/studio-f8kz3krj.js +0 -7
- package/dist/chunks/studio-fw3jp9m7.js +0 -5
- package/dist/chunks/studio-g6ysm05p.js +0 -7
- package/dist/chunks/studio-gmhd0j3f.js +0 -5
- package/dist/chunks/studio-gqpty626.js +0 -5
- package/dist/chunks/studio-grp684wx.js +0 -5
- package/dist/chunks/studio-h98520qr.js +0 -5
- package/dist/chunks/studio-hvxt8z79.js +0 -5
- package/dist/chunks/studio-j4e627w1.js +0 -5
- package/dist/chunks/studio-kt2j51sa.js +0 -5
- package/dist/chunks/studio-m4w6sygj.js +0 -16
- package/dist/chunks/studio-nqxqf8p2.js +0 -4
- package/dist/chunks/studio-p89w12s2.js +0 -7
- package/dist/chunks/studio-pbagym4j.js +0 -5
- package/dist/chunks/studio-pr23qhrw.js +0 -5
- package/dist/chunks/studio-r4ya3sb2.js +0 -5
- package/dist/chunks/studio-rm9m3z60.js +0 -5
- package/dist/chunks/studio-tpmnbgmt.js +0 -5
- package/dist/chunks/studio-ttxhq5yt.js +0 -5
- package/dist/chunks/studio-y1x65h5r.js +0 -5
- package/dist/chunks/studio-zf188hnd.js +0 -5
- package/dist/chunks/toast-1gv0h53h.js +0 -4
- package/dist/chunks/workspace-3nt93k48.js +0 -4
- package/dist/chunks/workspace-home-zbgyqqws.js +0 -4
- package/src/services/directory-picker.ts +0 -187
- package/src/services/gated-registry.ts +0 -77
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jxsuite/studio",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "9.0.0",
|
|
4
4
|
"description": "Jx Studio — visual builder for Jx documents",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -31,7 +31,6 @@
|
|
|
31
31
|
"./platform": "./src/platform.ts",
|
|
32
32
|
"./types": "./src/types.ts",
|
|
33
33
|
"./base64": "./src/utils/base64.ts",
|
|
34
|
-
"./directory-picker": "./src/services/directory-picker.ts",
|
|
35
34
|
"./import-client": "./src/services/import-client.ts",
|
|
36
35
|
"./github-auth": "./src/github/github-auth.ts",
|
|
37
36
|
"./github-auth-status": "./src/github/github-auth-status.ts",
|
|
@@ -61,16 +60,16 @@
|
|
|
61
60
|
"dependencies": {
|
|
62
61
|
"@atlaskit/pragmatic-drag-and-drop": "^4.0.0",
|
|
63
62
|
"@atlaskit/pragmatic-drag-and-drop-hitbox": "^3.0.0",
|
|
64
|
-
"@jxsuite/ai": "^0.
|
|
65
|
-
"@jxsuite/collab": "^0.11.
|
|
66
|
-
"@jxsuite/create": "^1.4.
|
|
67
|
-
"@jxsuite/formulas": "^0.0.
|
|
68
|
-
"@jxsuite/markup": "^1.0.
|
|
69
|
-
"@jxsuite/protocol": "^
|
|
70
|
-
"@jxsuite/runtime": "^
|
|
71
|
-
"@jxsuite/schema": "^4.
|
|
72
|
-
"@jxsuite/site": "^2.2.
|
|
73
|
-
"@jxsuite/ui": "^0.8.
|
|
63
|
+
"@jxsuite/ai": "^0.41.0",
|
|
64
|
+
"@jxsuite/collab": "^0.11.3",
|
|
65
|
+
"@jxsuite/create": "^1.4.1",
|
|
66
|
+
"@jxsuite/formulas": "^0.0.25",
|
|
67
|
+
"@jxsuite/markup": "^1.0.1",
|
|
68
|
+
"@jxsuite/protocol": "^5.0.0",
|
|
69
|
+
"@jxsuite/runtime": "^6.0.0",
|
|
70
|
+
"@jxsuite/schema": "^4.1.0",
|
|
71
|
+
"@jxsuite/site": "^2.2.1",
|
|
72
|
+
"@jxsuite/ui": "^0.8.1",
|
|
74
73
|
"@vue/reactivity": "3.5.44",
|
|
75
74
|
"ajv": "^8.20.0",
|
|
76
75
|
"ajv-formats": "^3.0.1",
|
|
@@ -26,7 +26,7 @@ import { jsonClone } from "../utils/studio-utils";
|
|
|
26
26
|
import { cloneSelection } from "../tabs/selection";
|
|
27
27
|
import {
|
|
28
28
|
applyExternalDocOps,
|
|
29
|
-
|
|
29
|
+
isBatched,
|
|
30
30
|
setBatchEndNotifier,
|
|
31
31
|
setHistoryDelegate,
|
|
32
32
|
setTransactGate,
|
|
@@ -318,7 +318,11 @@ function onBatchEnd(tab: Tab): void {
|
|
|
318
318
|
|
|
319
319
|
function publishRecord(session: ActiveSession, record: TransactionRecord): void {
|
|
320
320
|
const forward = record.docOps.map((pair) => pair.forward);
|
|
321
|
-
|
|
321
|
+
/* Buffered only while THIS tab has a lane segment open, which means the transaction just
|
|
322
|
+
observed was the lane owner's: anyone else's write closes the segment before it runs, and the
|
|
323
|
+
close flushes the buffer, so the Y document receives the owner's ops before the other writer's
|
|
324
|
+
and in local order. */
|
|
325
|
+
if (isBatched(session.tab)) {
|
|
322
326
|
session.batchOps ??= [];
|
|
323
327
|
if (forward.length === 0) {
|
|
324
328
|
session.batchNeedsDiff = true;
|
package/src/commands/context.ts
CHANGED
|
@@ -31,6 +31,9 @@ export const CAPABILITIES = [
|
|
|
31
31
|
"gitClone",
|
|
32
32
|
"importSite",
|
|
33
33
|
"openProjectInNewWindow",
|
|
34
|
+
/* Open Project's gate: a native dialog or the cloud's repository picker. A dev-server window opens
|
|
35
|
+
its project from `?project=`, so the command is not offered there. */
|
|
36
|
+
"openProject",
|
|
34
37
|
"dataRows",
|
|
35
38
|
"windowControls",
|
|
36
39
|
"findReferences",
|
|
@@ -339,6 +342,7 @@ export function emptyContext(): CommandContext {
|
|
|
339
342
|
gitClone: false,
|
|
340
343
|
importSite: false,
|
|
341
344
|
openProjectInNewWindow: false,
|
|
345
|
+
openProject: false,
|
|
342
346
|
dataRows: false,
|
|
343
347
|
windowControls: false,
|
|
344
348
|
findReferences: false,
|
package/src/commands/defaults.ts
CHANGED
|
@@ -563,6 +563,8 @@ export function defaultCommands(deps: CommandDeps): AnyCommand[] {
|
|
|
563
563
|
keybinding: "mod+o",
|
|
564
564
|
menus: ["commandbar/overflow", "statusbar/project", "palette"],
|
|
565
565
|
group: "1_file",
|
|
566
|
+
when: (ctx: CommandContext) => ctx.capability.openProject,
|
|
567
|
+
requires: "a host that can open a project from a dialog",
|
|
566
568
|
run: () => deps.openProject(),
|
|
567
569
|
},
|
|
568
570
|
{
|
|
@@ -38,6 +38,7 @@ import { collabState } from "../collab/collab-state";
|
|
|
38
38
|
|
|
39
39
|
import { canvasViewForMode, editorKindForMode, emptyContext } from "./context";
|
|
40
40
|
import type { CommandContext } from "./context";
|
|
41
|
+
import { canOpenProject } from "../platform";
|
|
41
42
|
import type { StudioPlatform } from "../types";
|
|
42
43
|
|
|
43
44
|
/** Elements that own the keyboard while focused. Matches the old listener's target test exactly. */
|
|
@@ -95,6 +96,7 @@ function capabilities(platform: StudioPlatform | null): CommandContext["capabili
|
|
|
95
96
|
findReferences: has("findReferences"),
|
|
96
97
|
gitClone: has("gitClone"),
|
|
97
98
|
importSite: has("importSite"),
|
|
99
|
+
openProject: canOpenProject(platform),
|
|
98
100
|
openProjectInNewWindow: has("openProjectInNewWindow"),
|
|
99
101
|
readFileBytes: has("readFileBytes"),
|
|
100
102
|
publish: has("cfApi"),
|
package/src/files/files.ts
CHANGED
|
@@ -39,7 +39,7 @@ import { errorMessage } from "@jxsuite/schema/parse";
|
|
|
39
39
|
import { localeLabel, localeOfPath, resolveI18n } from "@jxsuite/schema/locale";
|
|
40
40
|
import { showPromptDialog } from "../ui/layers";
|
|
41
41
|
import { projectState, requireProjectState, setProjectState } from "../store";
|
|
42
|
-
import { getPlatform } from "../platform";
|
|
42
|
+
import { canOpenProject, getPlatform } from "../platform";
|
|
43
43
|
import { disarmPreviewOverlay } from "../preview/preview-overlay";
|
|
44
44
|
import { notify } from "../services/notify";
|
|
45
45
|
import { loadComponentRegistry } from "./components";
|
|
@@ -309,7 +309,8 @@ export async function openProject({
|
|
|
309
309
|
}): Promise<boolean> {
|
|
310
310
|
try {
|
|
311
311
|
const platform = getPlatform();
|
|
312
|
-
|
|
312
|
+
// The command is gated on the capability, so a host without the member never gets here.
|
|
313
|
+
const result = await platform.openProject?.();
|
|
313
314
|
if (!result) {
|
|
314
315
|
return false;
|
|
315
316
|
} // User cancelled
|
|
@@ -864,7 +865,9 @@ function filesPanelValues(): FilesPanelValues {
|
|
|
864
865
|
}
|
|
865
866
|
if (!projectState.isSiteProject && projectState.projectRoot === ".") {
|
|
866
867
|
_fileRows = [];
|
|
867
|
-
|
|
868
|
+
// A host with no Open Project (the dev server) is told how its windows DO open one instead.
|
|
869
|
+
const view = canOpenProject(getPlatform()) ? "welcome" : "address";
|
|
870
|
+
const values: FilesPanelValues = { ...emptyFilesPanelValues(), view };
|
|
868
871
|
_lastValues = values;
|
|
869
872
|
return values;
|
|
870
873
|
}
|
|
@@ -346,10 +346,7 @@ function finish(result: { root: string; config: ProjectConfig } | null) {
|
|
|
346
346
|
* runs this same sequence itself.
|
|
347
347
|
*/
|
|
348
348
|
async function finishCreated(result: { root: string; config: ProjectConfig }) {
|
|
349
|
-
const { adopted, error, openedElsewhere } = await adoptCreatedProject(result.root
|
|
350
|
-
// The wizard runs outside the agent loop — there is never a batch to flush and re-anchor.
|
|
351
|
-
getTab: () => null,
|
|
352
|
-
});
|
|
349
|
+
const { adopted, error, openedElsewhere } = await adoptCreatedProject(result.root);
|
|
353
350
|
if (!adopted && !openedElsewhere) {
|
|
354
351
|
notify.warn(
|
|
355
352
|
"Created the project, but could not open it here.",
|
package/src/panels/ai-panel.ts
CHANGED
|
@@ -28,13 +28,13 @@
|
|
|
28
28
|
|
|
29
29
|
import { effect, effectScope } from "../reactivity";
|
|
30
30
|
import { createDocumentAssistant } from "../services/document-assistant";
|
|
31
|
-
import {
|
|
31
|
+
import { restoreTurn } from "../services/ai-writes";
|
|
32
|
+
import { isString } from "@jxsuite/schema/guards";
|
|
32
33
|
import { notify } from "../services/notify";
|
|
33
|
-
import {
|
|
34
|
+
import type { NotifyOptions } from "../services/notify";
|
|
34
35
|
import { activeTab } from "../workspace/workspace";
|
|
35
36
|
import { setOpenAiKey } from "../services/ai-settings";
|
|
36
37
|
import { hasAiCredentials } from "../services/ai-models";
|
|
37
|
-
import { answerAsk, isAwaitingAnswer, pendingAsk, skipAsk } from "../services/ai-ask";
|
|
38
38
|
import { activeImportRun, importRun } from "../services/import-run";
|
|
39
39
|
import type { ImportBrief } from "../services/import-seed";
|
|
40
40
|
import type { ImportBreakpointPolicy } from "../types";
|
|
@@ -124,7 +124,7 @@ export function bindAiPanelHost(el: HTMLElement | null) {
|
|
|
124
124
|
run: runCommand,
|
|
125
125
|
send: () => composer.send(),
|
|
126
126
|
skip: () => {
|
|
127
|
-
|
|
127
|
+
assistant.interactions.skip();
|
|
128
128
|
renderAiPanel();
|
|
129
129
|
},
|
|
130
130
|
stop,
|
|
@@ -208,10 +208,10 @@ function watchAssistant() {
|
|
|
208
208
|
// The registry is composed AFTER the bootstrap mounts this, and it is a reactive holder —
|
|
209
209
|
// Reading it here is what turns the header's skeleton into its real buttons.
|
|
210
210
|
void activeRegistry();
|
|
211
|
-
// The question is not chat state — it lives in
|
|
212
|
-
//
|
|
213
|
-
// (and its ANSWER would never repaint the card at all).
|
|
214
|
-
void
|
|
211
|
+
// The question is not chat state — it lives in the assistant's interaction store — but it is
|
|
212
|
+
// Drawn into this panel, so the same effect has to track it or a question would appear a turn
|
|
213
|
+
// Late (and its ANSWER would never repaint the card at all).
|
|
214
|
+
void assistant.interactions.pending;
|
|
215
215
|
/* The run record, for the same reason: an import reports a line at a time from a store that
|
|
216
216
|
is not chat state, and its chip has to follow every one of them. Reading the ACTIVE run
|
|
217
217
|
tracks the whole record, so any field moving re-projects. */
|
|
@@ -268,7 +268,7 @@ async function handleAssistantSend(text: string) {
|
|
|
268
268
|
}
|
|
269
269
|
/* Answering comes first: a question is put by a turn that is still running, so a send while one
|
|
270
270
|
waits is its answer, never a second turn. */
|
|
271
|
-
if (
|
|
271
|
+
if (assistant.interactions.answer(text.trim())) {
|
|
272
272
|
surface?.pin();
|
|
273
273
|
renderAiPanel();
|
|
274
274
|
return;
|
|
@@ -319,38 +319,31 @@ async function handleRetry(): Promise<void> {
|
|
|
319
319
|
/**
|
|
320
320
|
* Undo everything one assistant turn changed — ai.md §3.2's "Restore to here".
|
|
321
321
|
*
|
|
322
|
-
* The
|
|
323
|
-
*
|
|
324
|
-
*
|
|
325
|
-
*
|
|
326
|
-
*
|
|
322
|
+
* The turn's batch lane left one step per document it wrote, and {@link restoreTurn} takes each
|
|
323
|
+
* back while it is still the top of its tab's history, or refuses the whole turn with a sentence.
|
|
324
|
+
* The button is offered by the projection only when every recorded change was transactional; the
|
|
325
|
+
* refusals are the second half of the same promise, because a ledger can be trimmed (MAX_TURNS) and
|
|
326
|
+
* a document edited between the projection and the click.
|
|
327
327
|
*
|
|
328
328
|
* @param {string} messageId
|
|
329
329
|
*/
|
|
330
330
|
/* Exported so the guard can be exercised directly: the button is not DRAWN for a turn that touched
|
|
331
331
|
disk, which would otherwise make the refusal path unreachable from the panel. */
|
|
332
332
|
export function handleRestore(messageId: string): void {
|
|
333
|
-
const
|
|
334
|
-
if (
|
|
335
|
-
notify.warn(
|
|
333
|
+
const restored = restoreTurn(messageId);
|
|
334
|
+
if (isString(restored)) {
|
|
335
|
+
notify.warn(restored, { source: "Assistant" });
|
|
336
336
|
return;
|
|
337
337
|
}
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
return;
|
|
346
|
-
}
|
|
347
|
-
const tab = activeTab.value;
|
|
348
|
-
if (!tab) {
|
|
349
|
-
notify.warn("Open the document that turn edited to restore it.", { source: "Assistant" });
|
|
350
|
-
return;
|
|
338
|
+
/* Redo reaches the active tab only, so it is offered only when that is the one tab restored;
|
|
339
|
+
across documents it would bring back part of the turn. */
|
|
340
|
+
const [only, ...rest] = restored;
|
|
341
|
+
const redoable = rest.length === 0 && only === activeTab.value;
|
|
342
|
+
const options: NotifyOptions = { source: "Assistant" };
|
|
343
|
+
if (redoable) {
|
|
344
|
+
options.action = "edit.redo";
|
|
351
345
|
}
|
|
352
|
-
|
|
353
|
-
notify.success("Restored to before that turn.", { action: "edit.redo", source: "Assistant" });
|
|
346
|
+
notify.success("Restored to before that turn.", options);
|
|
354
347
|
renderAiPanel();
|
|
355
348
|
}
|
|
356
349
|
|
|
@@ -509,7 +502,7 @@ export function revealAssistant(): void {
|
|
|
509
502
|
* turn waiting forever on a question nobody wants to answer is exactly what Stop is for.
|
|
510
503
|
*/
|
|
511
504
|
export function isAssistantWaiting(): boolean {
|
|
512
|
-
return
|
|
505
|
+
return assistant.interactions.pending !== null;
|
|
513
506
|
}
|
|
514
507
|
|
|
515
508
|
/**
|
|
@@ -617,7 +610,7 @@ function projectPanel(): AiChatView {
|
|
|
617
610
|
const advice = error ? formatErrorAdvice(error) : "";
|
|
618
611
|
const newChatCommand = projectCommand("assistant.newChat", { icon: "plus" });
|
|
619
612
|
const rows = projectRows({
|
|
620
|
-
ask: { importRun, pendingId:
|
|
613
|
+
ask: { importRun, pendingId: assistant.interactions.pending?.callId ?? null },
|
|
621
614
|
messages: cs.messages,
|
|
622
615
|
status: cs.status,
|
|
623
616
|
});
|
package/src/platform.ts
CHANGED
|
@@ -50,6 +50,26 @@ declare global {
|
|
|
50
50
|
var __jxLauncher: LauncherSignal | undefined;
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
+
/**
|
|
54
|
+
* Whether this host offers Open Project at all: a native dialog (`openProject`), or the cloud's
|
|
55
|
+
* repository picker. The dev server has neither, because its windows open a project from the
|
|
56
|
+
* `?project=` URL parameter, so every surface that offers Open Project asks this first rather than
|
|
57
|
+
* drawing a control that cannot do anything.
|
|
58
|
+
*/
|
|
59
|
+
export function canOpenProject(platform: StudioPlatform | null | undefined): boolean {
|
|
60
|
+
if (!platform) {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
if (isFunction(platform.openProject)) {
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
return (
|
|
67
|
+
platform.openProjectPicker === "repo-list" &&
|
|
68
|
+
isFunction(platform.listRepos) &&
|
|
69
|
+
isFunction(platform.importProject)
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
53
73
|
/** @param {StudioPlatform} platform */
|
|
54
74
|
export function registerPlatform(platform: StudioPlatform) {
|
|
55
75
|
globalThis.__jxPlatform = platform;
|
|
@@ -2,18 +2,16 @@
|
|
|
2
2
|
* Devserver.js — Dev Server Platform Adapter
|
|
3
3
|
*
|
|
4
4
|
* Implements the StudioPlatform interface for the @jxsuite/server development workflow. All file
|
|
5
|
-
* I/O goes through /__studio/* REST endpoints.
|
|
6
|
-
*
|
|
5
|
+
* I/O goes through /__studio/* REST endpoints. There is no Open Project: a dev-server window opens
|
|
6
|
+
* a project from its `?project=` URL parameter (specs/desktop.md §8.2).
|
|
7
7
|
*
|
|
8
8
|
* See spec/desktop.md §8 for the full specification.
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import { streamImport } from "../services/import-client";
|
|
12
|
-
import { canPickDirectory, pickDirectoryPath } from "../services/directory-picker";
|
|
13
12
|
import { negotiateCollab } from "@jxsuite/collab/negotiate";
|
|
14
13
|
import type { CollabNegotiation } from "@jxsuite/collab/negotiate";
|
|
15
14
|
import type { WsCollabConnection } from "@jxsuite/collab/client";
|
|
16
|
-
import { isFunction } from "@jxsuite/schema/guards";
|
|
17
15
|
import type { JsonObject, JsonValue, JxMutableNode, ProjectConfig } from "@jxsuite/schema/types";
|
|
18
16
|
import type {
|
|
19
17
|
DataConnectionsResponse,
|
|
@@ -80,16 +78,6 @@ interface FsEventBatch {
|
|
|
80
78
|
events?: FsEvent[];
|
|
81
79
|
}
|
|
82
80
|
|
|
83
|
-
/** `showDirectoryPicker()` (WICG File System Access), which only Chromium-based browsers expose. */
|
|
84
|
-
type ShowDirectoryPicker = (options: { mode: string }) => Promise<FileSystemDirectoryHandle>;
|
|
85
|
-
|
|
86
|
-
/** True when the window implements the File System Access directory picker. */
|
|
87
|
-
function hasDirectoryPicker(
|
|
88
|
-
host: Window,
|
|
89
|
-
): host is Window & { showDirectoryPicker: ShowDirectoryPicker } {
|
|
90
|
-
return "showDirectoryPicker" in host && isFunction(host.showDirectoryPicker);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
81
|
/** The options object {@link StudioPlatform.createProject} takes. */
|
|
94
82
|
type CreateProjectOptions = Parameters<StudioPlatform["createProject"]>[0];
|
|
95
83
|
|
|
@@ -225,36 +213,6 @@ export function createDevServerPlatform() {
|
|
|
225
213
|
return p.startsWith(`${_projectRoot}/`) ? p.slice(_projectRoot.length + 1) : p;
|
|
226
214
|
}
|
|
227
215
|
|
|
228
|
-
/**
|
|
229
|
-
* Native folder chooser for the New Project modal's Browse… button. Present only when the browser
|
|
230
|
-
* implements `showDirectoryPicker` (Chromium-based); elsewhere the member stays undefined and the
|
|
231
|
-
* modal falls back to a typed Location field.
|
|
232
|
-
*
|
|
233
|
-
* The picked handle carries no filesystem path, so it is resolved by the id the picker tags the
|
|
234
|
-
* folder with (see `@jxsuite/studio/directory-picker`). Resolves `null` only when the user
|
|
235
|
-
* dismisses the chooser; every other failure rejects with a sentence the modal shows under
|
|
236
|
-
* Location. So `locate` keeps the helper's contract: `null` is "no folder holds this id" (the
|
|
237
|
-
* route's 200 `{ path: null }`), and a non-ok answer throws the problem the server sent rather
|
|
238
|
-
* than posing as a miss. A network failure (`TypeError: Failed to fetch`, e.g. a page that is not
|
|
239
|
-
* being served by the dev server at all) propagates as is, and the picker names it.
|
|
240
|
-
*/
|
|
241
|
-
const directoryPicking: Pick<StudioPlatform, "pickDirectory"> = {};
|
|
242
|
-
if (canPickDirectory()) {
|
|
243
|
-
directoryPicking.pickDirectory = async (): Promise<string | null> =>
|
|
244
|
-
pickDirectoryPath(async ({ id, name }) => {
|
|
245
|
-
const res = await fetch(
|
|
246
|
-
`/__studio/locate-directory?name=${encodeURIComponent(name)}&id=${encodeURIComponent(id)}`,
|
|
247
|
-
);
|
|
248
|
-
if (!res.ok) {
|
|
249
|
-
// A proxy, or a `views://` host with no dev server behind it, may answer in HTML.
|
|
250
|
-
const body = await readJson<JsonValue>(res).catch(() => null);
|
|
251
|
-
throw new Error(problemMessage(body, res.status));
|
|
252
|
-
}
|
|
253
|
-
const found = await readJson<{ path?: string | null }>(res);
|
|
254
|
-
return found.path ?? null;
|
|
255
|
-
});
|
|
256
|
-
}
|
|
257
|
-
|
|
258
216
|
return {
|
|
259
217
|
id: "devserver",
|
|
260
218
|
|
|
@@ -293,80 +251,6 @@ export function createDevServerPlatform() {
|
|
|
293
251
|
await postActivate(root ?? _projectRoot);
|
|
294
252
|
},
|
|
295
253
|
|
|
296
|
-
// ─── Project opening ──────────────────────────────────────────────────
|
|
297
|
-
|
|
298
|
-
async openProject() {
|
|
299
|
-
// Use Chrome's showDirectoryPicker API
|
|
300
|
-
const host = window;
|
|
301
|
-
if (!hasDirectoryPicker(host)) {
|
|
302
|
-
throw new Error("showDirectoryPicker not available — use a Chromium-based browser");
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
let dirHandle;
|
|
306
|
-
try {
|
|
307
|
-
dirHandle = await host.showDirectoryPicker({ mode: "readwrite" });
|
|
308
|
-
} catch (error) {
|
|
309
|
-
// User cancelled the picker
|
|
310
|
-
if (error instanceof Error && error.name === "AbortError") {
|
|
311
|
-
return null;
|
|
312
|
-
}
|
|
313
|
-
throw error;
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
// Read project.json from the chosen directory
|
|
317
|
-
let siteHandle;
|
|
318
|
-
try {
|
|
319
|
-
siteHandle = await dirHandle.getFileHandle("project.json");
|
|
320
|
-
} catch {
|
|
321
|
-
throw new Error("No project.json found in selected folder");
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
const file = await siteHandle.getFile();
|
|
325
|
-
/* SAFETY: the user picked this folder as a project, and project.json is that project's own
|
|
326
|
-
config file; it is matched below against the server's parse of the same file, and every
|
|
327
|
-
adapter hands project.json to Studio as the ProjectConfig the session edits. */
|
|
328
|
-
const config = JSON.parse(await file.text()) as ProjectConfig;
|
|
329
|
-
|
|
330
|
-
// Resolve server-relative path by matching against known sites
|
|
331
|
-
const sitesRes = await fetch("/__studio/sites");
|
|
332
|
-
if (!sitesRes.ok) {
|
|
333
|
-
throw new Error("Failed to fetch site list from server");
|
|
334
|
-
}
|
|
335
|
-
const sites = await readJson<SiteEntry[]>(sitesRes);
|
|
336
|
-
const match = sites.find(
|
|
337
|
-
(s: SiteEntry) => JSON.stringify(s.config) === JSON.stringify(config),
|
|
338
|
-
);
|
|
339
|
-
|
|
340
|
-
if (!match) {
|
|
341
|
-
// Project is outside dev server root — ask the server to find it by directory name
|
|
342
|
-
const findRes = await fetch(
|
|
343
|
-
`/__studio/find-project?name=${encodeURIComponent(dirHandle.name)}`,
|
|
344
|
-
);
|
|
345
|
-
if (!findRes.ok) {
|
|
346
|
-
throw new Error("Could not locate project on disk");
|
|
347
|
-
}
|
|
348
|
-
const found = await readJson<{ path?: string }>(findRes);
|
|
349
|
-
if (!found.path) {
|
|
350
|
-
throw new Error(`Could not find project directory "${dirHandle.name}"`);
|
|
351
|
-
}
|
|
352
|
-
_projectRoot = found.path;
|
|
353
|
-
} else {
|
|
354
|
-
_projectRoot = match.path;
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
// Notify server of active project for static file resolution
|
|
358
|
-
await this.activate();
|
|
359
|
-
|
|
360
|
-
return {
|
|
361
|
-
config,
|
|
362
|
-
handle: {
|
|
363
|
-
name: config.name || _projectRoot.split("/").pop()!,
|
|
364
|
-
projectConfig: config,
|
|
365
|
-
root: _projectRoot,
|
|
366
|
-
},
|
|
367
|
-
};
|
|
368
|
-
},
|
|
369
|
-
|
|
370
254
|
/**
|
|
371
255
|
* Probe the server root to see if it is itself a site project. Used at startup to auto-detect
|
|
372
256
|
* projects.
|
|
@@ -408,8 +292,6 @@ export function createDevServerPlatform() {
|
|
|
408
292
|
return await res.json();
|
|
409
293
|
},
|
|
410
294
|
|
|
411
|
-
...directoryPicking,
|
|
412
|
-
|
|
413
295
|
/** List starter templates from the dev server. */
|
|
414
296
|
async listStarters(): Promise<StarterInfo[]> {
|
|
415
297
|
const res = await fetch("/__studio/starters");
|