@jxsuite/studio 10.0.0 → 10.2.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-j56903f1.js → 2020-bx702wqa.js} +3 -3
- package/dist/chunks/adapter-pp011m5x.js +4 -0
- package/dist/chunks/add-new-flow-ecqmzz76.js +4 -0
- package/dist/chunks/cf-account-picker-c07znkqt.js +4 -0
- package/dist/chunks/cf-settings-0v2bmqy0.js +4 -0
- package/dist/chunks/components-yn0dn2pq.js +4 -0
- package/dist/chunks/content-source-g0j41yy6.js +4 -0
- package/dist/chunks/{convert-to-repeater-fhdwyg14.js → convert-to-repeater-sbjk7tgn.js} +3 -3
- package/dist/chunks/editor-xz02jeh1.js +4 -0
- package/dist/chunks/entry-commands-kmt02crp.js +4 -0
- package/dist/chunks/extension-sections-0rprf12d.js +4 -0
- package/dist/chunks/file-ops-mexj5qed.js +4 -0
- package/dist/chunks/files-b2tcppdd.js +4 -0
- package/dist/chunks/format-host-btsevv0c.js +4 -0
- package/dist/chunks/fs-events-r0apejva.js +4 -0
- package/dist/chunks/grid-open-n2a8xkht.js +4 -0
- package/dist/chunks/{grid-view-ka55z38f.js → grid-view-tghcwrrt.js} +12 -12
- package/dist/chunks/iframe-host-gx0ypyh3.js +4 -0
- package/dist/chunks/index-bs4k9tr6.js +4 -0
- package/dist/chunks/{index-qb98xhse.js → index-vr006kbt.js} +3 -3
- package/dist/chunks/{javascript-ces13wny.js → javascript-z1g3s9fd.js} +2 -2
- package/dist/chunks/{jsonMode-6md16d4v.js → jsonMode-7szwg8c4.js} +5 -5
- package/dist/chunks/jx-validate-ymf10teh.js +4 -0
- package/dist/chunks/layers-p9nfya38.js +4 -0
- package/dist/chunks/layers-panel-tt1hzhhf.js +4 -0
- package/dist/chunks/lit-html-x18n2sfz.js +4 -0
- package/dist/chunks/media-library-0n45gv8r.js +4 -0
- package/dist/chunks/media-picker-s09at95z.js +4 -0
- package/dist/chunks/{monaco-binding-p8j80s4w.js → monaco-binding-v42w9c1v.js} +3 -3
- package/dist/chunks/monaco-buffer-een2ea0v.js +4 -0
- package/dist/chunks/{monaco-setup-wasktzfg.js → monaco-setup-7bffxd6v.js} +3 -3
- package/dist/chunks/notify-qwb8radt.js +4 -0
- package/dist/chunks/parse-v77xtwv7.js +4 -0
- package/dist/chunks/platform-8jnez0wr.js +4 -0
- package/dist/chunks/publish-panel-aysnj611.js +4 -0
- package/dist/chunks/{publish-quick-vr695j26.js → publish-quick-kpbqs9mz.js} +3 -3
- package/dist/chunks/publish-workspace-nd0a8q99.js +4 -0
- package/dist/chunks/quick-search-vg0jkhk2.js +4 -0
- package/dist/chunks/references-a7v6a0e2.js +4 -0
- package/dist/chunks/right-panel-cvk1vxx5.js +4 -0
- package/dist/chunks/source-control-fsqzc6he.js +4 -0
- package/dist/chunks/studio-05jfwbmf.js +5 -0
- package/dist/chunks/{studio-hy4ye0dx.js → studio-0q5jwddh.js} +7 -7
- package/dist/chunks/{studio-jttyqrmh.js → studio-0w2dr0q8.js} +4 -4
- package/dist/chunks/{studio-xkxetm65.js → studio-1tzpds97.js} +5 -5
- package/dist/chunks/{studio-m48v13s3.js → studio-216b27sv.js} +3 -3
- package/dist/chunks/studio-2ejhqns4.js +5 -0
- package/dist/chunks/studio-2r90cts3.js +16 -0
- package/dist/chunks/{studio-h310mbqm.js → studio-399a25za.js} +4 -4
- package/dist/chunks/studio-399xknkb.js +6 -0
- package/dist/chunks/{studio-wxhy1yh7.js → studio-43y04qck.js} +4 -4
- package/dist/chunks/{studio-06j4en5h.js → studio-4g8yb6jn.js} +2 -2
- package/dist/chunks/{studio-jw0q5j6k.js → studio-4jhw38b2.js} +4 -4
- package/dist/chunks/{studio-3v5cagy9.js → studio-4ncmwqw5.js} +4 -4
- package/dist/chunks/studio-5atwjgkj.js +7 -0
- package/dist/chunks/{studio-gr36vnte.js → studio-5frqk7c0.js} +4 -4
- package/dist/chunks/{studio-9dz76yqc.js → studio-646nn2ys.js} +5 -5
- package/dist/chunks/{studio-tj2s0tks.js → studio-6cwj6xrx.js} +4 -4
- package/dist/chunks/studio-6hzdjfv0.js +5 -0
- package/dist/chunks/{studio-hcsba201.js → studio-71m92v9s.js} +7 -7
- package/dist/chunks/{studio-3c8pp7pb.js → studio-75kcdbsk.js} +4 -4
- package/dist/chunks/{studio-gwrhcrss.js → studio-78pywnp2.js} +5 -5
- package/dist/chunks/{studio-9cxmp61c.js → studio-86r8hp7r.js} +5 -5
- package/dist/chunks/studio-886t6ztg.js +5 -0
- package/dist/chunks/studio-8wnvtr4z.js +5 -0
- package/dist/chunks/studio-8z66jk4z.js +5 -0
- package/dist/chunks/{studio-ajw4k8hp.js → studio-9rbe2xpw.js} +3 -3
- package/dist/chunks/{studio-y26hx9re.js → studio-awxwjawq.js} +4 -4
- package/dist/chunks/studio-b2qpt0xy.js +14 -0
- package/dist/chunks/{studio-dx73md4n.js → studio-bvs9kpst.js} +7 -7
- package/dist/chunks/studio-c71806xz.js +7 -0
- package/dist/chunks/{studio-7dzyz9n8.js → studio-cbwks69x.js} +144 -144
- package/dist/chunks/studio-cg1bfyhf.js +9 -0
- package/dist/chunks/studio-cg710sk9.js +5 -0
- package/dist/chunks/studio-cw0wb9qk.js +7 -0
- package/dist/chunks/studio-cyheys7k.js +5 -0
- package/dist/chunks/studio-ea5m9kg5.js +5 -0
- package/dist/chunks/{studio-ry81j4q1.js → studio-ea74s8c1.js} +4 -4
- package/dist/chunks/{studio-xdpz9rpe.js → studio-efdxjwyp.js} +34 -34
- package/dist/chunks/{studio-agyvzfm4.js → studio-egmw2837.js} +4 -4
- package/dist/chunks/{studio-5tzgq6fx.js → studio-er5f383a.js} +4 -4
- package/dist/chunks/studio-f746039f.js +5 -0
- package/dist/chunks/studio-fmdymd9v.js +5 -0
- package/dist/chunks/studio-gcm12z00.js +6 -0
- package/dist/chunks/{studio-vswcpzk7.js → studio-gj98m8gy.js} +5 -5
- package/dist/chunks/{studio-yg3djpnm.js → studio-hasbgjah.js} +4 -4
- package/dist/chunks/studio-hhp4x8xf.js +6 -0
- package/dist/chunks/{studio-3w35qvay.js → studio-hz5n7x27.js} +4 -4
- package/dist/chunks/{studio-7xag4na8.js → studio-j3d2fjqz.js} +4 -4
- package/dist/chunks/studio-jeraxdgv.js +7 -0
- package/dist/chunks/studio-jvgek9xt.js +5 -0
- package/dist/chunks/studio-jws1a87n.js +5 -0
- package/dist/chunks/studio-k2sz13zg.js +5 -0
- package/dist/chunks/studio-k3m4jkdk.js +5 -0
- package/dist/chunks/{studio-r17asgs0.js → studio-kg1abnz0.js} +4 -4
- package/dist/chunks/{studio-s0fg2wav.js → studio-kg5ya5r2.js} +4 -4
- package/dist/chunks/studio-khwbgrtg.js +5 -0
- package/dist/chunks/studio-mx6wzh25.js +5 -0
- package/dist/chunks/studio-mxs3ar9t.js +5 -0
- package/dist/chunks/studio-nm4szey4.js +5 -0
- package/dist/chunks/studio-nqxqf8p2.js +4 -0
- package/dist/chunks/{studio-h5tpz5ke.js → studio-pjtntksy.js} +4 -4
- package/dist/chunks/studio-qh8berdz.js +5 -0
- package/dist/chunks/studio-qn7z4krm.js +5 -0
- package/dist/chunks/studio-qyed0ah6.js +5 -0
- package/dist/chunks/{studio-x97ay0v4.js → studio-r4bcdwhq.js} +4 -4
- package/dist/chunks/{studio-wxdcd42g.js → studio-r7pf0rkv.js} +6 -6
- package/dist/chunks/studio-rtfnhrdg.js +5 -0
- package/dist/chunks/{studio-fy7jzj9j.js → studio-rzqaz21g.js} +4 -4
- package/dist/chunks/studio-s5tzw84t.js +5 -0
- package/dist/chunks/{studio-1e8dw2w2.js → studio-sevbc3xv.js} +4 -4
- package/dist/chunks/{studio-ewarnzm4.js → studio-sxdb9h3q.js} +6 -6
- package/dist/chunks/studio-t51y8v1d.js +5 -0
- package/dist/chunks/{studio-k3bcy02e.js → studio-t98vq4r6.js} +4 -4
- package/dist/chunks/studio-th7z3fg7.js +5 -0
- package/dist/chunks/studio-tsxpqvh0.js +6 -0
- package/dist/chunks/{studio-396srjbk.js → studio-v1yascv2.js} +4 -4
- package/dist/chunks/{studio-ekgtrndg.js → studio-wc6za3zs.js} +4 -4
- package/dist/chunks/{studio-ndskgqef.js → studio-wxq6v5sv.js} +4 -4
- package/dist/chunks/{studio-4kzbrr5t.js → studio-y60sts62.js} +3 -3
- package/dist/chunks/studio-ybnwymxw.js +5 -0
- package/dist/chunks/studio-zndrqawb.js +5 -0
- package/dist/chunks/studio-zwaxn5s2.js +5 -0
- package/dist/chunks/{tsMode-h6tkz0ea.js → tsMode-f54j66ym.js} +5 -5
- package/dist/chunks/workspace-home-x1787wpv.js +4 -0
- package/dist/chunks/workspace-zpe1np43.js +4 -0
- package/dist/chunks/{ws-client-e951my95.js → ws-client-e8kj684q.js} +3 -3
- package/dist/manifest.json +76 -75
- package/dist/studio.js +83 -81
- package/package.json +4 -4
- package/src/browse/library-model.ts +56 -23
- package/src/commands/context.ts +1 -0
- package/src/editor/shortcuts.ts +3 -0
- package/src/panels/data-grid.ts +0 -4
- package/src/panels/settings-menu.ts +3 -3
- package/src/panels/settings-pane.ts +42 -56
- package/src/platform-errors.ts +17 -1
- package/src/platforms/cloud.ts +14 -0
- package/src/publish/publish-changes.ts +136 -4
- package/src/services/ai-import-tools.ts +6 -3
- package/src/services/ai-models.ts +65 -11
- package/src/services/ai-settings.ts +45 -5
- package/src/services/document-assistant.ts +5 -2
- package/src/services/settings/definitions.ts +19 -0
- package/src/settings/contributed-section.ts +16 -7
- package/src/settings/css-vars-editor.ts +4 -4
- package/src/settings/general-settings.ts +1 -4
- package/src/settings/preferences-accounts.ts +2 -2
- package/src/settings/preferences-dialog.ts +4 -2
- package/src/settings/project-sections.ts +1 -1
- package/src/settings/section-registry.ts +5 -4
- package/src/settings/settings-document.ts +33 -24
- package/src/settings/settings-workspace.ts +205 -0
- package/src/shell.ts +31 -6
- package/src/surfaces/ai-credentials-form.json +59 -5
- package/src/surfaces/ai-credentials-form.ts +15 -2
- package/src/surfaces/commandbar.ts +7 -3
- package/src/surfaces/settings-contributed.json +4 -4
- package/src/surfaces/settings-contributed.ts +2 -2
- package/src/surfaces/settings-pane.json +262 -180
- package/src/surfaces/settings-pane.ts +63 -14
- package/src/types.ts +14 -0
- package/src/ui/ai-credentials-form.ts +128 -11
- package/src/ui/regions.ts +5 -5
- package/src/workspace/tab-workspace.ts +3 -2
- package/src/workspace/workspace-home.ts +18 -4
- package/src/workspace/workspace-ids.ts +40 -7
- package/dist/chunks/adapter-wc5zvg9a.js +0 -4
- package/dist/chunks/add-new-flow-snx1s4b0.js +0 -4
- package/dist/chunks/cf-account-picker-xyqw0jex.js +0 -4
- package/dist/chunks/cf-settings-hp1pjm0n.js +0 -4
- package/dist/chunks/components-nkwphrx3.js +0 -4
- package/dist/chunks/content-source-6a253wmk.js +0 -4
- package/dist/chunks/editor-wqrc68ca.js +0 -4
- package/dist/chunks/entry-commands-nef0wrmp.js +0 -4
- package/dist/chunks/extension-sections-8ez2rrk8.js +0 -4
- package/dist/chunks/file-ops-y3w71k1n.js +0 -4
- package/dist/chunks/files-de90apn9.js +0 -4
- package/dist/chunks/format-host-zfjz4rzm.js +0 -4
- package/dist/chunks/fs-events-77c0mr0x.js +0 -4
- package/dist/chunks/grid-open-4n4fjfpf.js +0 -4
- package/dist/chunks/iframe-host-na2mywz4.js +0 -4
- package/dist/chunks/index-ewmgdb7c.js +0 -4
- package/dist/chunks/jx-validate-b309c020.js +0 -4
- package/dist/chunks/layers-8rn8y6qv.js +0 -4
- package/dist/chunks/layers-panel-eyah7h9z.js +0 -4
- package/dist/chunks/lit-html-wyqw04y2.js +0 -4
- package/dist/chunks/media-library-gs3np47y.js +0 -4
- package/dist/chunks/media-picker-tt7dk4tb.js +0 -4
- package/dist/chunks/monaco-buffer-wsf6cmnq.js +0 -4
- package/dist/chunks/notify-qwst108r.js +0 -4
- package/dist/chunks/parse-n8dfxnqf.js +0 -4
- package/dist/chunks/platform-c16mqvmp.js +0 -4
- package/dist/chunks/publish-panel-d34bhvwj.js +0 -4
- package/dist/chunks/publish-workspace-2yr60mt4.js +0 -4
- package/dist/chunks/quick-search-e6xsdv0d.js +0 -4
- package/dist/chunks/references-gsj6trdv.js +0 -4
- package/dist/chunks/right-panel-33jv1knc.js +0 -4
- package/dist/chunks/source-control-bj9bejjv.js +0 -4
- package/dist/chunks/studio-03mqh8ax.js +0 -5
- package/dist/chunks/studio-0ydxv2mr.js +0 -7
- package/dist/chunks/studio-2hsqbq7b.js +0 -7
- package/dist/chunks/studio-2j86y34y.js +0 -6
- package/dist/chunks/studio-2ks6ka4g.js +0 -5
- package/dist/chunks/studio-34jrz12k.js +0 -5
- package/dist/chunks/studio-35vrtpdq.js +0 -5
- package/dist/chunks/studio-38g0jdz7.js +0 -5
- package/dist/chunks/studio-3zge48z4.js +0 -7
- package/dist/chunks/studio-45gyxpcq.js +0 -5
- package/dist/chunks/studio-5wzdy1jc.js +0 -16
- package/dist/chunks/studio-732mw0g8.js +0 -5
- package/dist/chunks/studio-771k2n12.js +0 -5
- package/dist/chunks/studio-88bmt9rh.js +0 -5
- package/dist/chunks/studio-c1pxszat.js +0 -5
- package/dist/chunks/studio-cpkamjdn.js +0 -14
- package/dist/chunks/studio-d038kqbs.js +0 -5
- package/dist/chunks/studio-d83m0wyq.js +0 -5
- package/dist/chunks/studio-dbxv7w4s.js +0 -5
- package/dist/chunks/studio-de1s2x9v.js +0 -5
- package/dist/chunks/studio-epenqmvk.js +0 -7
- package/dist/chunks/studio-et2q4s8n.js +0 -5
- package/dist/chunks/studio-h7weqf2q.js +0 -5
- package/dist/chunks/studio-hwejq03c.js +0 -5
- package/dist/chunks/studio-k3a8rp3d.js +0 -5
- package/dist/chunks/studio-p1n2xjda.js +0 -5
- package/dist/chunks/studio-pgm83a2f.js +0 -6
- package/dist/chunks/studio-pqqq6v85.js +0 -5
- package/dist/chunks/studio-psh8mwnw.js +0 -4
- package/dist/chunks/studio-rzg8hnkf.js +0 -5
- package/dist/chunks/studio-tyw7tk0x.js +0 -5
- package/dist/chunks/studio-v9ywz2se.js +0 -6
- package/dist/chunks/studio-vzgmyt0v.js +0 -5
- package/dist/chunks/studio-x08a3m0a.js +0 -5
- package/dist/chunks/studio-x3mjr5cc.js +0 -5
- package/dist/chunks/studio-xh683qzy.js +0 -5
- package/dist/chunks/studio-yx8hzqja.js +0 -5
- package/dist/chunks/studio-zg16eeq1.js +0 -5
- package/dist/chunks/studio-zgx7svjd.js +0 -9
- package/dist/chunks/studio-zkq3r91p.js +0 -5
- package/dist/chunks/workspace-home-zkkrxq3r.js +0 -4
- package/dist/chunks/workspace-tdqqjqaa.js +0 -4
- package/src/settings/settings-dialog.ts +0 -192
- package/src/surfaces/settings-dialog.json +0 -51
- package/src/surfaces/settings-dialog.ts +0 -169
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jxsuite/studio",
|
|
3
|
-
"version": "10.
|
|
3
|
+
"version": "10.2.0",
|
|
4
4
|
"description": "Jx Studio — visual builder for Jx documents",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -60,12 +60,12 @@
|
|
|
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.
|
|
63
|
+
"@jxsuite/ai": "^0.42.0",
|
|
64
64
|
"@jxsuite/collab": "^0.11.4",
|
|
65
|
-
"@jxsuite/create": "^1.5.
|
|
65
|
+
"@jxsuite/create": "^1.5.2",
|
|
66
66
|
"@jxsuite/formulas": "^0.0.26",
|
|
67
67
|
"@jxsuite/markup": "^1.0.2",
|
|
68
|
-
"@jxsuite/protocol": "^6.
|
|
68
|
+
"@jxsuite/protocol": "^6.2.0",
|
|
69
69
|
"@jxsuite/runtime": "^6.0.1",
|
|
70
70
|
"@jxsuite/schema": "^5.0.0",
|
|
71
71
|
"@jxsuite/site": "^2.2.2",
|
|
@@ -200,44 +200,64 @@ export function entryCollectionOf(path: string, pageExtensions?: readonly string
|
|
|
200
200
|
*/
|
|
201
201
|
const SKIP_DIRS = new Set(["node_modules", ".git", ".jx", ".cache"]);
|
|
202
202
|
|
|
203
|
+
/** What every branch of one scan shares. */
|
|
204
|
+
interface ScanRun {
|
|
205
|
+
platform: Pick<StudioPlatform, "listDirectory">;
|
|
206
|
+
out: LibraryFile[];
|
|
207
|
+
failures: ScanFailure[];
|
|
208
|
+
i18n: ResolvedI18n | null;
|
|
209
|
+
pageExtensions: readonly string[] | undefined;
|
|
210
|
+
/**
|
|
211
|
+
* Every directory some branch has claimed, the roots included. A collection may live in an
|
|
212
|
+
* ANCESTOR of the project (a site inside the vault it publishes declares `source: "../.."`), and
|
|
213
|
+
* that walk reaches the project's own directories under the same project-relative paths the roots
|
|
214
|
+
* walk: every page was listed twice. A directory is walked by whichever branch claims it first.
|
|
215
|
+
*/
|
|
216
|
+
claimed: Set<string>;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* The project root as a listing names it from outside: `""` (relative to itself) or `"."`. A walk
|
|
221
|
+
* from an ancestor stops there; the project's own directories are roots of the scan already, and
|
|
222
|
+
* the rest of the root (`dist/`, `scripts/`) holds no collection's entries.
|
|
223
|
+
*/
|
|
224
|
+
function isProjectRoot(path: string): boolean {
|
|
225
|
+
const normalized = normalizeDir(path);
|
|
226
|
+
return normalized === "" || normalized === ".";
|
|
227
|
+
}
|
|
228
|
+
|
|
203
229
|
/**
|
|
204
230
|
* Walk one directory tree, collecting files and recording — never swallowing — what it could not
|
|
205
231
|
* read. A failure at depth is reported against the directory that failed, not the root, so the
|
|
206
232
|
* Problem names the path the author can act on.
|
|
207
233
|
*/
|
|
208
|
-
async function walk(
|
|
209
|
-
dir: string,
|
|
210
|
-
platform: Pick<StudioPlatform, "listDirectory">,
|
|
211
|
-
out: LibraryFile[],
|
|
212
|
-
failures: ScanFailure[],
|
|
213
|
-
i18n: ResolvedI18n | null,
|
|
214
|
-
optional = false,
|
|
215
|
-
pageExtensions?: readonly string[],
|
|
216
|
-
): Promise<void> {
|
|
234
|
+
async function walk(dir: string, run: ScanRun, optional = false): Promise<void> {
|
|
217
235
|
let entries: DirEntry[];
|
|
218
236
|
try {
|
|
219
237
|
// Noted: the Library walks every media directory, so it is where most versions are learned.
|
|
220
|
-
entries = await notedListing(() => platform.listDirectory(dir));
|
|
238
|
+
entries = await notedListing(() => run.platform.listDirectory(dir));
|
|
221
239
|
} catch (error) {
|
|
222
240
|
// A declared collection's folder that does not exist yet is an empty collection, not a fault.
|
|
223
241
|
if (!optional) {
|
|
224
|
-
failures.push({ dir, error: errorMessage(error) });
|
|
242
|
+
run.failures.push({ dir, error: errorMessage(error) });
|
|
225
243
|
}
|
|
226
244
|
return;
|
|
227
245
|
}
|
|
228
246
|
const subdirectories: string[] = [];
|
|
229
247
|
for (const entry of entries) {
|
|
230
248
|
if (entry.type === "directory") {
|
|
231
|
-
|
|
249
|
+
const key = normalizeDir(entry.path);
|
|
250
|
+
if (!SKIP_DIRS.has(entry.name) && !isProjectRoot(key) && !run.claimed.has(key)) {
|
|
251
|
+
run.claimed.add(key);
|
|
232
252
|
subdirectories.push(entry.path);
|
|
233
253
|
}
|
|
234
254
|
continue;
|
|
235
255
|
}
|
|
236
256
|
const ext = extensionOf(entry.name);
|
|
237
|
-
out.push({
|
|
238
|
-
collection: entryCollectionOf(entry.path, pageExtensions),
|
|
257
|
+
run.out.push({
|
|
258
|
+
collection: entryCollectionOf(entry.path, run.pageExtensions),
|
|
239
259
|
ext,
|
|
240
|
-
locale: servedLocaleOfPath(entry.path, i18n) ?? undefined,
|
|
260
|
+
locale: servedLocaleOfPath(entry.path, run.i18n) ?? undefined,
|
|
241
261
|
media: MEDIA_EXTENSIONS.has(ext),
|
|
242
262
|
modified: entry.modified,
|
|
243
263
|
name: entry.name,
|
|
@@ -248,9 +268,7 @@ async function walk(
|
|
|
248
268
|
}
|
|
249
269
|
// Siblings in parallel, depth serialized per branch: the platform counts every call as in-flight,
|
|
250
270
|
// So `probe.idle()` already knows the scan is running without the Library declaring a source.
|
|
251
|
-
await Promise.all(
|
|
252
|
-
subdirectories.map((sub) => walk(sub, platform, out, failures, i18n, false, pageExtensions)),
|
|
253
|
-
);
|
|
271
|
+
await Promise.all(subdirectories.map((sub) => walk(sub, run)));
|
|
254
272
|
}
|
|
255
273
|
|
|
256
274
|
/**
|
|
@@ -265,16 +283,31 @@ export async function scanLibrary(
|
|
|
265
283
|
optional: ReadonlySet<string> = new Set(),
|
|
266
284
|
pageExtensions?: readonly string[],
|
|
267
285
|
): Promise<LibraryScan> {
|
|
268
|
-
const files: LibraryFile[] = [];
|
|
269
|
-
const failures: ScanFailure[] = [];
|
|
270
286
|
// Resolved ONCE for the whole walk. `resolveI18n` canonicalizes every declared tag through
|
|
271
287
|
// `Intl.Locale`, and a project with 300 pages would otherwise pay for that per file.
|
|
272
288
|
const { i18n } = resolveI18n(projectState?.projectConfig ?? {});
|
|
289
|
+
const run: ScanRun = {
|
|
290
|
+
claimed: new Set(),
|
|
291
|
+
failures: [],
|
|
292
|
+
i18n,
|
|
293
|
+
out: [],
|
|
294
|
+
pageExtensions,
|
|
295
|
+
platform,
|
|
296
|
+
};
|
|
297
|
+
/* Every root is claimed before any walk descends: `map` runs to the end synchronously, and a walk
|
|
298
|
+
claims its subdirectories only after its own listing has been awaited. A root named twice is
|
|
299
|
+
walked once. */
|
|
273
300
|
await Promise.all(
|
|
274
|
-
dirs.map((dir) =>
|
|
275
|
-
|
|
276
|
-
|
|
301
|
+
dirs.map((dir) => {
|
|
302
|
+
const key = normalizeDir(dir);
|
|
303
|
+
if (run.claimed.has(key)) {
|
|
304
|
+
return Promise.resolve();
|
|
305
|
+
}
|
|
306
|
+
run.claimed.add(key);
|
|
307
|
+
return walk(dir, run, optional.has(dir));
|
|
308
|
+
}),
|
|
277
309
|
);
|
|
310
|
+
const { failures, out: files } = run;
|
|
278
311
|
files.sort((a, b) => a.path.localeCompare(b.path));
|
|
279
312
|
return { failures, files };
|
|
280
313
|
}
|
package/src/commands/context.ts
CHANGED
|
@@ -107,6 +107,7 @@ const EDITOR_KIND_BY_MODE = new Map<string, EditorKind>([
|
|
|
107
107
|
["content-home", "home"],
|
|
108
108
|
["media-home", "home"],
|
|
109
109
|
["publish-home", "home"],
|
|
110
|
+
["settings-home", "home"],
|
|
110
111
|
/* The Media viewer — `media/media-pane.ts`. An image, a video, a font specimen: files the studio
|
|
111
112
|
can SHOW but has no document model for, which is why clicking one used to end in "No format
|
|
112
113
|
class imported for …", advice that means nothing about a PNG. */
|
package/src/editor/shortcuts.ts
CHANGED
|
@@ -1010,6 +1010,9 @@ const SELF_SCROLLING_MODES: ReadonlySet<string> = new Set([
|
|
|
1010
1010
|
"media-home",
|
|
1011
1011
|
"preview",
|
|
1012
1012
|
"publish-home",
|
|
1013
|
+
/* Project Settings' home — `settings/settings-workspace.ts`. Two columns, each scrolling its own
|
|
1014
|
+
list, with nothing to zoom. */
|
|
1015
|
+
"settings-home",
|
|
1013
1016
|
/* The Styles workspace's Variables view — `style/variables-view.ts`. An `overflow-y: auto`
|
|
1014
1017
|
column over the stage; without it here the wheel panned an artboard that does not exist. */
|
|
1015
1018
|
"tokens",
|
package/src/panels/data-grid.ts
CHANGED
|
@@ -23,7 +23,6 @@ import { mountDataActionsSurface } from "../surfaces/data-actions";
|
|
|
23
23
|
import { mountPushPlanSurface } from "../surfaces/push-plan";
|
|
24
24
|
import { dataSurfaceAvailable, pushSchema, testConnection } from "../services/data-service";
|
|
25
25
|
import { openGridSourcePicker } from "../grid/grid-open";
|
|
26
|
-
import { closeSettingsDialog } from "../settings/settings-dialog";
|
|
27
26
|
import type { DataActionsSurfaceHandle, DataActionsView } from "../surfaces/data-actions";
|
|
28
27
|
import type { DialogSurfaceHandle } from "../surfaces/dialog";
|
|
29
28
|
import type { PushPlanSurfaceHandle, PushPlanView } from "../surfaces/push-plan";
|
|
@@ -278,9 +277,6 @@ function paintSectionActions(
|
|
|
278
277
|
host,
|
|
279
278
|
mountDataActionsSurface(host, projectActions(sectionKey, selected), {
|
|
280
279
|
openGrid: () => {
|
|
281
|
-
/* The actions row is drawn in the settings dialog, and a grid opens as a TAB: left up, the
|
|
282
|
-
dialog would cover the very tab the reader asked for (§17.1). */
|
|
283
|
-
closeSettingsDialog();
|
|
284
280
|
void openGridSourcePicker();
|
|
285
281
|
},
|
|
286
282
|
push: () => {
|
|
@@ -15,15 +15,15 @@
|
|
|
15
15
|
* a record joins the menu by declaring the placement and leaves by not declaring it. The submenus
|
|
16
16
|
* are the enumeration of each parent command's own `section` ARGUMENT, read from that argument's
|
|
17
17
|
* own definition site — which is state the command already validates, not a second vocabulary
|
|
18
|
-
* (`studio-ui-guidelines.md` §12.5). Project Settings' submenu is GROUPED as its
|
|
18
|
+
* (`studio-ui-guidelines.md` §12.5). Project Settings' submenu is GROUPED as its own nav is
|
|
19
19
|
* (General, Integrations, Advanced), with a divider where the group changes.
|
|
20
20
|
*
|
|
21
21
|
* **A parent row runs its own command AND owns a submenu.** The APG menu pattern does not describe
|
|
22
22
|
* that and Spectrum's stock submenu forbade it outright; the kit's `jx-menu-item` makes it the rule
|
|
23
23
|
* (`ui.md` §5.1), so the deviation `specs/studio-ui-guidelines.md` §8.4 records is now what the
|
|
24
24
|
* element does rather than what this file hand-rolls around it. Nothing is unreachable by keyboard:
|
|
25
|
-
* Enter runs the row, ArrowRight reaches every child, and every child is also in
|
|
26
|
-
*
|
|
25
|
+
* Enter runs the row, ArrowRight reaches every child, and every child is also in Project Settings'
|
|
26
|
+
* own nav.
|
|
27
27
|
*
|
|
28
28
|
* The menu is the `menu` surface (`surfaces/menu.json`, through `surfaces/menu.ts`). What is left
|
|
29
29
|
* here is the projection — which records, which sections, what each row runs — and where the panel
|
|
@@ -1,26 +1,25 @@
|
|
|
1
1
|
/// <reference lib="dom" />
|
|
2
2
|
/**
|
|
3
3
|
* The Project Settings editor's flow: the grouped section list, the section body, and the Moved
|
|
4
|
-
* footnote, drawn
|
|
4
|
+
* footnote, drawn as the Settings workspace's home (§17.1, §22.9).
|
|
5
5
|
*
|
|
6
6
|
* Two columns. The list is the section registry in its three groups (General, Integrations,
|
|
7
|
-
* Advanced), so an extension's section joins
|
|
8
|
-
*
|
|
7
|
+
* Advanced), so an extension's section joins Settings by registering and leaves by going away; the
|
|
8
|
+
* body is whichever section is current, drawn by that section's own renderer.
|
|
9
9
|
*
|
|
10
10
|
* **This file is the FLOW; the markup is a document.** `surfaces/settings-pane.json` owns the two
|
|
11
11
|
* columns, the lists' ARIA and keyboard, and every value in their style;
|
|
12
12
|
* `surfaces/settings-pane.ts` mounts it (studio-ui-guidelines.md §6, §9.3), and
|
|
13
|
-
* `settings/settings-
|
|
13
|
+
* `settings/settings-workspace.ts` owns the pane it is mounted into. What stays here is the part
|
|
14
14
|
* that is a decision: which sections there are, which one is current, which renderer gets the body,
|
|
15
15
|
* and what a renderer's failure says.
|
|
16
16
|
*
|
|
17
17
|
* **It imports the registry, never the sections.** The section RENDERERS are registered by
|
|
18
18
|
* `settings/settings-document.ts`, which the app loads when it registers its commands.
|
|
19
19
|
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* `settings/
|
|
23
|
-
* `overlay.dialog:settings/editor`.
|
|
20
|
+
* The home carries the region its host hands in, `pane.<id>/settings`, and the entry rows and entry
|
|
21
|
+
* editor of a map-layout section are stamped by `settings/contributed-section.ts` under it, as
|
|
22
|
+
* `pane.<id>/settings/entry:<key>` and `pane.<id>/settings/editor`.
|
|
24
23
|
*
|
|
25
24
|
* @docs studio/projects/settings
|
|
26
25
|
*/
|
|
@@ -41,6 +40,9 @@ import type { WorkspaceId } from "../workspace/workspace-ids";
|
|
|
41
40
|
import type { StylesView } from "../style/project-styles";
|
|
42
41
|
import type { PublishTab } from "../publish/publish-ids";
|
|
43
42
|
|
|
43
|
+
/** The home's heading: the workspace's own title, so the two cannot drift apart. */
|
|
44
|
+
const HEADING = workspaceInfo("settings").title;
|
|
45
|
+
|
|
44
46
|
/** What the body says when the section that is current is registered by nobody. */
|
|
45
47
|
const NO_SECTIONS = "No settings sections.";
|
|
46
48
|
|
|
@@ -80,9 +82,15 @@ const MOVED: readonly {
|
|
|
80
82
|
|
|
81
83
|
/** What the mounted editor is told to do by things only its host can do. */
|
|
82
84
|
export interface SettingsPaneOptions {
|
|
85
|
+
/** The region the home is stamped with: `pane.<id>/settings`. */
|
|
86
|
+
region: string;
|
|
87
|
+
/** Where the Back control goes, as its text says it ("Back to Design"). Read on every draw. */
|
|
88
|
+
backLabel: () => string;
|
|
89
|
+
/** The Back control: leave Settings for the workspace it was opened from. */
|
|
90
|
+
back: () => void;
|
|
83
91
|
/**
|
|
84
|
-
* A Moved footnote's button:
|
|
85
|
-
*
|
|
92
|
+
* A Moved footnote's button: go to the named workspace, and to the named Inspector tab or Styles
|
|
93
|
+
* view there when the row names one.
|
|
86
94
|
*/
|
|
87
95
|
openMoved: (
|
|
88
96
|
workspace: WorkspaceId,
|
|
@@ -96,15 +104,10 @@ export interface SettingsPaneOptions {
|
|
|
96
104
|
export interface SettingsPaneMount {
|
|
97
105
|
/** Settles once the columns are mounted and the current section has been drawn once. */
|
|
98
106
|
ready: Promise<void>;
|
|
99
|
-
/** Put the keyboard on the current section's row. */
|
|
100
|
-
focusActive: () => void
|
|
101
|
-
/**
|
|
102
|
-
|
|
103
|
-
* section registering or a project closing in the meantime is caught up on by {@link resume}.
|
|
104
|
-
*/
|
|
105
|
-
suspend: () => void;
|
|
106
|
-
/** Follow the registry again and redraw the current section: the dialog is being shown again. */
|
|
107
|
-
resume: () => void;
|
|
107
|
+
/** Put the keyboard on the current section's row. Settles once it has moved. */
|
|
108
|
+
focusActive: () => Promise<void>;
|
|
109
|
+
/** Whether the home still stands in the stage it was mounted into. */
|
|
110
|
+
attached: () => boolean;
|
|
108
111
|
/** Take the editor down and unsubscribe. Idempotent. */
|
|
109
112
|
dispose: () => void;
|
|
110
113
|
}
|
|
@@ -122,14 +125,16 @@ function movedRow(value: string): (typeof MOVED)[number] | null {
|
|
|
122
125
|
/**
|
|
123
126
|
* Everything the document draws, decided here so the document decides nothing.
|
|
124
127
|
*
|
|
128
|
+
* @param {SettingsPaneOptions} options The host's region and Back control
|
|
125
129
|
* @param {string} failure The current section's failure sentence, or the empty string
|
|
126
130
|
* @returns {SettingsPaneValues}
|
|
127
131
|
*/
|
|
128
|
-
function values(failure: string): SettingsPaneValues {
|
|
132
|
+
function values(options: SettingsPaneOptions, failure: string): SettingsPaneValues {
|
|
129
133
|
const active = settingsDocumentSection();
|
|
130
134
|
const grouped = groupedSettingsSections();
|
|
131
135
|
return {
|
|
132
136
|
active,
|
|
137
|
+
backLabel: options.backLabel(),
|
|
133
138
|
activeTabId: tabIdFor(active),
|
|
134
139
|
empty: NO_SECTIONS,
|
|
135
140
|
failure,
|
|
@@ -146,6 +151,7 @@ function values(failure: string): SettingsPaneValues {
|
|
|
146
151
|
titleId: `settings-group-${group.id}`,
|
|
147
152
|
})),
|
|
148
153
|
hasSection: settingsSection(active) !== undefined,
|
|
154
|
+
heading: HEADING,
|
|
149
155
|
// The section's own sentence when it has one, its group's otherwise.
|
|
150
156
|
intro:
|
|
151
157
|
settingsSection(active)?.intro ||
|
|
@@ -161,11 +167,12 @@ function values(failure: string): SettingsPaneValues {
|
|
|
161
167
|
workspace: row.workspace,
|
|
162
168
|
};
|
|
163
169
|
}),
|
|
170
|
+
region: options.region,
|
|
164
171
|
};
|
|
165
172
|
}
|
|
166
173
|
|
|
167
174
|
/**
|
|
168
|
-
* Mount the editor into `host`,
|
|
175
|
+
* Mount the editor into `host`, a pane's stage, and keep it current until it is disposed.
|
|
169
176
|
*
|
|
170
177
|
* It subscribes to the section registry and to the chosen section, so an extension's section
|
|
171
178
|
* appearing a tick later redraws the nav, and a deep link to an entry inside the section already on
|
|
@@ -198,7 +205,10 @@ export function mountSettingsPane(
|
|
|
198
205
|
/** Counts renders, so a late rejection from a superseded one cannot overwrite a newer answer. */
|
|
199
206
|
let generation = 0;
|
|
200
207
|
|
|
201
|
-
const surface: SettingsPaneHandle = mountSettingsPaneSurface(host, values(failure), {
|
|
208
|
+
const surface: SettingsPaneHandle = mountSettingsPaneSurface(host, values(options, failure), {
|
|
209
|
+
back: () => {
|
|
210
|
+
options.back();
|
|
211
|
+
},
|
|
202
212
|
openMoved: (workspace: string) => {
|
|
203
213
|
const target = movedRow(workspace);
|
|
204
214
|
if (target !== null) {
|
|
@@ -217,7 +227,7 @@ export function mountSettingsPane(
|
|
|
217
227
|
}
|
|
218
228
|
const label = settingsSection(key)?.label ?? key;
|
|
219
229
|
failure = `Could not show ${label}: ${reason}`;
|
|
220
|
-
surface.update(values(failure));
|
|
230
|
+
surface.update(values(options, failure));
|
|
221
231
|
}
|
|
222
232
|
|
|
223
233
|
/**
|
|
@@ -237,7 +247,7 @@ export function mountSettingsPane(
|
|
|
237
247
|
rendered = null;
|
|
238
248
|
}
|
|
239
249
|
if (!island || (!force && rendered === active)) {
|
|
240
|
-
surface.update(values(failure));
|
|
250
|
+
surface.update(values(options, failure));
|
|
241
251
|
return;
|
|
242
252
|
}
|
|
243
253
|
/* Read BEFORE the write: whether the section is changing is what decides the host, and
|
|
@@ -245,10 +255,10 @@ export function mountSettingsPane(
|
|
|
245
255
|
const changed = rendered !== active;
|
|
246
256
|
rendered = active;
|
|
247
257
|
failure = "";
|
|
248
|
-
surface.update(values(failure));
|
|
258
|
+
surface.update(values(options, failure));
|
|
249
259
|
const section = settingsSection(active);
|
|
250
260
|
if (!section) {
|
|
251
|
-
/* Every section unregistered at once: a project closing while
|
|
261
|
+
/* Every section unregistered at once: a project closing while Settings is on screen. The
|
|
252
262
|
document says so itself (`hasSection`), and the island is emptied so a renderer's leftovers
|
|
253
263
|
do not sit under the sentence explaining that there is nothing to draw. */
|
|
254
264
|
sectionHost = null;
|
|
@@ -276,12 +286,9 @@ export function mountSettingsPane(
|
|
|
276
286
|
}
|
|
277
287
|
}
|
|
278
288
|
|
|
279
|
-
const
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
});
|
|
283
|
-
/** The registry subscription, or null while the dialog is closed. */
|
|
284
|
-
let off: (() => void) | null = follow();
|
|
289
|
+
const off = onSettingsDocumentChanged(() => {
|
|
290
|
+
draw(true);
|
|
291
|
+
});
|
|
285
292
|
/* The island does not exist until the mount lands, so the first pass that reaches a section
|
|
286
293
|
renderer is this one. */
|
|
287
294
|
const ready = surface.ready.then(() => {
|
|
@@ -289,37 +296,16 @@ export function mountSettingsPane(
|
|
|
289
296
|
});
|
|
290
297
|
|
|
291
298
|
return {
|
|
299
|
+
attached: () => surface.attached(),
|
|
292
300
|
dispose() {
|
|
293
301
|
if (disposed) {
|
|
294
302
|
return;
|
|
295
303
|
}
|
|
296
304
|
disposed = true;
|
|
297
|
-
off
|
|
298
|
-
off = null;
|
|
305
|
+
off();
|
|
299
306
|
surface.dispose();
|
|
300
307
|
},
|
|
301
|
-
focusActive: () =>
|
|
302
|
-
surface.focusActive();
|
|
303
|
-
},
|
|
308
|
+
focusActive: () => surface.focusActive(),
|
|
304
309
|
ready,
|
|
305
|
-
resume() {
|
|
306
|
-
if (disposed || off !== null) {
|
|
307
|
-
return;
|
|
308
|
-
}
|
|
309
|
-
off = follow();
|
|
310
|
-
/* Redrawn rather than trusted: anything may have moved while it was closed (a section
|
|
311
|
-
registered, the project closed or changed, the configuration edited in Styles). Into a
|
|
312
|
-
FRESH host, as every open drew into when each one built its own frame: several sections
|
|
313
|
-
keep their form state (a half-typed entry, a failed save's error) in a WeakMap keyed on
|
|
314
|
-
the host they were given, and an open is a new visit, so none of that may come back from
|
|
315
|
-
the last one, or from another project's. Only a redraw WHILE open keeps the host, which is
|
|
316
|
-
what keeps the caret in the field being typed in. */
|
|
317
|
-
sectionHost = null;
|
|
318
|
-
draw(true);
|
|
319
|
-
},
|
|
320
|
-
suspend() {
|
|
321
|
-
off?.();
|
|
322
|
-
off = null;
|
|
323
|
-
},
|
|
324
310
|
};
|
|
325
311
|
}
|
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
|
@@ -37,6 +37,7 @@ import type {
|
|
|
37
37
|
FsEvent,
|
|
38
38
|
FsResyncReason,
|
|
39
39
|
GitBranchesResult,
|
|
40
|
+
GitCommitBranchResult,
|
|
40
41
|
GitLogEntry,
|
|
41
42
|
GitStatusResult,
|
|
42
43
|
ImportProgressEvent,
|
|
@@ -220,6 +221,7 @@ interface StructuredBody extends ErrorBody {
|
|
|
220
221
|
actions?: JsonValue;
|
|
221
222
|
heading?: JsonValue;
|
|
222
223
|
retry?: JsonValue;
|
|
224
|
+
conflicts?: JsonValue;
|
|
223
225
|
}
|
|
224
226
|
|
|
225
227
|
/** What {@link platformError} carries on the Error it returns: the HTTP status, plus what it found. */
|
|
@@ -271,6 +273,10 @@ async function platformError(res: Response, fallback: string): Promise<Error> {
|
|
|
271
273
|
if (retry !== undefined) {
|
|
272
274
|
details.retry = retry;
|
|
273
275
|
}
|
|
276
|
+
const conflicts = body?.conflicts;
|
|
277
|
+
if (Array.isArray(conflicts)) {
|
|
278
|
+
details.conflicts = conflicts.filter((path) => isString(path));
|
|
279
|
+
}
|
|
274
280
|
return Object.assign(new Error(problemDetail(body) ?? fallback), details);
|
|
275
281
|
}
|
|
276
282
|
|
|
@@ -1590,6 +1596,14 @@ export function createCloudPlatform(project: CloudProject | null): StudioPlatfor
|
|
|
1590
1596
|
await okJson(await postJson("/git/create-branch", { name }), "Failed to create branch");
|
|
1591
1597
|
},
|
|
1592
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
|
+
|
|
1593
1607
|
async gitDiff(path?: string) {
|
|
1594
1608
|
const data = await okJson<{ diff: string }>(
|
|
1595
1609
|
await api(`/git/diff?path=${encodeURIComponent(path ?? "")}`),
|