@jxsuite/studio 9.0.0 → 10.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunks/{2020-89fz3pfy.js → 2020-mkb0qa6p.js} +3 -3
- package/dist/chunks/adapter-xcpsmecx.js +4 -0
- package/dist/chunks/{add-new-flow-nmkqpjym.js → add-new-flow-ptmhmzr9.js} +3 -3
- package/dist/chunks/{cf-account-picker-2e06qxey.js → cf-account-picker-z1sgwpwd.js} +3 -3
- package/dist/chunks/cf-settings-xqvhxxe9.js +4 -0
- package/dist/chunks/components-whsc81a7.js +4 -0
- package/dist/chunks/content-source-jwtxg78w.js +4 -0
- package/dist/chunks/{convert-to-repeater-j9hfcj4f.js → convert-to-repeater-f92ey3kp.js} +3 -3
- package/dist/chunks/editor-zdqdpq1x.js +4 -0
- package/dist/chunks/entry-commands-w8p2fbpt.js +4 -0
- package/dist/chunks/{extension-sections-thfke054.js → extension-sections-y2p1te2r.js} +3 -3
- package/dist/chunks/file-ops-13ab4swj.js +4 -0
- package/dist/chunks/files-zbc8p6v1.js +4 -0
- package/dist/chunks/format-host-zjsbktbx.js +4 -0
- package/dist/chunks/fs-events-t6x0hch6.js +4 -0
- package/dist/chunks/grid-open-fyzdwpkh.js +4 -0
- package/dist/chunks/{grid-view-7d2nx83y.js → grid-view-r22jsqht.js} +5 -5
- package/dist/chunks/iframe-host-kbyqc7pt.js +4 -0
- package/dist/chunks/index-6nvj89vb.js +4 -0
- package/dist/chunks/{index-jxmt7vh8.js → index-pxwwf00y.js} +3 -3
- package/dist/chunks/{javascript-vmqp56ke.js → javascript-pzqryby6.js} +2 -2
- package/dist/chunks/{jsonMode-n3wmpant.js → jsonMode-tcnyxyap.js} +5 -5
- package/dist/chunks/jx-validate-5sp8a4rm.js +4 -0
- package/dist/chunks/layers-panel-qrxj9vr2.js +4 -0
- package/dist/chunks/layers-tpzayggx.js +4 -0
- package/dist/chunks/lit-html-x6nrxsx3.js +4 -0
- package/dist/chunks/media-library-ywppnjrz.js +4 -0
- package/dist/chunks/media-picker-etxgc8qq.js +4 -0
- package/dist/chunks/{monaco-binding-wfhmz89d.js → monaco-binding-bqpge64f.js} +3 -3
- package/dist/chunks/monaco-buffer-qrnqhk0b.js +4 -0
- package/dist/chunks/{monaco-setup-fty7a8vg.js → monaco-setup-1mkab762.js} +3 -3
- package/dist/chunks/notify-kcv38wwe.js +4 -0
- package/dist/chunks/parse-df2kkmhc.js +4 -0
- package/dist/chunks/platform-dtsysmyd.js +4 -0
- package/dist/chunks/publish-panel-evb1rcxt.js +4 -0
- package/dist/chunks/{publish-quick-12hh8926.js → publish-quick-m9vkhahq.js} +3 -3
- package/dist/chunks/publish-workspace-ktk9h536.js +4 -0
- package/dist/chunks/quick-search-2f47fxcw.js +4 -0
- package/dist/chunks/references-5w6ajjv7.js +4 -0
- package/dist/chunks/right-panel-kz0a6byf.js +4 -0
- package/dist/chunks/source-control-1phx90zk.js +4 -0
- package/dist/chunks/{studio-qevf5z9d.js → studio-07k0geap.js} +4 -4
- package/dist/chunks/{studio-k5n4es74.js → studio-0g2sm01j.js} +4 -4
- package/dist/chunks/{studio-7q6jdd4h.js → studio-0hr000ba.js} +4 -4
- package/dist/chunks/{studio-e4rq6x0q.js → studio-1wyp7dt5.js} +4 -4
- package/dist/chunks/{studio-vtey9w98.js → studio-20ap8dds.js} +4 -4
- package/dist/chunks/studio-291gqh98.js +5 -0
- package/dist/chunks/{studio-32d8wyxq.js → studio-37twdaf7.js} +30 -30
- package/dist/chunks/{studio-8pne0rx4.js → studio-4nkm4404.js} +4 -4
- package/dist/chunks/{studio-z1p91z5a.js → studio-4smcnv33.js} +4 -4
- package/dist/chunks/{studio-s4fq9jht.js → studio-4zqfw5ha.js} +4 -4
- package/dist/chunks/{studio-n4qq1n57.js → studio-56egy6vf.js} +4 -4
- package/dist/chunks/{studio-t6dqnnft.js → studio-5dmgmyw4.js} +4 -4
- package/dist/chunks/{studio-9ff0vqff.js → studio-5e7p1vgd.js} +4 -4
- package/dist/chunks/{studio-4qg61g9j.js → studio-5m0w815d.js} +4 -4
- package/dist/chunks/{studio-0rg30pvs.js → studio-695k95bw.js} +4 -4
- package/dist/chunks/{studio-spza69tb.js → studio-6vwvshw8.js} +5 -5
- package/dist/chunks/{studio-ae7a69jc.js → studio-76t560kn.js} +5 -5
- package/dist/chunks/{studio-9gzvypw4.js → studio-7823f0g8.js} +4 -4
- package/dist/chunks/{studio-gskkgfe3.js → studio-7vb997ff.js} +4 -4
- package/dist/chunks/{studio-sc35ttdk.js → studio-8486jn4r.js} +4 -4
- package/dist/chunks/{studio-a6e5fzve.js → studio-848rk4p1.js} +4 -4
- package/dist/chunks/{studio-tyzgte21.js → studio-8jb31dcf.js} +4 -4
- package/dist/chunks/{studio-jr30g4ae.js → studio-8qejv8yf.js} +4 -4
- package/dist/chunks/studio-8x8616xd.js +5 -0
- package/dist/chunks/{studio-kyw47pet.js → studio-9b7177jq.js} +4 -4
- package/dist/chunks/{studio-gk9yk8dj.js → studio-9cp96v90.js} +6 -6
- package/dist/chunks/{studio-h4jbgne2.js → studio-9rcbvksg.js} +6 -6
- package/dist/chunks/{studio-3hpcawh8.js → studio-a3s6frde.js} +3 -3
- package/dist/chunks/{studio-tfmhnwyr.js → studio-ahf3cspq.js} +3 -3
- package/dist/chunks/{studio-6rbaedw2.js → studio-bkmv1h3e.js} +6 -6
- package/dist/chunks/{studio-4j35hgsw.js → studio-brdp85q7.js} +4 -4
- package/dist/chunks/{studio-hcy9zeac.js → studio-cgb97rwv.js} +4 -4
- package/dist/chunks/{studio-gczve8zf.js → studio-ch8gt8vt.js} +3 -3
- package/dist/chunks/studio-cqf1k4qk.js +5 -0
- package/dist/chunks/{studio-myb4mf0h.js → studio-cvz0e00f.js} +4 -4
- package/dist/chunks/{studio-nqexty8x.js → studio-dbgv4fv9.js} +14 -14
- package/dist/chunks/{studio-8hf8qcm1.js → studio-dy2xtt7a.js} +4 -4
- package/dist/chunks/{studio-c88gq88j.js → studio-eesyjg7h.js} +5 -5
- package/dist/chunks/studio-et6t8g4m.js +4 -0
- package/dist/chunks/{studio-qs5fmhtj.js → studio-f2j9et3h.js} +8 -8
- package/dist/chunks/{studio-79r8msnr.js → studio-fep7hr66.js} +4 -4
- package/dist/chunks/{studio-1cfyd1ys.js → studio-fyf7j57j.js} +3 -3
- package/dist/chunks/{studio-mkw17ngg.js → studio-g5prntab.js} +4 -4
- package/dist/chunks/{studio-x2trkhk5.js → studio-ga77mf07.js} +4 -4
- package/dist/chunks/{studio-yjh9gddd.js → studio-ggm7zf70.js} +5 -5
- package/dist/chunks/studio-h7gy12h2.js +5 -0
- package/dist/chunks/{studio-qd0cycm4.js → studio-hsr3k06v.js} +5 -5
- package/dist/chunks/{studio-ztppbam0.js → studio-j933k8nr.js} +4 -4
- package/dist/chunks/studio-jgbvqcgh.js +5 -0
- package/dist/chunks/{studio-xzgp0cp7.js → studio-jh6t5qz4.js} +4 -4
- package/dist/chunks/{studio-k6hbx2fc.js → studio-jw0q5j6k.js} +4 -4
- package/dist/chunks/studio-k4xc5g6h.js +5 -0
- package/dist/chunks/{studio-fg4fj9ec.js → studio-mwnmzx7g.js} +6 -6
- package/dist/chunks/{studio-ekzbgnjp.js → studio-nf0pqpbj.js} +4 -4
- package/dist/chunks/{studio-sm2f0sc6.js → studio-p0nf2e1f.js} +5 -5
- package/dist/chunks/{studio-by8x0jvc.js → studio-pd9dsqpq.js} +4 -4
- package/dist/chunks/{studio-cg54vtet.js → studio-pmzg17fj.js} +4 -4
- package/dist/chunks/{studio-ht81xjkr.js → studio-pzfa2vmb.js} +4 -4
- package/dist/chunks/{studio-9179bsm7.js → studio-qyb5bebs.js} +7 -7
- package/dist/chunks/{studio-aghsg0ds.js → studio-rf8d6p9f.js} +6 -6
- package/dist/chunks/{studio-9ra9e7m4.js → studio-rt498t2v.js} +4 -4
- package/dist/chunks/{studio-trcfer2b.js → studio-s0fg2wav.js} +4 -4
- package/dist/chunks/studio-sd07xva5.js +6 -0
- package/dist/chunks/{studio-apapy17g.js → studio-skynbac3.js} +2 -2
- package/dist/chunks/{studio-nvfa137z.js → studio-t3j8htp7.js} +5 -5
- package/dist/chunks/{studio-ry4789ng.js → studio-thj81e98.js} +6 -6
- package/dist/chunks/{studio-nfwacw31.js → studio-tpy3rdxn.js} +4 -4
- package/dist/chunks/{studio-g45r5vm8.js → studio-va0e63sk.js} +4 -4
- package/dist/chunks/{studio-kqh7fxn6.js → studio-vcz63n42.js} +4 -4
- package/dist/chunks/studio-vffwe9k7.js +5 -0
- package/dist/chunks/{studio-nm86874b.js → studio-vjhej0w5.js} +123 -123
- package/dist/chunks/{studio-ws2r1d7e.js → studio-vxjap704.js} +4 -4
- package/dist/chunks/{studio-9ndgd55w.js → studio-w67vfgy5.js} +4 -4
- package/dist/chunks/{studio-ajw4k8hp.js → studio-x2metr13.js} +3 -3
- package/dist/chunks/{studio-14hh60j8.js → studio-xebavr9s.js} +4 -4
- package/dist/chunks/{studio-4rgem12q.js → studio-yg5n49za.js} +4 -4
- package/dist/chunks/{studio-6k9djnxw.js → studio-yhz5b313.js} +4 -4
- package/dist/chunks/{studio-nfmpmkqb.js → studio-zacvfm31.js} +4 -4
- package/dist/chunks/{studio-ywf9y1e2.js → studio-zsjpsw08.js} +4 -4
- package/dist/chunks/{studio-0nefxhar.js → studio-zxm98z2m.js} +6 -6
- package/dist/chunks/{tsMode-byje4a01.js → tsMode-6vwhy6ty.js} +5 -5
- package/dist/chunks/workspace-d65bz1qk.js +4 -0
- package/dist/chunks/workspace-home-vbg9b3jh.js +4 -0
- package/dist/chunks/{ws-client-h09h6sqg.js → ws-client-zcxkfgmf.js} +3 -3
- package/dist/iframe-entry.js +8 -8
- package/dist/manifest.json +74 -74
- package/dist/studio.js +86 -84
- package/package.json +11 -11
- package/src/canvas/iframe-editable-root.ts +32 -2
- package/src/format/format-host.ts +1 -1
- package/src/github/github-auth-status.ts +2 -2
- package/src/media/media-library.ts +73 -3
- package/src/packages/ensure-deps.ts +5 -5
- package/src/packages/pull-package-sync.ts +4 -4
- package/src/panels/activity-panel.ts +1 -1
- package/src/platform-errors.ts +17 -1
- package/src/platforms/cloud.ts +62 -34
- package/src/publish/publish-changes.ts +136 -4
- package/src/settings/extension-commands.ts +1 -1
- package/src/settings/extension-rows.ts +1 -6
- package/src/settings/extensions-section.ts +3 -6
- package/src/surfaces/block-action-bar.json +1 -3
- package/src/surfaces/media-library.json +35 -0
- package/src/surfaces/media-library.ts +2 -0
- package/src/surfaces/menu.ts +62 -1
- package/src/surfaces/settings-packages.ts +1 -1
- package/src/types.ts +24 -8
- package/src/ui/layers.ts +1 -1
- package/src/ui/progress-modal.ts +3 -3
- package/dist/chunks/adapter-ay13gema.js +0 -4
- package/dist/chunks/cf-settings-5y04810c.js +0 -4
- package/dist/chunks/components-3vf75n83.js +0 -4
- package/dist/chunks/content-source-4f2raf4a.js +0 -4
- package/dist/chunks/editor-645krvtz.js +0 -4
- package/dist/chunks/entry-commands-3qmv8wkr.js +0 -4
- package/dist/chunks/file-ops-kkhp9ma7.js +0 -4
- package/dist/chunks/files-yswdhxrf.js +0 -4
- package/dist/chunks/format-host-4g8ex6bz.js +0 -4
- package/dist/chunks/fs-events-z9gpvc6t.js +0 -4
- package/dist/chunks/grid-open-fm4j183c.js +0 -4
- package/dist/chunks/iframe-host-q7fedsmr.js +0 -4
- package/dist/chunks/index-pt4wh98x.js +0 -4
- package/dist/chunks/jx-validate-19y62knt.js +0 -4
- package/dist/chunks/layers-panel-1qvc83y9.js +0 -4
- package/dist/chunks/layers-qf0bc7ze.js +0 -4
- package/dist/chunks/lit-html-wyqw04y2.js +0 -4
- package/dist/chunks/media-library-f9d5qcjb.js +0 -4
- package/dist/chunks/media-picker-rsb1fmk2.js +0 -4
- package/dist/chunks/monaco-buffer-ak0raadw.js +0 -4
- package/dist/chunks/notify-4qj70q0x.js +0 -4
- package/dist/chunks/parse-n8dfxnqf.js +0 -4
- package/dist/chunks/platform-ehg6t9vd.js +0 -4
- package/dist/chunks/publish-panel-yhmda3cd.js +0 -4
- package/dist/chunks/publish-workspace-hx8qdv9p.js +0 -4
- package/dist/chunks/quick-search-r955m3q1.js +0 -4
- package/dist/chunks/references-8gxv0x7t.js +0 -4
- package/dist/chunks/right-panel-jarwqjpy.js +0 -4
- package/dist/chunks/source-control-84qt25m9.js +0 -4
- package/dist/chunks/studio-2bq7hheq.js +0 -5
- package/dist/chunks/studio-a9k86nq6.js +0 -5
- package/dist/chunks/studio-dvhxxxbd.js +0 -5
- package/dist/chunks/studio-fj6jzgxw.js +0 -5
- package/dist/chunks/studio-ge08zwpv.js +0 -5
- package/dist/chunks/studio-nr0kcqc6.js +0 -5
- package/dist/chunks/studio-psh8mwnw.js +0 -4
- package/dist/chunks/studio-tn7v8gey.js +0 -6
- package/dist/chunks/studio-y47090vq.js +0 -5
- package/dist/chunks/workspace-7q4arnt3.js +0 -4
- package/dist/chunks/workspace-home-ge027kr6.js +0 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jxsuite/studio",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "10.1.0",
|
|
4
4
|
"description": "Jx Studio — visual builder for Jx documents",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -60,16 +60,16 @@
|
|
|
60
60
|
"dependencies": {
|
|
61
61
|
"@atlaskit/pragmatic-drag-and-drop": "^4.0.0",
|
|
62
62
|
"@atlaskit/pragmatic-drag-and-drop-hitbox": "^3.0.0",
|
|
63
|
-
"@jxsuite/ai": "^0.41.
|
|
64
|
-
"@jxsuite/collab": "^0.11.
|
|
65
|
-
"@jxsuite/create": "^1.
|
|
66
|
-
"@jxsuite/formulas": "^0.0.
|
|
67
|
-
"@jxsuite/markup": "^1.0.
|
|
68
|
-
"@jxsuite/protocol": "^
|
|
69
|
-
"@jxsuite/runtime": "^6.0.
|
|
70
|
-
"@jxsuite/schema": "^
|
|
71
|
-
"@jxsuite/site": "^2.2.
|
|
72
|
-
"@jxsuite/ui": "^0.8.
|
|
63
|
+
"@jxsuite/ai": "^0.41.2",
|
|
64
|
+
"@jxsuite/collab": "^0.11.4",
|
|
65
|
+
"@jxsuite/create": "^1.5.1",
|
|
66
|
+
"@jxsuite/formulas": "^0.0.26",
|
|
67
|
+
"@jxsuite/markup": "^1.0.2",
|
|
68
|
+
"@jxsuite/protocol": "^6.1.0",
|
|
69
|
+
"@jxsuite/runtime": "^6.0.1",
|
|
70
|
+
"@jxsuite/schema": "^5.0.0",
|
|
71
|
+
"@jxsuite/site": "^2.2.2",
|
|
72
|
+
"@jxsuite/ui": "^0.8.2",
|
|
73
73
|
"@vue/reactivity": "3.5.44",
|
|
74
74
|
"ajv": "^8.20.0",
|
|
75
75
|
"ajv-formats": "^3.0.1",
|
|
@@ -445,8 +445,9 @@ export function startEditableRoot(
|
|
|
445
445
|
* something no caret can enter, so the caret went on blinking in the block clicked BEFORE, with
|
|
446
446
|
* the component now selected in the editor and every keystroke still typed into that block.
|
|
447
447
|
*/
|
|
448
|
-
const onPointerDownCapture = (e:
|
|
449
|
-
|
|
448
|
+
const onPointerDownCapture = (e: PointerEvent) => {
|
|
449
|
+
// A secondary press asks for the element menu, not an edit: see `onMouseDownCapture`.
|
|
450
|
+
if (!editingAllowed() || e.button === 2) {
|
|
450
451
|
return;
|
|
451
452
|
}
|
|
452
453
|
const target = e.target instanceof Element ? e.target : null;
|
|
@@ -478,6 +479,33 @@ export function startEditableRoot(
|
|
|
478
479
|
}
|
|
479
480
|
};
|
|
480
481
|
|
|
482
|
+
/**
|
|
483
|
+
* A secondary press outside the active block selects, it never starts an edit.
|
|
484
|
+
*
|
|
485
|
+
* The whole canvas is one `contenteditable`, and a right-button press places the caret where it
|
|
486
|
+
* lands, as a left one does. That caret activated the block through `selectionchange`, so every
|
|
487
|
+
* right-click on a block also entered inline editing: the bridge's edit start ran ahead of the
|
|
488
|
+
* menu's mount in the same task, which held the element menu back by hundreds of milliseconds on
|
|
489
|
+
* a real page, and the block was left in edit mode after the menu closed. Cancelling the press's
|
|
490
|
+
* default keeps the caret where it was; `contextmenu` still fires, so the menu is unaffected. The
|
|
491
|
+
* block being edited keeps its native press, because the caret placed there is what the native
|
|
492
|
+
* spelling and edit menu reads. A right-click elsewhere leaves that block, as a left click
|
|
493
|
+
* would.
|
|
494
|
+
*/
|
|
495
|
+
const onMouseDownCapture = (e: MouseEvent) => {
|
|
496
|
+
if (e.button !== 2 || !editingAllowed()) {
|
|
497
|
+
return;
|
|
498
|
+
}
|
|
499
|
+
if (activeEl && e.target instanceof Node && activeEl.contains(e.target)) {
|
|
500
|
+
return;
|
|
501
|
+
}
|
|
502
|
+
e.preventDefault();
|
|
503
|
+
if (activeEl) {
|
|
504
|
+
deactivate();
|
|
505
|
+
doc.getSelection()?.removeAllRanges();
|
|
506
|
+
}
|
|
507
|
+
};
|
|
508
|
+
|
|
481
509
|
/**
|
|
482
510
|
* Reordering is the block action bar's drag handle, full stop. A `contenteditable` region is
|
|
483
511
|
* natively draggable by its text, which would both fight text selection and move content behind
|
|
@@ -501,6 +529,7 @@ export function startEditableRoot(
|
|
|
501
529
|
container.addEventListener("compositionend", onCompositionEnd);
|
|
502
530
|
container.addEventListener("dragstart", onDragStart);
|
|
503
531
|
doc.addEventListener("pointerdown", onPointerDownCapture, true);
|
|
532
|
+
doc.addEventListener("mousedown", onMouseDownCapture, true);
|
|
504
533
|
|
|
505
534
|
return {
|
|
506
535
|
capture: () => captureDocSelection(container, deps.isEditableBlock),
|
|
@@ -542,6 +571,7 @@ export function startEditableRoot(
|
|
|
542
571
|
container.removeEventListener("compositionend", onCompositionEnd);
|
|
543
572
|
container.removeEventListener("dragstart", onDragStart);
|
|
544
573
|
doc.removeEventListener("pointerdown", onPointerDownCapture, true);
|
|
574
|
+
doc.removeEventListener("mousedown", onMouseDownCapture, true);
|
|
545
575
|
// Deactivating commits, so the pending tick is cancelled rather than fired.
|
|
546
576
|
deactivate();
|
|
547
577
|
},
|
|
@@ -217,7 +217,7 @@ async function shareProjectSchemas(
|
|
|
217
217
|
*/
|
|
218
218
|
export function refreshExtensionUi(platform: Pick<StudioPlatform, "fetchProjectSchemas">): void {
|
|
219
219
|
void shareProjectSchemas(platform);
|
|
220
|
-
// `installed`
|
|
220
|
+
// `installed` is a per-project fact, so an install or a project switch can move
|
|
221
221
|
// Them even when the shipped half has not changed.
|
|
222
222
|
void loadExtensionCatalog();
|
|
223
223
|
void import("../settings/extension-sections")
|
|
@@ -21,8 +21,8 @@ const STORAGE_KEY = "jx_github_token";
|
|
|
21
21
|
/**
|
|
22
22
|
* The GitHub OAuth app every Jx sign-in authorizes against: the browser's device flow in
|
|
23
23
|
* `github-auth.ts`, and the desktop launcher's RFC 8252 loopback redirect
|
|
24
|
-
* (`packages/desktop/src/github-signin.ts`). Here, in the module with no UI dependency,
|
|
25
|
-
* launcher's main process imports it too.
|
|
24
|
+
* (`packages/desktop/src/backend/github-signin.ts`). Here, in the module with no UI dependency,
|
|
25
|
+
* because the launcher's main process imports it too.
|
|
26
26
|
*/
|
|
27
27
|
export const GITHUB_OAUTH_CLIENT_ID = "Ov23liYVlMFpgjOEPXJH";
|
|
28
28
|
|
|
@@ -67,6 +67,7 @@ import { dirName, previewFileSrc } from "../files/media-paths";
|
|
|
67
67
|
import { forgetVersions } from "../files/asset-versions";
|
|
68
68
|
import { loadMediaUsages, peekMediaUsages } from "../files/media-usage";
|
|
69
69
|
import { onProjectMediaChange, projectMediaFiles } from "../ui/media-picker";
|
|
70
|
+
import { openMenu } from "../surfaces/menu";
|
|
70
71
|
import { openMediaTab } from "./media-open";
|
|
71
72
|
import { factsLine, mediaStageFor, specimenOf, usageOf } from "./media-pane";
|
|
72
73
|
import {
|
|
@@ -93,6 +94,7 @@ import type {
|
|
|
93
94
|
MediaTileRow,
|
|
94
95
|
} from "../surfaces/media-library";
|
|
95
96
|
import type { MediaPickDialogHandle } from "../surfaces/media-pick-dialog";
|
|
97
|
+
import type { MenuHandle, MenuRowProjection } from "../surfaces/menu";
|
|
96
98
|
import type { PreviewObserver } from "../browse/library-preview";
|
|
97
99
|
import type { Tab } from "../tabs/tab";
|
|
98
100
|
import type { CanvasSurface } from "../canvas/canvas-surface";
|
|
@@ -788,11 +790,11 @@ function actionsFor(mount: LibraryMount) {
|
|
|
788
790
|
activate(mount, selected);
|
|
789
791
|
}
|
|
790
792
|
},
|
|
793
|
+
contextMenu: (file: string, x: number, y: number) => showTileMenu(mount, file, x, y),
|
|
791
794
|
copyRef: () => {
|
|
792
795
|
const entry = selectedEntry(mount);
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
void navigator.clipboard.writeText(entry.ref);
|
|
796
|
+
if (entry) {
|
|
797
|
+
copySiteUrl(entry);
|
|
796
798
|
}
|
|
797
799
|
},
|
|
798
800
|
dragOut: () => {
|
|
@@ -913,6 +915,74 @@ function actionsFor(mount: LibraryMount) {
|
|
|
913
915
|
}
|
|
914
916
|
|
|
915
917
|
/** Enter or a double-click: open the viewer, or in pick mode, use the file. */
|
|
918
|
+
/** Put a file's site URL on the clipboard. */
|
|
919
|
+
function copySiteUrl(entry: ProjectMediaFile): void {
|
|
920
|
+
// `navigator.clipboard` is [SecureContext]: a plain-http host has none, whatever lib.dom says.
|
|
921
|
+
if ("clipboard" in navigator) {
|
|
922
|
+
void navigator.clipboard.writeText(entry.ref);
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
/**
|
|
927
|
+
* The tile menu that is up, if any. One handle, because only one can be open: a second kit menu
|
|
928
|
+
* opened over the first is how two of them light-dismiss each other.
|
|
929
|
+
*/
|
|
930
|
+
let _tileMenu: MenuHandle | null = null;
|
|
931
|
+
|
|
932
|
+
/**
|
|
933
|
+
* A right-click on a tile: select it, then offer the detail pane's actions at the pointer.
|
|
934
|
+
*
|
|
935
|
+
* The same four verbs as the detail pane's (studio.md §22.6), in the order the pane draws them, and
|
|
936
|
+
* each calls what the pane's control calls, so the menu cannot drift from the pane. Selecting first
|
|
937
|
+
* is what makes the pane describe the file the menu acts on, as a right-click on a row does in the
|
|
938
|
+
* Content workspace.
|
|
939
|
+
*/
|
|
940
|
+
function showTileMenu(mount: LibraryMount, file: string, x: number, y: number): void {
|
|
941
|
+
select(mount, file);
|
|
942
|
+
const entry = selectedEntry(mount);
|
|
943
|
+
if (!entry) {
|
|
944
|
+
return;
|
|
945
|
+
}
|
|
946
|
+
_tileMenu?.close();
|
|
947
|
+
const row = (
|
|
948
|
+
id: string,
|
|
949
|
+
title: string,
|
|
950
|
+
run: () => void,
|
|
951
|
+
extra: Partial<MenuRowProjection> = {},
|
|
952
|
+
): MenuRowProjection => ({
|
|
953
|
+
destructive: false,
|
|
954
|
+
disabled: false,
|
|
955
|
+
dividerAbove: false,
|
|
956
|
+
id,
|
|
957
|
+
run,
|
|
958
|
+
title,
|
|
959
|
+
...extra,
|
|
960
|
+
});
|
|
961
|
+
_tileMenu = openMenu({
|
|
962
|
+
label: `${entry.name} actions`,
|
|
963
|
+
onClosed: (handle) => {
|
|
964
|
+
if (_tileMenu === handle) {
|
|
965
|
+
_tileMenu = null;
|
|
966
|
+
}
|
|
967
|
+
},
|
|
968
|
+
origin: { x, y },
|
|
969
|
+
region: "media-tile",
|
|
970
|
+
rows: [
|
|
971
|
+
row("open", "Open in Viewer", () => openMediaTab(file)),
|
|
972
|
+
row("copy-url", "Copy Site URL", () => copySiteUrl(entry)),
|
|
973
|
+
row("replace", "Replace…", () => requestMediaReplace(file), { dividerAbove: true }),
|
|
974
|
+
row(
|
|
975
|
+
"delete",
|
|
976
|
+
"Delete",
|
|
977
|
+
() => {
|
|
978
|
+
void deleteMediaFile(file);
|
|
979
|
+
},
|
|
980
|
+
{ destructive: true, dividerAbove: true },
|
|
981
|
+
),
|
|
982
|
+
],
|
|
983
|
+
});
|
|
984
|
+
}
|
|
985
|
+
|
|
916
986
|
function activate(mount: LibraryMount, file: string): void {
|
|
917
987
|
if (mount.mode === "pick") {
|
|
918
988
|
const entry = selectedEntry(mount);
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/// <reference lib="dom" />
|
|
2
2
|
/**
|
|
3
3
|
* Ensure the active project's dependencies are installed before the editor loads. When the backend
|
|
4
|
-
* reports node_modules is missing,
|
|
5
|
-
*
|
|
4
|
+
* reports node_modules is missing, install them behind a blocking progress modal so component and
|
|
5
|
+
* format resolution see the real dependency tree. No-op on platforms without package support.
|
|
6
6
|
*
|
|
7
7
|
* It REPORTS whether an install actually ran, because "format resolution sees the real dependency
|
|
8
8
|
* tree" was only half true: project activation fetches the format registry before calling this, and
|
|
@@ -17,7 +17,7 @@ import { shouldInstallAutomation } from "../services/automation";
|
|
|
17
17
|
|
|
18
18
|
export async function ensureDependenciesInstalled(): Promise<boolean> {
|
|
19
19
|
// Automation/screenshot runs open projects read-only to drive the canvas — they must never mutate
|
|
20
|
-
// The project on disk (
|
|
20
|
+
// The project on disk (an install would litter node_modules and a lockfile into starter templates).
|
|
21
21
|
if (shouldInstallAutomation(location.search)) {
|
|
22
22
|
return false;
|
|
23
23
|
}
|
|
@@ -35,7 +35,7 @@ export async function ensureDependenciesInstalled(): Promise<boolean> {
|
|
|
35
35
|
return false;
|
|
36
36
|
}
|
|
37
37
|
const progress = showProgressModal({
|
|
38
|
-
status: "
|
|
38
|
+
status: "Installing dependencies…",
|
|
39
39
|
title: "Installing dependencies",
|
|
40
40
|
});
|
|
41
41
|
try {
|
|
@@ -44,7 +44,7 @@ export async function ensureDependenciesInstalled(): Promise<boolean> {
|
|
|
44
44
|
progress.done();
|
|
45
45
|
return true;
|
|
46
46
|
}
|
|
47
|
-
progress.fail(result.log ?? "
|
|
47
|
+
progress.fail(result.log ?? "Install failed");
|
|
48
48
|
} catch (error) {
|
|
49
49
|
progress.fail(error instanceof Error ? error.message : String(error));
|
|
50
50
|
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* updates. The on-open @jxsuite update rewrites package.json and bun.lock without committing, so a
|
|
5
5
|
* teammate pushing the same update makes a later `git pull` refuse to merge. Since those local
|
|
6
6
|
* changes are machine-generated and reproducible, we discard them, pull, and re-run the package
|
|
7
|
-
* sync (re-apply the update if the pulled project is still behind, or
|
|
7
|
+
* sync (re-apply the update if the pulled project is still behind, or an install if the pull
|
|
8
8
|
* brought it). Detection is state-based (git status + content comparison against `@{u}`), never git
|
|
9
9
|
* stderr parsing, so it works identically across backends and locales.
|
|
10
10
|
*/
|
|
@@ -188,7 +188,7 @@ async function applyDiscard(plan: PackagePullPlan): Promise<void> {
|
|
|
188
188
|
|
|
189
189
|
/**
|
|
190
190
|
* Bring packages back in line after a pull that touched (or followed a discard of) package files:
|
|
191
|
-
* re-apply the @jxsuite update if the pulled project is still behind, otherwise
|
|
191
|
+
* re-apply the @jxsuite update if the pulled project is still behind, otherwise an install so
|
|
192
192
|
* node_modules matches the pulled lockfile. Never throws — a successful pull must not turn into an
|
|
193
193
|
* error banner; failures surface through the progress modal.
|
|
194
194
|
*/
|
|
@@ -209,7 +209,7 @@ async function syncPackagesAfterPull(): Promise<void> {
|
|
|
209
209
|
return;
|
|
210
210
|
}
|
|
211
211
|
const progress = showProgressModal({
|
|
212
|
-
status: "
|
|
212
|
+
status: "Installing dependencies…",
|
|
213
213
|
title: "Syncing dependencies",
|
|
214
214
|
});
|
|
215
215
|
try {
|
|
@@ -217,7 +217,7 @@ async function syncPackagesAfterPull(): Promise<void> {
|
|
|
217
217
|
if (result.ok) {
|
|
218
218
|
progress.done();
|
|
219
219
|
} else {
|
|
220
|
-
progress.fail(result.log ?? "
|
|
220
|
+
progress.fail(result.log ?? "Install failed");
|
|
221
221
|
}
|
|
222
222
|
} catch (error) {
|
|
223
223
|
progress.fail(errorMessage(error));
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* What this replaces: `ui/progress-modal.ts` was **the only surface in Studio with a real error
|
|
6
6
|
* view** — a red headline, the captured log in a `<pre>`, an explicit Close — and it had four call
|
|
7
7
|
* sites, all package operations. It blocked the whole app, offered no cancel, and vanished
|
|
8
|
-
* completely on success, so
|
|
8
|
+
* completely on success, so an install that hung had no exit and one that failed had a log you
|
|
9
9
|
* could read exactly once. Every OTHER long operation — git clone, publish, media upload, site
|
|
10
10
|
* import, project open — got a three-second grey line or nothing at all.
|
|
11
11
|
*
|
package/src/platform-errors.ts
CHANGED
|
@@ -26,6 +26,8 @@ export interface PlatformErrorInfo {
|
|
|
26
26
|
heading?: string;
|
|
27
27
|
/** What to do once one of its actions is done. */
|
|
28
28
|
retry?: RetryHint;
|
|
29
|
+
/** The paths a `conflict` names: the files the remote changed that this side changed too. */
|
|
30
|
+
conflicts?: string[];
|
|
29
31
|
}
|
|
30
32
|
|
|
31
33
|
const RETRY_HINTS = new Set<string>(["reload", "repeat", "none"]);
|
|
@@ -90,7 +92,7 @@ export function platformErrorInfo(cause: unknown): PlatformErrorInfo {
|
|
|
90
92
|
if (!isJsonObject(cause)) {
|
|
91
93
|
return {};
|
|
92
94
|
}
|
|
93
|
-
const { actions, code, heading, installUrl, retry, type } = cause;
|
|
95
|
+
const { actions, code, conflicts, heading, installUrl, retry, type } = cause;
|
|
94
96
|
const info: PlatformErrorInfo = {};
|
|
95
97
|
// A problem type wins over a legacy code: it is the shape the backend is migrating toward.
|
|
96
98
|
const derived = problemSlug(type) ?? (isString(code) ? normalizeCode(code) : null);
|
|
@@ -109,9 +111,23 @@ export function platformErrorInfo(cause: unknown): PlatformErrorInfo {
|
|
|
109
111
|
if (isRetryHint(retry)) {
|
|
110
112
|
info.retry = retry;
|
|
111
113
|
}
|
|
114
|
+
if (Array.isArray(conflicts)) {
|
|
115
|
+
info.conflicts = conflicts.filter((path) => isString(path));
|
|
116
|
+
}
|
|
112
117
|
return info;
|
|
113
118
|
}
|
|
114
119
|
|
|
120
|
+
/**
|
|
121
|
+
* The files a refused publish overlaps on the remote, when the error is that refusal: a pull or a
|
|
122
|
+
* commit the remote has moved under, whose changes touch the same paths (`pull_conflict`, or the
|
|
123
|
+
* `conflict` problem type carrying `conflicts`). Null for every other failure.
|
|
124
|
+
*/
|
|
125
|
+
export function remoteConflictsOf(cause: unknown): string[] | null {
|
|
126
|
+
const info = platformErrorInfo(cause);
|
|
127
|
+
const conflicting = info.code === "pull-conflict" || info.code === "conflict";
|
|
128
|
+
return conflicting && info.conflicts ? info.conflicts : null;
|
|
129
|
+
}
|
|
130
|
+
|
|
115
131
|
/** The GitHub-App install link when the error is the structured needs-installation 403. */
|
|
116
132
|
export function installUrlOf(cause: unknown): string | null {
|
|
117
133
|
const info = platformErrorInfo(cause);
|
package/src/platforms/cloud.ts
CHANGED
|
@@ -7,8 +7,9 @@
|
|
|
7
7
|
* before Studio boots — while project-less mode (null) powers the /studio welcome screen.
|
|
8
8
|
*
|
|
9
9
|
* Cloud omissions (Studio degrades per @jxsuite/protocol's route table): pickDirectory, package
|
|
10
|
-
* install
|
|
11
|
-
*
|
|
10
|
+
* install and the install-on-open check (a cloud session has no `node_modules`; add, remove and
|
|
11
|
+
* set-versions update `package.json` and `package-lock.json` through the session's package
|
|
12
|
+
* service), multi-window, gitClone, resolveClass, component discovery, code services.
|
|
12
13
|
*/
|
|
13
14
|
|
|
14
15
|
import { negotiateCollab } from "@jxsuite/collab/negotiate";
|
|
@@ -36,6 +37,7 @@ import type {
|
|
|
36
37
|
FsEvent,
|
|
37
38
|
FsResyncReason,
|
|
38
39
|
GitBranchesResult,
|
|
40
|
+
GitCommitBranchResult,
|
|
39
41
|
GitLogEntry,
|
|
40
42
|
GitStatusResult,
|
|
41
43
|
ImportProgressEvent,
|
|
@@ -43,6 +45,8 @@ import type {
|
|
|
43
45
|
ImportSiteOptions,
|
|
44
46
|
OfferedAction,
|
|
45
47
|
PackageInfo,
|
|
48
|
+
PackageOpResult,
|
|
49
|
+
PackageVersionInfo,
|
|
46
50
|
ProjectListEntry,
|
|
47
51
|
SiteBuildResult,
|
|
48
52
|
ProjectSchemasResponse,
|
|
@@ -217,6 +221,7 @@ interface StructuredBody extends ErrorBody {
|
|
|
217
221
|
actions?: JsonValue;
|
|
218
222
|
heading?: JsonValue;
|
|
219
223
|
retry?: JsonValue;
|
|
224
|
+
conflicts?: JsonValue;
|
|
220
225
|
}
|
|
221
226
|
|
|
222
227
|
/** What {@link platformError} carries on the Error it returns: the HTTP status, plus what it found. */
|
|
@@ -268,6 +273,10 @@ async function platformError(res: Response, fallback: string): Promise<Error> {
|
|
|
268
273
|
if (retry !== undefined) {
|
|
269
274
|
details.retry = retry;
|
|
270
275
|
}
|
|
276
|
+
const conflicts = body?.conflicts;
|
|
277
|
+
if (Array.isArray(conflicts)) {
|
|
278
|
+
details.conflicts = conflicts.filter((path) => isString(path));
|
|
279
|
+
}
|
|
271
280
|
return Object.assign(new Error(problemDetail(body) ?? fallback), details);
|
|
272
281
|
}
|
|
273
282
|
|
|
@@ -947,17 +956,6 @@ export function createCloudPlatform(project: CloudProject | null): StudioPlatfor
|
|
|
947
956
|
}
|
|
948
957
|
}
|
|
949
958
|
|
|
950
|
-
async function writePackageJson(pkg: JsonObject): Promise<void> {
|
|
951
|
-
const res = await api("/file", {
|
|
952
|
-
method: "PUT",
|
|
953
|
-
headers: { "Content-Type": "application/json" },
|
|
954
|
-
body: JSON.stringify({ path: "package.json", content: `${JSON.stringify(pkg, null, 2)}\n` }),
|
|
955
|
-
});
|
|
956
|
-
if (!res.ok) {
|
|
957
|
-
throw new Error(await errorMessage(res, "Failed to update package.json"));
|
|
958
|
-
}
|
|
959
|
-
}
|
|
960
|
-
|
|
961
959
|
// The bound session's /raw tree; see `documentBaseUrl` below for why both members ride together.
|
|
962
960
|
const repoAssets: Pick<StudioPlatform, "assetSpace" | "documentBaseUrl"> = {};
|
|
963
961
|
if (base) {
|
|
@@ -1284,10 +1282,12 @@ export function createCloudPlatform(project: CloudProject | null): StudioPlatfor
|
|
|
1284
1282
|
*
|
|
1285
1283
|
* A Worker ships a fixed set of extension packages (specs/extensions.md §5.5), decided by the
|
|
1286
1284
|
* platform build rather than by this repository's `extensions/` tree, so the gateway is the
|
|
1287
|
-
* only thing that knows.
|
|
1288
|
-
*
|
|
1289
|
-
*
|
|
1290
|
-
*
|
|
1285
|
+
* only thing that knows. An extension the Worker does not bundle is dropped from the registry
|
|
1286
|
+
* before composition, so advertising it would promise a toggle that silently does nothing.
|
|
1287
|
+
*
|
|
1288
|
+
* What the Worker bundles is what it can EDIT with, not what the project gets: the site is
|
|
1289
|
+
* built in Pages CI from the project's own install, so an entry the project has not declared is
|
|
1290
|
+
* install-then-enable here exactly as on every other backend (specs/extensions.md §3).
|
|
1291
1291
|
*
|
|
1292
1292
|
* `installed` here means DECLARED. Nothing resolves a module in a Worker: `addPackage` is a
|
|
1293
1293
|
* manifest edit and resolution happens later in Pages CI, so the manifest is the only fact this
|
|
@@ -1309,7 +1309,6 @@ export function createCloudPlatform(project: CloudProject | null): StudioPlatfor
|
|
|
1309
1309
|
...Object.keys(dependencyBlock(pkg, "devDependencies")),
|
|
1310
1310
|
]);
|
|
1311
1311
|
for (const entry of entries) {
|
|
1312
|
-
entry.bundled = true;
|
|
1313
1312
|
entry.installed = declared.has(entry.name);
|
|
1314
1313
|
}
|
|
1315
1314
|
return entries;
|
|
@@ -1435,28 +1434,49 @@ export function createCloudPlatform(project: CloudProject | null): StudioPlatfor
|
|
|
1435
1434
|
return null;
|
|
1436
1435
|
},
|
|
1437
1436
|
|
|
1438
|
-
// ─── Packages (
|
|
1437
|
+
// ─── Packages (the session's lockfile service; nothing installs) ───
|
|
1439
1438
|
|
|
1439
|
+
/* Each mutation runs npm's installer lockfile-only on the platform, which writes
|
|
1440
|
+
`package.json` and `package-lock.json` back into the shared tree as one change, so a commit
|
|
1441
|
+
carries a lockfile a build can install from frozen (server.md §4.1). A failure changes
|
|
1442
|
+
nothing and its message names the cause. */
|
|
1440
1443
|
async addPackage(name: string) {
|
|
1441
|
-
const
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1444
|
+
const res = await api("/packages/add", {
|
|
1445
|
+
body: JSON.stringify({ name }),
|
|
1446
|
+
headers: { "Content-Type": "application/json" },
|
|
1447
|
+
method: "POST",
|
|
1448
|
+
});
|
|
1449
|
+
if (!res.ok) {
|
|
1450
|
+
throw new Error(await errorMessage(res, `Failed to add ${name}`));
|
|
1451
|
+
}
|
|
1449
1452
|
},
|
|
1450
1453
|
|
|
1451
1454
|
async removePackage(name: string) {
|
|
1452
|
-
const
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1455
|
+
const res = await api("/packages/remove", {
|
|
1456
|
+
body: JSON.stringify({ name }),
|
|
1457
|
+
headers: { "Content-Type": "application/json" },
|
|
1458
|
+
method: "POST",
|
|
1459
|
+
});
|
|
1460
|
+
if (!res.ok) {
|
|
1461
|
+
throw new Error(await errorMessage(res, `Failed to remove ${name}`));
|
|
1462
|
+
}
|
|
1463
|
+
},
|
|
1464
|
+
|
|
1465
|
+
async setPackageVersions(updates: { name: string; version: string; dev?: boolean }[]) {
|
|
1466
|
+
const res = await api("/packages/set-versions", {
|
|
1467
|
+
body: JSON.stringify({ updates }),
|
|
1468
|
+
headers: { "Content-Type": "application/json" },
|
|
1469
|
+
method: "POST",
|
|
1470
|
+
});
|
|
1471
|
+
if (!res.ok) {
|
|
1472
|
+
return { log: await errorMessage(res, "Failed to update versions"), ok: false };
|
|
1458
1473
|
}
|
|
1459
|
-
|
|
1474
|
+
return readJson<PackageOpResult>(res);
|
|
1475
|
+
},
|
|
1476
|
+
|
|
1477
|
+
async packageVersions() {
|
|
1478
|
+
const res = await api("/packages/versions");
|
|
1479
|
+
return res.ok ? readJson<PackageVersionInfo[]>(res) : [];
|
|
1460
1480
|
},
|
|
1461
1481
|
|
|
1462
1482
|
async listPackages(): Promise<PackageInfo[]> {
|
|
@@ -1576,6 +1596,14 @@ export function createCloudPlatform(project: CloudProject | null): StudioPlatfor
|
|
|
1576
1596
|
await okJson(await postJson("/git/create-branch", { name }), "Failed to create branch");
|
|
1577
1597
|
},
|
|
1578
1598
|
|
|
1599
|
+
/** One commit, parented on the session's base, onto a branch the backend creates for it. */
|
|
1600
|
+
async gitCommitBranch(opts: { branch: string; message: string }) {
|
|
1601
|
+
return okJson<GitCommitBranchResult>(
|
|
1602
|
+
await postJson("/git/commit-branch", { branch: opts.branch, message: opts.message }),
|
|
1603
|
+
`Could not publish to ${opts.branch}`,
|
|
1604
|
+
);
|
|
1605
|
+
},
|
|
1606
|
+
|
|
1579
1607
|
async gitDiff(path?: string) {
|
|
1580
1608
|
const data = await okJson<{ diff: string }>(
|
|
1581
1609
|
await api(`/git/diff?path=${encodeURIComponent(path ?? "")}`),
|