dsh-git-branch-manage 1.0.4 → 1.0.5
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/lib/client.js +480 -19
- package/lib/git.d.ts +25 -3
- package/lib/git.js +32 -6
- package/lib/index.d.ts +16 -0
- package/lib/index.js +28 -1
- package/lib/normalize.d.ts +28 -3
- package/lib/normalize.js +38 -3
- package/lib/routes.d.ts +4 -0
- package/lib/routes.js +16 -4
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -44,6 +44,36 @@ window.__ModuleLoader__.load({
|
|
|
44
44
|
if (/(^|\/)[^/]+\.lock($|\/)/.test(name)) return "illegal";
|
|
45
45
|
return null;
|
|
46
46
|
}
|
|
47
|
+
/**
|
|
48
|
+
* Shipped default for the cn-accelerate mirror prefix (the design doc's
|
|
49
|
+
* `cnAccelerateUrl` default and the popover's「恢复默认」target).
|
|
50
|
+
*/
|
|
51
|
+
const ACCEL_DEFAULT_URL = "https://gh-proxy.com/";
|
|
52
|
+
/**
|
|
53
|
+
* Pre-flight check of the cn-accelerate prefix against what the rewrite can
|
|
54
|
+
* actually use: non-empty after trim, `http://` or `https://` scheme, and no
|
|
55
|
+
* whitespace anywhere (a space would break the `url.<base>.insteadOf=<target>`
|
|
56
|
+
* git config value and smuggle a second argument).
|
|
57
|
+
* @param value - raw draft text.
|
|
58
|
+
* @returns the issue kind, or undefined when the value is acceptable.
|
|
59
|
+
*/
|
|
60
|
+
function accelerateUrlError(value) {
|
|
61
|
+
const trimmed = value.trim();
|
|
62
|
+
if (trimmed === "") return "empty";
|
|
63
|
+
if (/\s/.test(trimmed)) return "invalid";
|
|
64
|
+
if (!trimmed.startsWith("http://") && !trimmed.startsWith("https://")) return "invalid";
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Normalize an accepted accelerate URL: trim, and append the trailing `/`
|
|
68
|
+
* the prefix concatenation requires (`https://gh-proxy.com/` + `o/r.git`
|
|
69
|
+
* must stay one valid URL). Callers pass the result to `url.<base>.insteadOf`.
|
|
70
|
+
* @param value - an accepted (non-empty, http(s), whitespace-free) URL.
|
|
71
|
+
* @returns the prefix ending in `/`.
|
|
72
|
+
*/
|
|
73
|
+
function normalizeAccelerateUrl(value) {
|
|
74
|
+
const trimmed = value.trim();
|
|
75
|
+
return trimmed.endsWith("/") ? trimmed : `${trimmed}/`;
|
|
76
|
+
}
|
|
47
77
|
//#endregion
|
|
48
78
|
//#region src/wire.ts
|
|
49
79
|
/**
|
|
@@ -3401,6 +3431,151 @@ window.__ModuleLoader__.load({
|
|
|
3401
3431
|
}
|
|
3402
3432
|
};
|
|
3403
3433
|
//#endregion
|
|
3434
|
+
//#region src/client/accel-form.ts
|
|
3435
|
+
/** The fields this form edits. */
|
|
3436
|
+
const ACCEL_ENABLED_FIELD = "cnAccelerate";
|
|
3437
|
+
const ACCEL_URL_FIELD = "cnAccelerateUrl";
|
|
3438
|
+
/**
|
|
3439
|
+
* Stages the cn-accelerate edits over the `dsh-git-branch` Host entry.
|
|
3440
|
+
*
|
|
3441
|
+
* Publishes through a snapshot store like {@link CardForm}: the consumer
|
|
3442
|
+
* reads through a snapshot selector while the scope and the local drafts
|
|
3443
|
+
* change underneath; every projection is rebuilt from the two together.
|
|
3444
|
+
*/
|
|
3445
|
+
var AccelForm = class {
|
|
3446
|
+
scope;
|
|
3447
|
+
snapshotValue;
|
|
3448
|
+
listeners = /* @__PURE__ */ new Set();
|
|
3449
|
+
enabledDraft;
|
|
3450
|
+
urlDraft;
|
|
3451
|
+
saving = false;
|
|
3452
|
+
failed = false;
|
|
3453
|
+
/**
|
|
3454
|
+
* @param scope - the bound configuration form for the `dsh-git-branch` Host entry.
|
|
3455
|
+
*/
|
|
3456
|
+
constructor(scope) {
|
|
3457
|
+
this.scope = scope;
|
|
3458
|
+
this.snapshotValue = this.project();
|
|
3459
|
+
scope.subscribe(() => {
|
|
3460
|
+
this.publish();
|
|
3461
|
+
});
|
|
3462
|
+
}
|
|
3463
|
+
/** @returns the store a consumer reads through its bound selector. */
|
|
3464
|
+
bind() {
|
|
3465
|
+
return {
|
|
3466
|
+
getSnapshot: () => this.snapshotValue,
|
|
3467
|
+
subscribe: (listener) => {
|
|
3468
|
+
this.listeners.add(listener);
|
|
3469
|
+
return () => {
|
|
3470
|
+
this.listeners.delete(listener);
|
|
3471
|
+
};
|
|
3472
|
+
},
|
|
3473
|
+
set: (next) => {
|
|
3474
|
+
this.store(next);
|
|
3475
|
+
}
|
|
3476
|
+
};
|
|
3477
|
+
}
|
|
3478
|
+
/** @returns the stage, restore, save, and discard actions bound to this form. */
|
|
3479
|
+
actions() {
|
|
3480
|
+
return {
|
|
3481
|
+
setAccelEnabled: (value) => {
|
|
3482
|
+
this.enabledDraft = value;
|
|
3483
|
+
this.failed = false;
|
|
3484
|
+
this.publish();
|
|
3485
|
+
},
|
|
3486
|
+
editAccelUrl: (text) => {
|
|
3487
|
+
this.urlDraft = text;
|
|
3488
|
+
this.failed = false;
|
|
3489
|
+
this.publish();
|
|
3490
|
+
},
|
|
3491
|
+
restoreAccelDefault: () => {
|
|
3492
|
+
this.urlDraft = ACCEL_DEFAULT_URL;
|
|
3493
|
+
this.failed = false;
|
|
3494
|
+
this.publish();
|
|
3495
|
+
},
|
|
3496
|
+
saveAccel: () => this.save(),
|
|
3497
|
+
discardAccel: () => {
|
|
3498
|
+
if (this.enabledDraft === void 0 && this.urlDraft === void 0 && !this.failed) return;
|
|
3499
|
+
this.enabledDraft = void 0;
|
|
3500
|
+
this.urlDraft = void 0;
|
|
3501
|
+
this.failed = false;
|
|
3502
|
+
this.publish();
|
|
3503
|
+
}
|
|
3504
|
+
};
|
|
3505
|
+
}
|
|
3506
|
+
/** The URL as the user sees it: the live draft, else the stored value over
|
|
3507
|
+
* the shipped default. */
|
|
3508
|
+
effectiveUrlText() {
|
|
3509
|
+
const value = this.scope.getSnapshot().value?.[ACCEL_URL_FIELD];
|
|
3510
|
+
return this.urlDraft ?? value ?? "https://gh-proxy.com/";
|
|
3511
|
+
}
|
|
3512
|
+
/**
|
|
3513
|
+
* Write the switch and the normalized URL TOGETHER, then re-seed from what
|
|
3514
|
+
* the Host accepted. The URL is validated first — an unusable draft blocks
|
|
3515
|
+
* the save (and the surfaces disable the button on the same error), so the
|
|
3516
|
+
* switch can never land alone against a mirror the user has not confirmed.
|
|
3517
|
+
* A save that did not land keeps its drafts so the user can correct them.
|
|
3518
|
+
*/
|
|
3519
|
+
async save() {
|
|
3520
|
+
if (this.enabledDraft === void 0 && this.urlDraft === void 0 || this.saving) return;
|
|
3521
|
+
const urlText = this.effectiveUrlText();
|
|
3522
|
+
if (accelerateUrlError(urlText) !== void 0) return;
|
|
3523
|
+
const intendedEnabled = this.enabledDraft ?? this.scope.getSnapshot().value?.["cnAccelerate"] ?? false;
|
|
3524
|
+
const intendedUrl = normalizeAccelerateUrl(urlText);
|
|
3525
|
+
this.saving = true;
|
|
3526
|
+
this.failed = false;
|
|
3527
|
+
this.publish();
|
|
3528
|
+
let landed = true;
|
|
3529
|
+
try {
|
|
3530
|
+
await this.scope.set(ACCEL_ENABLED_FIELD, intendedEnabled);
|
|
3531
|
+
await this.scope.set(ACCEL_URL_FIELD, intendedUrl);
|
|
3532
|
+
const user = this.userLayer();
|
|
3533
|
+
if (user?.["cnAccelerate"] !== intendedEnabled || user?.["cnAccelerateUrl"] !== intendedUrl) landed = false;
|
|
3534
|
+
} catch (_settingsWriteFailure) {
|
|
3535
|
+
landed = false;
|
|
3536
|
+
}
|
|
3537
|
+
if (landed) {
|
|
3538
|
+
this.enabledDraft = void 0;
|
|
3539
|
+
this.urlDraft = void 0;
|
|
3540
|
+
}
|
|
3541
|
+
this.saving = false;
|
|
3542
|
+
this.failed = !landed;
|
|
3543
|
+
this.publish();
|
|
3544
|
+
}
|
|
3545
|
+
/** The raw user layer narrowed to a record; the wire answer is `unknown`. */
|
|
3546
|
+
userLayer() {
|
|
3547
|
+
const user = this.scope.getSnapshot().user;
|
|
3548
|
+
return typeof user === "object" && user !== null ? user : void 0;
|
|
3549
|
+
}
|
|
3550
|
+
project() {
|
|
3551
|
+
const snapshot = this.scope.getSnapshot();
|
|
3552
|
+
const storedEnabled = snapshot.value?.["cnAccelerate"] ?? false;
|
|
3553
|
+
const storedUrl = snapshot.value?.["cnAccelerateUrl"] ?? "https://gh-proxy.com/";
|
|
3554
|
+
const urlText = this.effectiveUrlText();
|
|
3555
|
+
const enabledDirty = this.enabledDraft !== void 0 && this.enabledDraft !== storedEnabled;
|
|
3556
|
+
const urlDirty = this.urlDraft !== void 0 && this.urlDraft !== storedUrl;
|
|
3557
|
+
return {
|
|
3558
|
+
available: snapshot.status === "ready",
|
|
3559
|
+
writable: snapshot.writable,
|
|
3560
|
+
enabled: this.enabledDraft ?? storedEnabled,
|
|
3561
|
+
urlText,
|
|
3562
|
+
dirty: enabledDirty || urlDirty,
|
|
3563
|
+
saving: this.saving,
|
|
3564
|
+
failed: this.failed,
|
|
3565
|
+
urlError: accelerateUrlError(urlText) ?? null
|
|
3566
|
+
};
|
|
3567
|
+
}
|
|
3568
|
+
/** Replace the snapshot reference and notify, only when the fact moved. */
|
|
3569
|
+
store(next) {
|
|
3570
|
+
if (next === this.snapshotValue) return;
|
|
3571
|
+
this.snapshotValue = next;
|
|
3572
|
+
for (const listener of this.listeners) listener();
|
|
3573
|
+
}
|
|
3574
|
+
publish() {
|
|
3575
|
+
this.store(this.project());
|
|
3576
|
+
}
|
|
3577
|
+
};
|
|
3578
|
+
//#endregion
|
|
3404
3579
|
//#region src/client/git-panel-data.ts
|
|
3405
3580
|
/**
|
|
3406
3581
|
* Classify raw %D decoration segments: `HEAD -> main` (and bare `HEAD`) is
|
|
@@ -3565,7 +3740,7 @@ window.__ModuleLoader__.load({
|
|
|
3565
3740
|
}
|
|
3566
3741
|
//#endregion
|
|
3567
3742
|
//#region \0git-worktree-css:/home/runner/work/dsh-git-branch/dsh-git-branch/src/client/GitHistoryPanel.module.css?inline
|
|
3568
|
-
const css$2 = "/* Git panel (sidebar global panel). Three stacked sections over the main\n * column: uncommitted changes + commit box, branches, commit history. Host\n * tokens only; the few status colors fall back to the same literals the\n * settings card uses. */\n\n.page_427795137 {\n display: flex;\n flex-direction: column;\n min-height: 0;\n height: 100%;\n}\n\n.topbar_427795137 {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 10px 16px 8px;\n}\n\n.back_427795137 {\n flex: none;\n display: inline-flex;\n align-items: center;\n gap: 6px;\n border: 0;\n background: none;\n cursor: pointer;\n font-size: 13px;\n line-height: 1.5;\n color: var(--dsw-alias-label-secondary);\n padding: 4px 8px;\n border-radius: 8px;\n}\n\n.back_427795137:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n color: var(--dsw-alias-label-primary);\n}\n\n.title_427795137 {\n margin: 0;\n font-size: 16px;\n font-weight: 600;\n color: var(--dsw-alias-label-primary);\n}\n\n.repoPill_427795137 {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n min-width: 0;\n font-size: 12px;\n line-height: 1.4;\n color: var(--dsw-alias-label-secondary);\n border: 0.5px solid var(--dsw-alias-border-l4);\n border-radius: 999px;\n padding: 3px 10px;\n white-space: nowrap;\n overflow: hidden;\n}\n\n.repoName_427795137 {\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.branchName_427795137 {\n color: var(--dsw-alias-label-primary);\n font-weight: 500;\n}\n\n.track_427795137 {\n font-variant-numeric: tabular-nums;\n}\n\n.topbarActions_427795137 {\n margin-left: auto;\n display: flex;\n align-items: center;\n gap: 6px;\n flex: none;\n}\n\n.refreshBtn_427795137 {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n border: 0;\n border-radius: 8px;\n background: none;\n color: var(--dsw-alias-label-secondary);\n cursor: pointer;\n}\n\n.refreshBtn_427795137:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n color: var(--dsw-alias-label-primary);\n}\n\n.refreshBtn_427795137[disabled] {\n opacity: 0.5;\n cursor: default;\n}\n\n.scroll_427795137 {\n flex: 1;\n min-height: 0;\n overflow-y: auto;\n padding: 0 16px 16px;\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n\n/* The page column below the topbar is a definite-height flex column and the\n * three cards split it 2:1:2 (uncommitted : branches : history) — five\n * shares, the topbar and gaps taking their natural height first. Each body\n * scrolls INSIDE its share — min-height: 0 is what lets a flex child shrink\n * below its content (the old natural-height model painted overflowing rows\n * over the sections below). A floor keeps a very short panel from\n * collapsing a card to its bare head; past the floor the column itself\n * scrolls. */\n.section_427795137 {\n border: 0.5px solid var(--dsw-alias-border-l2);\n border-radius: 10px;\n background: var(--dsw-alias-bg-layer-2);\n display: flex;\n flex-direction: column;\n min-height: 0;\n}\n\n.sectionFill_427795137 {\n flex: 1 1 0;\n min-height: 84px;\n}\n\n/* The three flex-grow overrides of the 2:1:2 split. */\n.sectionChanges_427795137 {\n flex-grow: 2;\n}\n\n.sectionBranches_427795137 {\n flex-grow: 1;\n}\n\n.sectionHistory_427795137 {\n flex-grow: 2;\n}\n\n.sectionHead_427795137 {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 12px;\n border-bottom: 0.5px solid var(--dsw-alias-border-l2);\n}\n\n.sectionTitle_427795137 {\n margin: 0;\n font-size: 13px;\n font-weight: 600;\n color: var(--dsw-alias-label-primary);\n}\n\n.countBadge_427795137 {\n font-size: 10px;\n line-height: 1;\n border-radius: 999px;\n padding: 3px 7px;\n background: var(--dsw-alias-bg-layer-3);\n color: var(--dsw-alias-label-secondary);\n font-variant-numeric: tabular-nums;\n white-space: nowrap;\n}\n\n.sectionTools_427795137 {\n margin-left: auto;\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n.sectionBody_427795137 {\n padding: 10px 12px;\n display: flex;\n flex-direction: column;\n gap: 8px;\n flex: 1 1 auto;\n min-height: 0;\n}\n\n/* The branch and history bodies scroll as a whole inside their share. */\n.sectionBodyScroll_427795137 {\n overflow-y: auto;\n overscroll-behavior: contain;\n}\n\n/* Inside the uncommitted card only the file lists scroll; the commit box\n * below stays pinned however long the change tree is. */\n.changeList_427795137 {\n display: flex;\n flex-direction: column;\n gap: 8px;\n flex: 1 1 auto;\n min-height: 0;\n overflow-y: auto;\n overscroll-behavior: contain;\n}\n\n/* The 全部展开/全部折叠 text chips in the branch card head. */\n.treeToggle_427795137 {\n background: none;\n border: 0;\n padding: 2px 5px;\n font: inherit;\n font-size: 11.5px;\n line-height: 1.4;\n color: var(--dsw-alias-label-secondary);\n cursor: pointer;\n border-radius: 5px;\n white-space: nowrap;\n}\n\n.treeToggle_427795137:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n color: var(--dsw-alias-brand-primary);\n}\n\n/* Small action chip used across the section heads and the commit row. */\n.act_427795137 {\n display: inline-flex;\n align-items: center;\n gap: 5px;\n font: inherit;\n font-size: 12px;\n line-height: 1.4;\n cursor: pointer;\n background: var(--dsw-alias-bg-layer-3);\n color: var(--dsw-alias-label-primary);\n border: 0.5px solid var(--dsw-alias-border-l4);\n border-radius: 7px;\n padding: 4px 10px;\n white-space: nowrap;\n}\n\n.act_427795137:hover:not([disabled]) {\n border-color: var(--dsw-alias-brand-primary);\n}\n\n.act_427795137[disabled] {\n opacity: 0.45;\n cursor: default;\n}\n\n.actPrimary_427795137 {\n /* Hardcoded blue by request — the brand token does not resolve reliably\n * inside the better-sidebar panel. */\n background: #4c6bf5;\n border-color: transparent;\n color: #fff;\n}\n\n.actPrimary_427795137:hover:not([disabled]) {\n border-color: transparent;\n filter: brightness(1.06);\n}\n\n/* Uncommitted changes */\n\n.groupTitleRow_427795137 {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 12px;\n color: var(--dsw-alias-label-secondary);\n}\n\n.linkBtn_427795137 {\n margin-left: auto;\n background: none;\n border: 0;\n padding: 2px 4px;\n font: inherit;\n font-size: 12px;\n color: var(--dsw-alias-brand-primary);\n cursor: pointer;\n border-radius: 5px;\n}\n\n.linkBtn_427795137:hover:not([disabled]) {\n text-decoration: underline;\n}\n\n.linkBtn_427795137[disabled] {\n opacity: 0.5;\n cursor: default;\n}\n\n.changeRow_427795137 {\n display: grid;\n grid-template-columns: 16px 18px minmax(0, 1fr) auto;\n gap: 8px;\n align-items: center;\n padding: 3px 6px;\n border-radius: 6px;\n font-size: 12px;\n color: var(--dsw-alias-label-primary);\n}\n\n.changeRow_427795137:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.changeCheck_427795137 {\n width: 14px;\n height: 14px;\n accent-color: var(--dsw-alias-brand-primary);\n cursor: pointer;\n margin: 0;\n}\n\n.statusBadge_427795137 {\n width: 16px;\n height: 16px;\n border-radius: 4px;\n font-size: 10px;\n line-height: 1;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n font-weight: 700;\n color: #fff;\n box-sizing: border-box;\n}\n\n.statusA_427795137 { background: var(--dsw-alias-status-success, #218358); }\n.statusM_427795137 { background: var(--dsw-alias-label-warning, #b07000); }\n.statusD_427795137 { background: var(--dsw-alias-status-danger, #d5304f); }\n.statusR_427795137 { background: #8350d8; }\n.statusC_427795137 { background: #8350d8; }\n.statusT_427795137 { background: #5b7a9d; }\n.statusQ_427795137 { background: var(--dsw-alias-label-dimmed, #9aa0aa); }\n.statusU_427795137 { background: #d5502e; }\n\n.changePath_427795137 {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-family: ui-monospace, Consolas, monospace;\n font-size: 11.5px;\n direction: rtl;\n text-align: left;\n}\n\n.stageHint_427795137 {\n color: var(--dsw-alias-label-tertiary);\n font-size: 11px;\n flex: none;\n}\n\n.commitBox_427795137 {\n display: flex;\n flex-direction: column;\n gap: 8px;\n margin-top: 4px;\n flex: none;\n}\n\n.commitInput_427795137 {\n width: 100%;\n box-sizing: border-box;\n resize: vertical;\n min-height: 52px;\n font: inherit;\n font-size: 12.5px;\n line-height: 1.5;\n border: 0.5px solid var(--dsw-alias-border-l4);\n border-radius: 8px;\n padding: 7px 10px;\n background: var(--dsw-alias-bg-layer-3);\n color: var(--dsw-alias-label-primary);\n outline: none;\n}\n\n.commitInput_427795137:focus {\n border-color: var(--dsw-alias-brand-primary);\n}\n\n.commitActions_427795137 {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.commitHint_427795137 {\n color: var(--dsw-alias-label-tertiary);\n font-size: 11px;\n min-height: 16px;\n}\n\n.split_427795137 {\n margin-left: auto;\n display: flex;\n position: relative;\n}\n\n.splitMain_427795137 {\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n padding: 6px 16px;\n font-size: 13px;\n}\n\n.splitCaret_427795137 {\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n margin-left: 1px;\n padding: 6px 6px;\n}\n\n.menu_427795137 {\n position: absolute;\n right: 0;\n top: calc(100% + 4px);\n z-index: 40;\n width: max-content;\n min-width: 0;\n background: var(--dsw-alias-surface-raised, var(--dsw-alias-bg-layer-3));\n border: 0.5px solid var(--dsw-alias-border-l4);\n border-radius: 8px;\n box-shadow: var(--dsw-shadow-lv3);\n backdrop-filter: var(--dsw-menu-backdrop-filter);\n padding: 3px;\n display: flex;\n flex-direction: column;\n}\n\n.menuItem_427795137 {\n display: block;\n width: 100%;\n text-align: left;\n background: none;\n border: 0;\n color: var(--dsw-alias-label-primary);\n font: inherit;\n font-size: 12px;\n line-height: 1.4;\n padding: 5px 10px;\n border-radius: 5px;\n cursor: pointer;\n white-space: nowrap;\n}\n\n.menuItem_427795137:hover:not([disabled]) {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.menuItem_427795137[disabled] {\n opacity: 0.5;\n cursor: default;\n}\n\n.menuSub_427795137 {\n color: var(--dsw-alias-label-tertiary);\n font-size: 11px;\n margin-top: 1px;\n}\n\n/* Branch section — two IDEA-style trees: 本地 → branches, 远程 → per-remote → branches */\n\n.treeHead_427795137 {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 4px 6px;\n border-radius: 6px;\n cursor: pointer;\n font-size: 12px;\n font-weight: 600;\n color: var(--dsw-alias-label-secondary);\n user-select: none;\n}\n\n.treeHead_427795137:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n color: var(--dsw-alias-label-primary);\n}\n\n.treeChevron_427795137 {\n display: inline-flex;\n align-items: center;\n color: var(--dsw-alias-label-tertiary);\n}\n\n.treeChildren_427795137 {\n display: flex;\n flex-direction: column;\n margin-left: 12px;\n}\n\n.treeHeadNest_427795137 {\n font-weight: 400;\n}\n\n.treeNest_427795137 {\n margin-left: 12px;\n}\n\n.branchRow_427795137 {\n display: grid;\n grid-template-columns: 20px minmax(0, 1fr) auto;\n gap: 8px;\n align-items: center;\n padding: 5px 8px;\n border-radius: 7px;\n /* The action menu opens on RIGHT-CLICK (Enter/Space for keyboard) — an\n * arrow, not the pointing hand a left-click affordance would promise. */\n cursor: default;\n font-size: 12.5px;\n color: var(--dsw-alias-label-primary);\n}\n\n.branchRow_427795137:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n/* Whole-row highlight for the current branch: the ✓ alone read as too\n * quiet inside the tree. A translucent brand-blue wash plus the inset left\n * accent and a colored name survives both themes; #4c6bf5 is the same\n * hardcoded blue actPrimary uses (the brand token does not resolve\n * reliably inside the better-sidebar panel). */\n.branchRowCurrent_427795137 {\n background: rgba(76, 107, 245, 0.16);\n box-shadow: inset 2px 0 0 #4c6bf5;\n cursor: default;\n}\n\n.branchRowCurrent_427795137:hover {\n background: rgba(76, 107, 245, 0.16);\n}\n\n.branchRowCurrent_427795137 .branchName_427795137 {\n color: #4c6bf5;\n font-weight: 600;\n}\n\n.branchMark_427795137 {\n color: var(--dsw-alias-brand-primary);\n display: inline-flex;\n align-items: center;\n justify-content: center;\n}\n\n.branchName_427795137 {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-weight: 500;\n}\n\n.branchKind_427795137 {\n color: var(--dsw-alias-label-tertiary);\n font-size: 10px;\n margin-left: 6px;\n}\n\n.branchTrack_427795137 {\n color: var(--dsw-alias-label-tertiary);\n font-size: 11px;\n font-variant-numeric: tabular-nums;\n flex: none;\n}\n\n/* The branch action menu is portaled; fixed positioning by row rect. */\n.floatMenu_427795137 {\n position: fixed;\n z-index: 60;\n min-width: 190px;\n}\n\n.menuHead_427795137 {\n padding: 6px 10px 5px;\n font-size: 11px;\n color: var(--dsw-alias-label-tertiary);\n border-bottom: 0.5px solid var(--dsw-alias-border-l2);\n margin-bottom: 3px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.menuHeadStrong_427795137 {\n color: var(--dsw-alias-label-primary);\n font-weight: 500;\n}\n\n.menuDanger_427795137 {\n color: var(--dsw-alias-status-danger, #d5304f);\n}\n\n.formField_427795137 {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.formLabel_427795137 {\n font-size: 12px;\n color: var(--dsw-alias-label-secondary);\n}\n\n.formInput_427795137 {\n font: inherit;\n font-size: 13px;\n border: 0.5px solid var(--dsw-alias-border-l4);\n border-radius: 8px;\n padding: 7px 10px;\n background: var(--dsw-alias-bg-layer-3);\n color: var(--dsw-alias-label-primary);\n outline: none;\n}\n\n.formInput_427795137:focus {\n border-color: var(--dsw-alias-brand-primary);\n}\n\n.formError_427795137 {\n font-size: 12px;\n color: var(--dsw-alias-status-danger, #d5304f);\n min-height: 16px;\n}\n\n/* Commit history */\n\n.historyHead_427795137 {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 12px;\n border-bottom: 0.5px solid var(--dsw-alias-border-l2);\n}\n\n.searchBox_427795137 {\n margin-left: auto;\n display: flex;\n align-items: center;\n gap: 6px;\n flex: 1;\n max-width: 300px;\n min-width: 140px;\n border: 0.5px solid var(--dsw-alias-border-l4);\n border-radius: 7px;\n padding: 0 9px;\n height: 28px;\n color: var(--dsw-alias-label-tertiary);\n background: var(--dsw-alias-bg-layer-3);\n}\n\n/* The branch card's head variant: sits right of the title and grows toward\n * the tools instead of being right-aligned by `margin-left: auto`. The\n * overrides win because both selectors are single classes and this one is\n * declared later. */\n.searchBoxHead_427795137 {\n margin-left: 0;\n flex: 1 1 auto;\n max-width: 320px;\n min-width: 90px;\n}\n\n.searchInput_427795137 {\n flex: 1;\n min-width: 0;\n border: 0;\n background: none;\n outline: none;\n font: inherit;\n font-size: 12px;\n color: var(--dsw-alias-label-primary);\n}\n\n.searchInput_427795137::placeholder {\n color: var(--dsw-alias-label-tertiary);\n}\n\n.commitRow_427795137 {\n position: relative;\n display: grid;\n grid-template-columns: 22px minmax(0, 1fr) auto;\n gap: 8px;\n align-items: center;\n padding: 6px 8px 6px 2px;\n cursor: pointer;\n border-radius: 7px;\n}\n\n.commitRow_427795137:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.commitRowOpen_427795137 {\n background: var(--dsw-alias-bg-layer-3);\n}\n\n.commitRowOpen_427795137:hover {\n background: var(--dsw-alias-bg-layer-3);\n}\n\n.commitGraph_427795137 {\n position: relative;\n align-self: stretch;\n min-height: 34px;\n}\n\n.commitGraph_427795137::before {\n content: \"\";\n position: absolute;\n left: 10px;\n top: 0;\n bottom: 0;\n width: 2px;\n background: var(--dsw-alias-border-l2);\n}\n\n.commitRowFirst_427795137 .commitGraph_427795137::before {\n top: 50%;\n}\n\n.commitRowLast_427795137 .commitGraph_427795137::before {\n bottom: 50%;\n}\n\n/* A small plain dot on the rail — the old 10px dot with a fat card-colored\n * ring read as a heavy black blob. #4c6bf5 for the same reason actPrimary\n * hardcodes it (the brand token does not resolve reliably in this panel). */\n.commitDot_427795137 {\n position: absolute;\n left: 8px;\n top: 50%;\n transform: translateY(-50%);\n width: 6px;\n height: 6px;\n border-radius: 50%;\n background: #4c6bf5;\n}\n\n.commitDotMerge_427795137 {\n background: #8350d8;\n}\n\n.commitMain_427795137 {\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 1px;\n}\n\n.commitSubject_427795137 {\n display: flex;\n align-items: center;\n gap: 6px;\n min-width: 0;\n font-size: 12.5px;\n color: var(--dsw-alias-label-primary);\n}\n\n.commitSubjectText_427795137 {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.commitRowOpen_427795137 .commitSubjectText_427795137 {\n font-weight: 600;\n}\n\n.refBadges_427795137 {\n display: inline-flex;\n gap: 4px;\n flex: none;\n}\n\n.refBadge_427795137 {\n font-size: 10px;\n line-height: 1;\n border-radius: 4px;\n padding: 3px 5px;\n font-weight: 500;\n white-space: nowrap;\n max-width: 140px;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.refHead_427795137 {\n background: var(--dsw-alias-bg-layer-3);\n color: var(--dsw-alias-label-primary-bluish);\n border: 0.5px solid var(--dsw-alias-border-l4);\n}\n\n.refTag_427795137 {\n background: var(--dsw-alias-bg-layer-3);\n color: var(--dsw-alias-label-warning, #b07000);\n border: 0.5px solid var(--dsw-alias-border-l4);\n}\n\n.refRemote_427795137 {\n background: var(--dsw-alias-bg-layer-3);\n color: #8350d8;\n border: 0.5px solid var(--dsw-alias-border-l4);\n}\n\n.refLocal_427795137 {\n background: var(--dsw-alias-bg-layer-3);\n color: var(--dsw-alias-status-success, #218358);\n border: 0.5px solid var(--dsw-alias-border-l4);\n}\n\n.commitMeta_427795137 {\n display: flex;\n align-items: center;\n gap: 6px;\n color: var(--dsw-alias-label-tertiary);\n font-size: 11.5px;\n}\n\n.commitSide_427795137 {\n display: flex;\n align-items: center;\n gap: 8px;\n color: var(--dsw-alias-label-tertiary);\n font-size: 11.5px;\n flex: none;\n}\n\n.commitHash_427795137 {\n font-family: ui-monospace, Consolas, monospace;\n}\n\n.commitExpand_427795137 {\n grid-column: 1 / -1;\n padding: 2px 4px 8px 28px;\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n\n.commitExpandTitle_427795137 {\n font-size: 11px;\n color: var(--dsw-alias-label-tertiary);\n margin-bottom: 3px;\n}\n\n.fileRow_427795137 {\n display: grid;\n grid-template-columns: 18px minmax(0, 1fr) auto;\n gap: 8px;\n align-items: center;\n padding: 3px 6px;\n border-radius: 6px;\n font-size: 12px;\n color: var(--dsw-alias-label-primary);\n}\n\n.fileRow_427795137:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.filePath_427795137 {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-family: ui-monospace, Consolas, monospace;\n font-size: 11.5px;\n direction: rtl;\n text-align: left;\n}\n\n.fileNums_427795137 {\n font-variant-numeric: tabular-nums;\n color: var(--dsw-alias-label-tertiary);\n flex: none;\n}\n\n.fileAdd_427795137 { color: var(--dsw-alias-status-success, #218358); }\n.fileDel_427795137 { color: var(--dsw-alias-status-danger, #d5304f); }\n\n.empty_427795137 {\n padding: 28px 16px;\n text-align: center;\n color: var(--dsw-alias-label-tertiary);\n font-size: 12.5px;\n}\n\n.errorText_427795137 {\n padding: 20px 16px;\n text-align: center;\n color: var(--dsw-alias-status-danger, #d5304f);\n font-size: 12.5px;\n}\n\n.moreRow_427795137 {\n display: flex;\n justify-content: center;\n padding: 8px 0 2px;\n}\n";
|
|
3743
|
+
const css$2 = "/* Git panel (sidebar global panel). Three stacked sections over the main\n * column: uncommitted changes + commit box, branches, commit history. Host\n * tokens only; the few status colors fall back to the same literals the\n * settings card uses. */\n\n.page_427795137 {\n display: flex;\n flex-direction: column;\n min-height: 0;\n height: 100%;\n}\n\n.topbar_427795137 {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 10px 16px 8px;\n}\n\n.back_427795137 {\n flex: none;\n display: inline-flex;\n align-items: center;\n gap: 6px;\n border: 0;\n background: none;\n cursor: pointer;\n font-size: 13px;\n line-height: 1.5;\n color: var(--dsw-alias-label-secondary);\n padding: 4px 8px;\n border-radius: 8px;\n}\n\n.back_427795137:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n color: var(--dsw-alias-label-primary);\n}\n\n.title_427795137 {\n margin: 0;\n font-size: 16px;\n font-weight: 600;\n color: var(--dsw-alias-label-primary);\n}\n\n.repoPill_427795137 {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n min-width: 0;\n font-size: 12px;\n line-height: 1.4;\n color: var(--dsw-alias-label-secondary);\n border: 0.5px solid var(--dsw-alias-border-l4);\n border-radius: 999px;\n padding: 3px 10px;\n white-space: nowrap;\n overflow: hidden;\n}\n\n.repoName_427795137 {\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.branchName_427795137 {\n color: var(--dsw-alias-label-primary);\n font-weight: 500;\n}\n\n.track_427795137 {\n font-variant-numeric: tabular-nums;\n}\n\n.topbarActions_427795137 {\n margin-left: auto;\n display: flex;\n align-items: center;\n gap: 6px;\n flex: none;\n}\n\n.refreshBtn_427795137 {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n border: 0;\n border-radius: 8px;\n background: none;\n color: var(--dsw-alias-label-secondary);\n cursor: pointer;\n}\n\n.refreshBtn_427795137:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n color: var(--dsw-alias-label-primary);\n}\n\n.refreshBtn_427795137[disabled] {\n opacity: 0.5;\n cursor: default;\n}\n\n/* Cn-accelerate: the topbar 国内加速 button and its anchored popover. The\n * button's on-state is brand tint + brand text + green dot; the popover\n * mirrors the settings card's switch/input affordances over this panel's\n * tokens (popup chrome follows .menu_427795137 above). */\n\n.cnWrap_427795137 {\n position: relative;\n display: inline-flex;\n}\n\n.cnBtn_427795137,\n.cnBtnOn_427795137 {\n display: inline-flex;\n align-items: center;\n gap: 5px;\n height: 28px;\n padding: 0 10px;\n border: 0;\n border-radius: 8px;\n background: none;\n color: var(--dsw-alias-label-secondary);\n font: inherit;\n font-size: 12px;\n line-height: 1;\n cursor: pointer;\n}\n\n.cnBtn_427795137:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n color: var(--dsw-alias-label-primary);\n}\n\n.cnBtnOn_427795137 {\n background: rgba(76, 107, 245, 0.16);\n color: var(--dsw-alias-brand-primary);\n}\n\n.cnBtnOn_427795137:hover {\n background: rgba(76, 107, 245, 0.22);\n color: var(--dsw-alias-brand-primary);\n}\n\n.cnDot_427795137 {\n width: 6px;\n height: 6px;\n border-radius: 50%;\n background: var(--dsw-alias-status-success, #218358);\n}\n\n.cnPop_427795137 {\n position: absolute;\n right: 0;\n top: calc(100% + 6px);\n z-index: 40;\n width: 320px;\n background: var(--dsw-alias-surface-raised, var(--dsw-alias-bg-layer-3));\n border: 0.5px solid var(--dsw-alias-border-l4);\n border-radius: 8px;\n box-shadow: var(--dsw-shadow-lv3);\n backdrop-filter: var(--dsw-menu-backdrop-filter);\n padding: 12px;\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.cnPopHead_427795137 {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n}\n\n.cnPopTitle_427795137 {\n font-size: 13px;\n font-weight: 600;\n color: var(--dsw-alias-label-primary);\n}\n\n.cnSwitchRow_427795137 {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n font-size: 12px;\n color: var(--dsw-alias-label-secondary);\n cursor: pointer;\n}\n\n.cnSwitch_427795137 {\n width: 16px;\n height: 16px;\n accent-color: var(--dsw-alias-brand-primary);\n cursor: pointer;\n}\n\n.cnSwitch_427795137:disabled {\n cursor: default;\n}\n\n.cnHint_427795137 {\n margin: 0;\n font-size: 11px;\n line-height: 16px;\n color: var(--dsw-alias-label-tertiary);\n}\n\n.cnFieldHead_427795137 {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n font-size: 12px;\n}\n\n.cnFieldLabel_427795137 {\n color: var(--dsw-alias-label-primary);\n font-weight: 500;\n}\n\n.cnLink_427795137 {\n border: 0;\n background: none;\n padding: 0;\n font: inherit;\n font-size: 12px;\n color: var(--dsw-alias-brand-primary);\n cursor: pointer;\n}\n\n.cnLink_427795137:hover {\n text-decoration: underline;\n}\n\n.cnLink_427795137:disabled {\n opacity: 0.5;\n cursor: default;\n text-decoration: none;\n}\n\n.cnInput_427795137,\n.cnInputBad_427795137 {\n box-sizing: border-box;\n width: 100%;\n border: 0.5px solid var(--dsw-alias-border-l4);\n border-radius: 6px;\n background: var(--dsw-alias-bg-layer-3);\n color: var(--dsw-alias-label-primary);\n font: inherit;\n font-size: 12px;\n padding: 6px 8px;\n outline: none;\n}\n\n.cnInput_427795137:focus {\n border-color: var(--dsw-alias-brand-primary);\n}\n\n.cnInputBad_427795137,\n.cnInputBad_427795137:focus {\n border-color: var(--dsw-alias-status-danger, #d5304f);\n}\n\n.cnError_427795137 {\n font-size: 11px;\n line-height: 16px;\n color: var(--dsw-alias-status-danger, #d5304f);\n}\n\n.cnFooter_427795137 {\n display: flex;\n justify-content: flex-end;\n gap: 6px;\n margin-top: 2px;\n}\n\n.cnGhost_427795137,\n.cnSave_427795137 {\n border: 0;\n border-radius: 6px;\n font: inherit;\n font-size: 12px;\n padding: 5px 12px;\n cursor: pointer;\n}\n\n.cnGhost_427795137 {\n background: none;\n color: var(--dsw-alias-label-secondary);\n}\n\n.cnGhost_427795137:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n color: var(--dsw-alias-label-primary);\n}\n\n.cnSave_427795137 {\n background: var(--dsw-alias-brand-primary);\n color: #fff;\n}\n\n.cnSave_427795137:disabled {\n opacity: 0.5;\n cursor: default;\n}\n\n.scroll_427795137 {\n flex: 1;\n min-height: 0;\n overflow-y: auto;\n padding: 0 16px 16px;\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n\n/* The page column below the topbar is a definite-height flex column and the\n * three cards split it 2:1:2 (uncommitted : branches : history) — five\n * shares, the topbar and gaps taking their natural height first. Each body\n * scrolls INSIDE its share — min-height: 0 is what lets a flex child shrink\n * below its content (the old natural-height model painted overflowing rows\n * over the sections below). A floor keeps a very short panel from\n * collapsing a card to its bare head; past the floor the column itself\n * scrolls. */\n.section_427795137 {\n border: 0.5px solid var(--dsw-alias-border-l2);\n border-radius: 10px;\n background: var(--dsw-alias-bg-layer-2);\n display: flex;\n flex-direction: column;\n min-height: 0;\n}\n\n.sectionFill_427795137 {\n flex: 1 1 0;\n min-height: 84px;\n}\n\n/* The three flex-grow overrides of the 2:1:2 split. */\n.sectionChanges_427795137 {\n flex-grow: 2;\n}\n\n.sectionBranches_427795137 {\n flex-grow: 1;\n}\n\n.sectionHistory_427795137 {\n flex-grow: 2;\n}\n\n.sectionHead_427795137 {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 12px;\n border-bottom: 0.5px solid var(--dsw-alias-border-l2);\n}\n\n.sectionTitle_427795137 {\n margin: 0;\n font-size: 13px;\n font-weight: 600;\n color: var(--dsw-alias-label-primary);\n}\n\n.countBadge_427795137 {\n font-size: 10px;\n line-height: 1;\n border-radius: 999px;\n padding: 3px 7px;\n background: var(--dsw-alias-bg-layer-3);\n color: var(--dsw-alias-label-secondary);\n font-variant-numeric: tabular-nums;\n white-space: nowrap;\n}\n\n.sectionTools_427795137 {\n margin-left: auto;\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n.sectionBody_427795137 {\n padding: 10px 12px;\n display: flex;\n flex-direction: column;\n gap: 8px;\n flex: 1 1 auto;\n min-height: 0;\n}\n\n/* The branch and history bodies scroll as a whole inside their share. */\n.sectionBodyScroll_427795137 {\n overflow-y: auto;\n overscroll-behavior: contain;\n}\n\n/* Inside the uncommitted card only the file lists scroll; the commit box\n * below stays pinned however long the change tree is. */\n.changeList_427795137 {\n display: flex;\n flex-direction: column;\n gap: 8px;\n flex: 1 1 auto;\n min-height: 0;\n overflow-y: auto;\n overscroll-behavior: contain;\n}\n\n/* The 全部展开/全部折叠 text chips in the branch card head. */\n.treeToggle_427795137 {\n background: none;\n border: 0;\n padding: 2px 5px;\n font: inherit;\n font-size: 11.5px;\n line-height: 1.4;\n color: var(--dsw-alias-label-secondary);\n cursor: pointer;\n border-radius: 5px;\n white-space: nowrap;\n}\n\n.treeToggle_427795137:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n color: var(--dsw-alias-brand-primary);\n}\n\n/* Small action chip used across the section heads and the commit row. */\n.act_427795137 {\n display: inline-flex;\n align-items: center;\n gap: 5px;\n font: inherit;\n font-size: 12px;\n line-height: 1.4;\n cursor: pointer;\n background: var(--dsw-alias-bg-layer-3);\n color: var(--dsw-alias-label-primary);\n border: 0.5px solid var(--dsw-alias-border-l4);\n border-radius: 7px;\n padding: 4px 10px;\n white-space: nowrap;\n}\n\n.act_427795137:hover:not([disabled]) {\n border-color: var(--dsw-alias-brand-primary);\n}\n\n.act_427795137[disabled] {\n opacity: 0.45;\n cursor: default;\n}\n\n.actPrimary_427795137 {\n /* Hardcoded blue by request — the brand token does not resolve reliably\n * inside the better-sidebar panel. */\n background: #4c6bf5;\n border-color: transparent;\n color: #fff;\n}\n\n.actPrimary_427795137:hover:not([disabled]) {\n border-color: transparent;\n filter: brightness(1.06);\n}\n\n/* Uncommitted changes */\n\n.groupTitleRow_427795137 {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 12px;\n color: var(--dsw-alias-label-secondary);\n}\n\n.linkBtn_427795137 {\n margin-left: auto;\n background: none;\n border: 0;\n padding: 2px 4px;\n font: inherit;\n font-size: 12px;\n color: var(--dsw-alias-brand-primary);\n cursor: pointer;\n border-radius: 5px;\n}\n\n.linkBtn_427795137:hover:not([disabled]) {\n text-decoration: underline;\n}\n\n.linkBtn_427795137[disabled] {\n opacity: 0.5;\n cursor: default;\n}\n\n.changeRow_427795137 {\n display: grid;\n grid-template-columns: 16px 18px minmax(0, 1fr) auto;\n gap: 8px;\n align-items: center;\n padding: 3px 6px;\n border-radius: 6px;\n font-size: 12px;\n color: var(--dsw-alias-label-primary);\n}\n\n.changeRow_427795137:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.changeCheck_427795137 {\n width: 14px;\n height: 14px;\n accent-color: var(--dsw-alias-brand-primary);\n cursor: pointer;\n margin: 0;\n}\n\n.statusBadge_427795137 {\n width: 16px;\n height: 16px;\n border-radius: 4px;\n font-size: 10px;\n line-height: 1;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n font-weight: 700;\n color: #fff;\n box-sizing: border-box;\n}\n\n.statusA_427795137 { background: var(--dsw-alias-status-success, #218358); }\n.statusM_427795137 { background: var(--dsw-alias-label-warning, #b07000); }\n.statusD_427795137 { background: var(--dsw-alias-status-danger, #d5304f); }\n.statusR_427795137 { background: #8350d8; }\n.statusC_427795137 { background: #8350d8; }\n.statusT_427795137 { background: #5b7a9d; }\n.statusQ_427795137 { background: var(--dsw-alias-label-dimmed, #9aa0aa); }\n.statusU_427795137 { background: #d5502e; }\n\n.changePath_427795137 {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-family: ui-monospace, Consolas, monospace;\n font-size: 11.5px;\n direction: rtl;\n text-align: left;\n}\n\n.stageHint_427795137 {\n color: var(--dsw-alias-label-tertiary);\n font-size: 11px;\n flex: none;\n}\n\n.commitBox_427795137 {\n display: flex;\n flex-direction: column;\n gap: 8px;\n margin-top: 4px;\n flex: none;\n}\n\n.commitInput_427795137 {\n width: 100%;\n box-sizing: border-box;\n resize: vertical;\n min-height: 52px;\n font: inherit;\n font-size: 12.5px;\n line-height: 1.5;\n border: 0.5px solid var(--dsw-alias-border-l4);\n border-radius: 8px;\n padding: 7px 10px;\n background: var(--dsw-alias-bg-layer-3);\n color: var(--dsw-alias-label-primary);\n outline: none;\n}\n\n.commitInput_427795137:focus {\n border-color: var(--dsw-alias-brand-primary);\n}\n\n.commitActions_427795137 {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.commitHint_427795137 {\n color: var(--dsw-alias-label-tertiary);\n font-size: 11px;\n min-height: 16px;\n}\n\n.split_427795137 {\n margin-left: auto;\n display: flex;\n position: relative;\n}\n\n.splitMain_427795137 {\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n padding: 6px 16px;\n font-size: 13px;\n}\n\n.splitCaret_427795137 {\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n margin-left: 1px;\n padding: 6px 6px;\n}\n\n.menu_427795137 {\n position: absolute;\n right: 0;\n top: calc(100% + 4px);\n z-index: 40;\n width: max-content;\n min-width: 0;\n background: var(--dsw-alias-surface-raised, var(--dsw-alias-bg-layer-3));\n border: 0.5px solid var(--dsw-alias-border-l4);\n border-radius: 8px;\n box-shadow: var(--dsw-shadow-lv3);\n backdrop-filter: var(--dsw-menu-backdrop-filter);\n padding: 3px;\n display: flex;\n flex-direction: column;\n}\n\n.menuItem_427795137 {\n display: block;\n width: 100%;\n text-align: left;\n background: none;\n border: 0;\n color: var(--dsw-alias-label-primary);\n font: inherit;\n font-size: 12px;\n line-height: 1.4;\n padding: 5px 10px;\n border-radius: 5px;\n cursor: pointer;\n white-space: nowrap;\n}\n\n.menuItem_427795137:hover:not([disabled]) {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.menuItem_427795137[disabled] {\n opacity: 0.5;\n cursor: default;\n}\n\n.menuSub_427795137 {\n color: var(--dsw-alias-label-tertiary);\n font-size: 11px;\n margin-top: 1px;\n}\n\n/* Branch section — two IDEA-style trees: 本地 → branches, 远程 → per-remote → branches */\n\n.treeHead_427795137 {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 4px 6px;\n border-radius: 6px;\n cursor: pointer;\n font-size: 12px;\n font-weight: 600;\n color: var(--dsw-alias-label-secondary);\n user-select: none;\n}\n\n.treeHead_427795137:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n color: var(--dsw-alias-label-primary);\n}\n\n.treeChevron_427795137 {\n display: inline-flex;\n align-items: center;\n color: var(--dsw-alias-label-tertiary);\n}\n\n.treeChildren_427795137 {\n display: flex;\n flex-direction: column;\n margin-left: 12px;\n}\n\n.treeHeadNest_427795137 {\n font-weight: 400;\n}\n\n.treeNest_427795137 {\n margin-left: 12px;\n}\n\n.branchRow_427795137 {\n display: grid;\n grid-template-columns: 20px minmax(0, 1fr) auto;\n gap: 8px;\n align-items: center;\n padding: 5px 8px;\n border-radius: 7px;\n /* The action menu opens on RIGHT-CLICK (Enter/Space for keyboard) — an\n * arrow, not the pointing hand a left-click affordance would promise. */\n cursor: default;\n font-size: 12.5px;\n color: var(--dsw-alias-label-primary);\n}\n\n.branchRow_427795137:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n/* Whole-row highlight for the current branch: the ✓ alone read as too\n * quiet inside the tree. A translucent brand-blue wash plus the inset left\n * accent and a colored name survives both themes; #4c6bf5 is the same\n * hardcoded blue actPrimary uses (the brand token does not resolve\n * reliably inside the better-sidebar panel). */\n.branchRowCurrent_427795137 {\n background: rgba(76, 107, 245, 0.16);\n box-shadow: inset 2px 0 0 #4c6bf5;\n cursor: default;\n}\n\n.branchRowCurrent_427795137:hover {\n background: rgba(76, 107, 245, 0.16);\n}\n\n.branchRowCurrent_427795137 .branchName_427795137 {\n color: #4c6bf5;\n font-weight: 600;\n}\n\n.branchMark_427795137 {\n color: var(--dsw-alias-brand-primary);\n display: inline-flex;\n align-items: center;\n justify-content: center;\n}\n\n.branchName_427795137 {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-weight: 500;\n}\n\n.branchKind_427795137 {\n color: var(--dsw-alias-label-tertiary);\n font-size: 10px;\n margin-left: 6px;\n}\n\n.branchTrack_427795137 {\n color: var(--dsw-alias-label-tertiary);\n font-size: 11px;\n font-variant-numeric: tabular-nums;\n flex: none;\n}\n\n/* The branch action menu is portaled; fixed positioning by row rect. */\n.floatMenu_427795137 {\n position: fixed;\n z-index: 60;\n min-width: 190px;\n}\n\n.menuHead_427795137 {\n padding: 6px 10px 5px;\n font-size: 11px;\n color: var(--dsw-alias-label-tertiary);\n border-bottom: 0.5px solid var(--dsw-alias-border-l2);\n margin-bottom: 3px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.menuHeadStrong_427795137 {\n color: var(--dsw-alias-label-primary);\n font-weight: 500;\n}\n\n.menuDanger_427795137 {\n color: var(--dsw-alias-status-danger, #d5304f);\n}\n\n.formField_427795137 {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.formLabel_427795137 {\n font-size: 12px;\n color: var(--dsw-alias-label-secondary);\n}\n\n.formInput_427795137 {\n font: inherit;\n font-size: 13px;\n border: 0.5px solid var(--dsw-alias-border-l4);\n border-radius: 8px;\n padding: 7px 10px;\n background: var(--dsw-alias-bg-layer-3);\n color: var(--dsw-alias-label-primary);\n outline: none;\n}\n\n.formInput_427795137:focus {\n border-color: var(--dsw-alias-brand-primary);\n}\n\n.formError_427795137 {\n font-size: 12px;\n color: var(--dsw-alias-status-danger, #d5304f);\n min-height: 16px;\n}\n\n/* Commit history */\n\n.historyHead_427795137 {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 12px;\n border-bottom: 0.5px solid var(--dsw-alias-border-l2);\n}\n\n.searchBox_427795137 {\n margin-left: auto;\n display: flex;\n align-items: center;\n gap: 6px;\n flex: 1;\n max-width: 300px;\n min-width: 140px;\n border: 0.5px solid var(--dsw-alias-border-l4);\n border-radius: 7px;\n padding: 0 9px;\n height: 28px;\n color: var(--dsw-alias-label-tertiary);\n background: var(--dsw-alias-bg-layer-3);\n}\n\n/* The branch card's head variant: sits right of the title and grows toward\n * the tools instead of being right-aligned by `margin-left: auto`. The\n * overrides win because both selectors are single classes and this one is\n * declared later. */\n.searchBoxHead_427795137 {\n margin-left: 0;\n flex: 1 1 auto;\n max-width: 320px;\n min-width: 90px;\n}\n\n.searchInput_427795137 {\n flex: 1;\n min-width: 0;\n border: 0;\n background: none;\n outline: none;\n font: inherit;\n font-size: 12px;\n color: var(--dsw-alias-label-primary);\n}\n\n.searchInput_427795137::placeholder {\n color: var(--dsw-alias-label-tertiary);\n}\n\n.commitRow_427795137 {\n position: relative;\n display: grid;\n grid-template-columns: 22px minmax(0, 1fr) auto;\n gap: 8px;\n align-items: center;\n padding: 6px 8px 6px 2px;\n cursor: pointer;\n border-radius: 7px;\n}\n\n.commitRow_427795137:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.commitRowOpen_427795137 {\n background: var(--dsw-alias-bg-layer-3);\n}\n\n.commitRowOpen_427795137:hover {\n background: var(--dsw-alias-bg-layer-3);\n}\n\n.commitGraph_427795137 {\n position: relative;\n align-self: stretch;\n min-height: 34px;\n}\n\n.commitGraph_427795137::before {\n content: \"\";\n position: absolute;\n left: 10px;\n top: 0;\n bottom: 0;\n width: 2px;\n background: var(--dsw-alias-border-l2);\n}\n\n.commitRowFirst_427795137 .commitGraph_427795137::before {\n top: 50%;\n}\n\n.commitRowLast_427795137 .commitGraph_427795137::before {\n bottom: 50%;\n}\n\n/* A small plain dot on the rail — the old 10px dot with a fat card-colored\n * ring read as a heavy black blob. #4c6bf5 for the same reason actPrimary\n * hardcodes it (the brand token does not resolve reliably in this panel). */\n.commitDot_427795137 {\n position: absolute;\n left: 8px;\n top: 50%;\n transform: translateY(-50%);\n width: 6px;\n height: 6px;\n border-radius: 50%;\n background: #4c6bf5;\n}\n\n.commitDotMerge_427795137 {\n background: #8350d8;\n}\n\n.commitMain_427795137 {\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 1px;\n}\n\n.commitSubject_427795137 {\n display: flex;\n align-items: center;\n gap: 6px;\n min-width: 0;\n font-size: 12.5px;\n color: var(--dsw-alias-label-primary);\n}\n\n.commitSubjectText_427795137 {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.commitRowOpen_427795137 .commitSubjectText_427795137 {\n font-weight: 600;\n}\n\n.refBadges_427795137 {\n display: inline-flex;\n gap: 4px;\n flex: none;\n}\n\n.refBadge_427795137 {\n font-size: 10px;\n line-height: 1;\n border-radius: 4px;\n padding: 3px 5px;\n font-weight: 500;\n white-space: nowrap;\n max-width: 140px;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.refHead_427795137 {\n background: var(--dsw-alias-bg-layer-3);\n color: var(--dsw-alias-label-primary-bluish);\n border: 0.5px solid var(--dsw-alias-border-l4);\n}\n\n.refTag_427795137 {\n background: var(--dsw-alias-bg-layer-3);\n color: var(--dsw-alias-label-warning, #b07000);\n border: 0.5px solid var(--dsw-alias-border-l4);\n}\n\n.refRemote_427795137 {\n background: var(--dsw-alias-bg-layer-3);\n color: #8350d8;\n border: 0.5px solid var(--dsw-alias-border-l4);\n}\n\n.refLocal_427795137 {\n background: var(--dsw-alias-bg-layer-3);\n color: var(--dsw-alias-status-success, #218358);\n border: 0.5px solid var(--dsw-alias-border-l4);\n}\n\n.commitMeta_427795137 {\n display: flex;\n align-items: center;\n gap: 6px;\n color: var(--dsw-alias-label-tertiary);\n font-size: 11.5px;\n}\n\n.commitSide_427795137 {\n display: flex;\n align-items: center;\n gap: 8px;\n color: var(--dsw-alias-label-tertiary);\n font-size: 11.5px;\n flex: none;\n}\n\n.commitHash_427795137 {\n font-family: ui-monospace, Consolas, monospace;\n}\n\n.commitExpand_427795137 {\n grid-column: 1 / -1;\n padding: 2px 4px 8px 28px;\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n\n.commitExpandTitle_427795137 {\n font-size: 11px;\n color: var(--dsw-alias-label-tertiary);\n margin-bottom: 3px;\n}\n\n.fileRow_427795137 {\n display: grid;\n grid-template-columns: 18px minmax(0, 1fr) auto;\n gap: 8px;\n align-items: center;\n padding: 3px 6px;\n border-radius: 6px;\n font-size: 12px;\n color: var(--dsw-alias-label-primary);\n}\n\n.fileRow_427795137:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.filePath_427795137 {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-family: ui-monospace, Consolas, monospace;\n font-size: 11.5px;\n direction: rtl;\n text-align: left;\n}\n\n.fileNums_427795137 {\n font-variant-numeric: tabular-nums;\n color: var(--dsw-alias-label-tertiary);\n flex: none;\n}\n\n.fileAdd_427795137 { color: var(--dsw-alias-status-success, #218358); }\n.fileDel_427795137 { color: var(--dsw-alias-status-danger, #d5304f); }\n\n.empty_427795137 {\n padding: 28px 16px;\n text-align: center;\n color: var(--dsw-alias-label-tertiary);\n font-size: 12.5px;\n}\n\n.errorText_427795137 {\n padding: 20px 16px;\n text-align: center;\n color: var(--dsw-alias-status-danger, #d5304f);\n font-size: 12.5px;\n}\n\n.moreRow_427795137 {\n display: flex;\n justify-content: center;\n padding: 8px 0 2px;\n}\n";
|
|
3569
3744
|
const tagId$2 = "dsh-git-branch-manage/GitHistoryPanel.module.css";
|
|
3570
3745
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=\"" + tagId$2 + "\"]") === null) {
|
|
3571
3746
|
const tag = document.createElement("style");
|
|
@@ -3585,6 +3760,26 @@ window.__ModuleLoader__.load({
|
|
|
3585
3760
|
"track": "track_427795137",
|
|
3586
3761
|
"topbarActions": "topbarActions_427795137",
|
|
3587
3762
|
"refreshBtn": "refreshBtn_427795137",
|
|
3763
|
+
"menu": "menu_427795137",
|
|
3764
|
+
"cnWrap": "cnWrap_427795137",
|
|
3765
|
+
"cnBtn": "cnBtn_427795137",
|
|
3766
|
+
"cnBtnOn": "cnBtnOn_427795137",
|
|
3767
|
+
"cnDot": "cnDot_427795137",
|
|
3768
|
+
"cnPop": "cnPop_427795137",
|
|
3769
|
+
"cnPopHead": "cnPopHead_427795137",
|
|
3770
|
+
"cnPopTitle": "cnPopTitle_427795137",
|
|
3771
|
+
"cnSwitchRow": "cnSwitchRow_427795137",
|
|
3772
|
+
"cnSwitch": "cnSwitch_427795137",
|
|
3773
|
+
"cnHint": "cnHint_427795137",
|
|
3774
|
+
"cnFieldHead": "cnFieldHead_427795137",
|
|
3775
|
+
"cnFieldLabel": "cnFieldLabel_427795137",
|
|
3776
|
+
"cnLink": "cnLink_427795137",
|
|
3777
|
+
"cnInput": "cnInput_427795137",
|
|
3778
|
+
"cnInputBad": "cnInputBad_427795137",
|
|
3779
|
+
"cnError": "cnError_427795137",
|
|
3780
|
+
"cnFooter": "cnFooter_427795137",
|
|
3781
|
+
"cnGhost": "cnGhost_427795137",
|
|
3782
|
+
"cnSave": "cnSave_427795137",
|
|
3588
3783
|
"scroll": "scroll_427795137",
|
|
3589
3784
|
"section": "section_427795137",
|
|
3590
3785
|
"sectionFill": "sectionFill_427795137",
|
|
@@ -3623,7 +3818,6 @@ window.__ModuleLoader__.load({
|
|
|
3623
3818
|
"split": "split_427795137",
|
|
3624
3819
|
"splitMain": "splitMain_427795137",
|
|
3625
3820
|
"splitCaret": "splitCaret_427795137",
|
|
3626
|
-
"menu": "menu_427795137",
|
|
3627
3821
|
"menuItem": "menuItem_427795137",
|
|
3628
3822
|
"menuSub": "menuSub_427795137",
|
|
3629
3823
|
"treeHead": "treeHead_427795137",
|
|
@@ -4116,6 +4310,135 @@ window.__ModuleLoader__.load({
|
|
|
4116
4310
|
});
|
|
4117
4311
|
}
|
|
4118
4312
|
//#endregion
|
|
4313
|
+
//#region src/client/panel/CnAcceleratePopover.tsx
|
|
4314
|
+
/**
|
|
4315
|
+
* CnAcceleratePopover: the 国内加速 popover under the topbar button.
|
|
4316
|
+
*
|
|
4317
|
+
* This is the SHORTCUT surface of the same staged form the settings card row
|
|
4318
|
+
* edits ({@link ../accel-form}): enable switch + mirror URL stage together
|
|
4319
|
+
* and commit together on 保存. Closing the popover — Esc, outside click, 取消
|
|
4320
|
+
* — discards the staged edits through the owner's close handler, so an
|
|
4321
|
+
* abandoned draft can never land half-written.
|
|
4322
|
+
*
|
|
4323
|
+
* Layout follows docs/design/cn-accelerate.md §2.2: title + switch → security
|
|
4324
|
+
* hint → URL field with 恢复默认 → error line → footer.
|
|
4325
|
+
*
|
|
4326
|
+
* @module git-worktree/client/panel/CnAcceleratePopover
|
|
4327
|
+
*/
|
|
4328
|
+
function CnAcceleratePopover(props) {
|
|
4329
|
+
const { accel, t, pushToast, onClose } = props;
|
|
4330
|
+
const rootRef = (0, react.useRef)(null);
|
|
4331
|
+
const state = (0, react.useSyncExternalStore)(accel.store.subscribe, accel.store.getSnapshot);
|
|
4332
|
+
(0, react.useEffect)(() => {
|
|
4333
|
+
const onKeyDown = (event) => {
|
|
4334
|
+
if (event.key === "Escape") onClose();
|
|
4335
|
+
};
|
|
4336
|
+
const onPointerDown = (event) => {
|
|
4337
|
+
if (rootRef.current !== null && !rootRef.current.contains(event.target)) onClose();
|
|
4338
|
+
};
|
|
4339
|
+
document.addEventListener("keydown", onKeyDown);
|
|
4340
|
+
document.addEventListener("pointerdown", onPointerDown);
|
|
4341
|
+
return () => {
|
|
4342
|
+
document.removeEventListener("keydown", onKeyDown);
|
|
4343
|
+
document.removeEventListener("pointerdown", onPointerDown);
|
|
4344
|
+
};
|
|
4345
|
+
}, [onClose]);
|
|
4346
|
+
const locked = state.saving || !state.writable;
|
|
4347
|
+
const onSave = (0, react.useCallback)(() => {
|
|
4348
|
+
if (state.saving || state.urlError !== null || !state.writable) return;
|
|
4349
|
+
(async () => {
|
|
4350
|
+
await accel.saveAccel();
|
|
4351
|
+
const after = accel.store.getSnapshot();
|
|
4352
|
+
if (after.failed) {
|
|
4353
|
+
pushToast(t("accel.saveFailed"), "error");
|
|
4354
|
+
return;
|
|
4355
|
+
}
|
|
4356
|
+
pushToast(after.enabled ? t("accel.savedOn", { url: after.urlText }) : t("accel.savedOff"), "success");
|
|
4357
|
+
onClose();
|
|
4358
|
+
})();
|
|
4359
|
+
}, [
|
|
4360
|
+
accel,
|
|
4361
|
+
onClose,
|
|
4362
|
+
pushToast,
|
|
4363
|
+
state.saving,
|
|
4364
|
+
state.urlError,
|
|
4365
|
+
state.writable,
|
|
4366
|
+
t
|
|
4367
|
+
]);
|
|
4368
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4369
|
+
className: GitHistoryPanel_module_default.cnPop,
|
|
4370
|
+
ref: rootRef,
|
|
4371
|
+
role: "dialog",
|
|
4372
|
+
"aria-label": t("panel.cnAccelerate"),
|
|
4373
|
+
children: [
|
|
4374
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4375
|
+
className: GitHistoryPanel_module_default.cnPopHead,
|
|
4376
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4377
|
+
className: GitHistoryPanel_module_default.cnPopTitle,
|
|
4378
|
+
children: t("panel.cnAccelerate")
|
|
4379
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
4380
|
+
className: GitHistoryPanel_module_default.cnSwitchRow,
|
|
4381
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("accel.enable") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
4382
|
+
type: "checkbox",
|
|
4383
|
+
className: GitHistoryPanel_module_default.cnSwitch,
|
|
4384
|
+
checked: state.enabled,
|
|
4385
|
+
disabled: locked,
|
|
4386
|
+
onChange: (event) => accel.setAccelEnabled(event.target.checked)
|
|
4387
|
+
})]
|
|
4388
|
+
})]
|
|
4389
|
+
}),
|
|
4390
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
4391
|
+
className: GitHistoryPanel_module_default.cnHint,
|
|
4392
|
+
children: t("accel.hint")
|
|
4393
|
+
}),
|
|
4394
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4395
|
+
className: GitHistoryPanel_module_default.cnFieldHead,
|
|
4396
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4397
|
+
className: GitHistoryPanel_module_default.cnFieldLabel,
|
|
4398
|
+
children: t("accel.urlLabel")
|
|
4399
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4400
|
+
type: "button",
|
|
4401
|
+
className: GitHistoryPanel_module_default.cnLink,
|
|
4402
|
+
disabled: locked,
|
|
4403
|
+
onClick: () => accel.restoreAccelDefault(),
|
|
4404
|
+
children: t("accel.restoreDefault")
|
|
4405
|
+
})]
|
|
4406
|
+
}),
|
|
4407
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
4408
|
+
type: "text",
|
|
4409
|
+
className: state.urlError !== null ? GitHistoryPanel_module_default.cnInputBad : GitHistoryPanel_module_default.cnInput,
|
|
4410
|
+
value: state.urlText,
|
|
4411
|
+
spellCheck: false,
|
|
4412
|
+
disabled: locked,
|
|
4413
|
+
"aria-invalid": state.urlError !== null,
|
|
4414
|
+
onChange: (event) => accel.editAccelUrl(event.target.value),
|
|
4415
|
+
onKeyDown: (event) => {
|
|
4416
|
+
if (event.key === "Enter") onSave();
|
|
4417
|
+
}
|
|
4418
|
+
}),
|
|
4419
|
+
state.urlError !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4420
|
+
className: GitHistoryPanel_module_default.cnError,
|
|
4421
|
+
children: state.urlError === "empty" ? t("accel.urlEmpty") : t("accel.urlInvalid")
|
|
4422
|
+
}) : null,
|
|
4423
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4424
|
+
className: GitHistoryPanel_module_default.cnFooter,
|
|
4425
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4426
|
+
type: "button",
|
|
4427
|
+
className: GitHistoryPanel_module_default.cnGhost,
|
|
4428
|
+
onClick: onClose,
|
|
4429
|
+
children: t("cancel")
|
|
4430
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4431
|
+
type: "button",
|
|
4432
|
+
className: GitHistoryPanel_module_default.cnSave,
|
|
4433
|
+
disabled: state.saving || state.urlError !== null || !state.writable,
|
|
4434
|
+
onClick: onSave,
|
|
4435
|
+
children: t("cardSave")
|
|
4436
|
+
})]
|
|
4437
|
+
})
|
|
4438
|
+
]
|
|
4439
|
+
});
|
|
4440
|
+
}
|
|
4441
|
+
//#endregion
|
|
4119
4442
|
//#region src/client/panel/HistorySection.tsx
|
|
4120
4443
|
/**
|
|
4121
4444
|
* HistorySection: the panel's third block — the commit list. One row shows
|
|
@@ -4656,7 +4979,7 @@ window.__ModuleLoader__.load({
|
|
|
4656
4979
|
/** Data owner for the three sections. Remounts when `cwd` changes (the page
|
|
4657
4980
|
* keys the mount by cwd), so no state crosses a repository boundary. */
|
|
4658
4981
|
const GitHistoryPanelData = (0, react.forwardRef)(function GitHistoryPanelData(props, ref) {
|
|
4659
|
-
const { cwd, model, t, pushToast } = props;
|
|
4982
|
+
const { cwd, model, t, pushToast, accelOn } = props;
|
|
4660
4983
|
const [status, setStatus] = (0, react.useState)(null);
|
|
4661
4984
|
const [statusError, setStatusError] = (0, react.useState)();
|
|
4662
4985
|
const [changes, setChanges] = (0, react.useState)([]);
|
|
@@ -4850,12 +5173,19 @@ window.__ModuleLoader__.load({
|
|
|
4850
5173
|
pushToast,
|
|
4851
5174
|
t
|
|
4852
5175
|
]);
|
|
5176
|
+
/** Fetch/update failure toast text: the git failure line, plus the
|
|
5177
|
+
* mirror-unavailable hint while cn-accelerate is on (push is never
|
|
5178
|
+
* accelerated, so its failures stay hint-free). */
|
|
5179
|
+
const syncFailureText = (0, react.useCallback)((message) => {
|
|
5180
|
+
const generic = t("errorGeneric", { message });
|
|
5181
|
+
return accelOn() ? `${generic} ${t("accel.mirrorDownHint")}` : generic;
|
|
5182
|
+
}, [accelOn, t]);
|
|
4853
5183
|
const onFetch = (0, react.useCallback)(() => {
|
|
4854
5184
|
setSyncBusy(true);
|
|
4855
5185
|
(async () => {
|
|
4856
5186
|
try {
|
|
4857
5187
|
const result = await requestFetch(cwd);
|
|
4858
|
-
if (!result.ok) pushToast(
|
|
5188
|
+
if (!result.ok) pushToast(syncFailureText(result.error), "error");
|
|
4859
5189
|
else {
|
|
4860
5190
|
pushToast(t("fetchDone"), "success");
|
|
4861
5191
|
emitGitMutated();
|
|
@@ -4869,6 +5199,7 @@ window.__ModuleLoader__.load({
|
|
|
4869
5199
|
cwd,
|
|
4870
5200
|
loadAll,
|
|
4871
5201
|
pushToast,
|
|
5202
|
+
syncFailureText,
|
|
4872
5203
|
t
|
|
4873
5204
|
]);
|
|
4874
5205
|
const onUpdate = (0, react.useCallback)(() => {
|
|
@@ -4876,7 +5207,7 @@ window.__ModuleLoader__.load({
|
|
|
4876
5207
|
(async () => {
|
|
4877
5208
|
try {
|
|
4878
5209
|
const result = await requestUpdate(cwd);
|
|
4879
|
-
if (!result.ok) pushToast(
|
|
5210
|
+
if (!result.ok) pushToast(syncFailureText(result.error), "error");
|
|
4880
5211
|
else {
|
|
4881
5212
|
pushToast(result.updated ? t("updateDone", { branch: result.branch }) : t("updateUpToDate"), "success");
|
|
4882
5213
|
emitGitMutated();
|
|
@@ -4890,6 +5221,7 @@ window.__ModuleLoader__.load({
|
|
|
4890
5221
|
cwd,
|
|
4891
5222
|
loadAll,
|
|
4892
5223
|
pushToast,
|
|
5224
|
+
syncFailureText,
|
|
4893
5225
|
t
|
|
4894
5226
|
]);
|
|
4895
5227
|
const onPush = (0, react.useCallback)(() => {
|
|
@@ -5098,7 +5430,7 @@ window.__ModuleLoader__.load({
|
|
|
5098
5430
|
/** The registered better-sidebar tab's content: chrome + data owner, keyed
|
|
5099
5431
|
* by cwd so a session switch starts every section from scratch. */
|
|
5100
5432
|
function GitPanelTab(props) {
|
|
5101
|
-
const { ctx, scope, t } = props;
|
|
5433
|
+
const { ctx, scope, t, accel } = props;
|
|
5102
5434
|
const subscribe = (0, react.useCallback)((listener) => ctx.sessions.list.subscribe(listener), [ctx]);
|
|
5103
5435
|
const cwd = (0, react.useSyncExternalStore)(subscribe, () => scope.cwd ?? ctx.sessions.list.getSnapshot().byId[scope.sessionId]?.cwd);
|
|
5104
5436
|
const model = (0, react.useSyncExternalStore)(subscribe, () => {
|
|
@@ -5117,6 +5449,13 @@ window.__ModuleLoader__.load({
|
|
|
5117
5449
|
tone
|
|
5118
5450
|
}]);
|
|
5119
5451
|
}, []);
|
|
5452
|
+
const accelState = (0, react.useSyncExternalStore)(accel.store.subscribe, accel.store.getSnapshot);
|
|
5453
|
+
const [accelOpen, setAccelOpen] = (0, react.useState)(false);
|
|
5454
|
+
const accelOn = (0, react.useCallback)(() => accel.store.getSnapshot().enabled, [accel]);
|
|
5455
|
+
const closeAccel = (0, react.useCallback)(() => {
|
|
5456
|
+
accel.discardAccel();
|
|
5457
|
+
setAccelOpen(false);
|
|
5458
|
+
}, [accel]);
|
|
5120
5459
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5121
5460
|
className: GitHistoryPanel_module_default.page,
|
|
5122
5461
|
children: [
|
|
@@ -5128,9 +5467,35 @@ window.__ModuleLoader__.load({
|
|
|
5128
5467
|
className: GitHistoryPanel_module_default.repoName,
|
|
5129
5468
|
children: cwd
|
|
5130
5469
|
})
|
|
5131
|
-
}) : null, /* @__PURE__ */ (0, react_jsx_runtime.
|
|
5470
|
+
}) : null, /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5132
5471
|
className: GitHistoryPanel_module_default.topbarActions,
|
|
5133
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.
|
|
5472
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5473
|
+
className: GitHistoryPanel_module_default.cnWrap,
|
|
5474
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
5475
|
+
type: "button",
|
|
5476
|
+
className: accelState.enabled ? GitHistoryPanel_module_default.cnBtnOn : GitHistoryPanel_module_default.cnBtn,
|
|
5477
|
+
title: t("panel.cnAccelerate"),
|
|
5478
|
+
"aria-label": t("panel.cnAccelerate"),
|
|
5479
|
+
"aria-expanded": accelOpen,
|
|
5480
|
+
onClick: () => accelOpen ? closeAccel() : setAccelOpen(true),
|
|
5481
|
+
children: [
|
|
5482
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5483
|
+
"aria-hidden": "true",
|
|
5484
|
+
children: "⚡"
|
|
5485
|
+
}),
|
|
5486
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("panel.cnAccelerate") }),
|
|
5487
|
+
accelState.enabled ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5488
|
+
className: GitHistoryPanel_module_default.cnDot,
|
|
5489
|
+
"aria-hidden": "true"
|
|
5490
|
+
}) : null
|
|
5491
|
+
]
|
|
5492
|
+
}), accelOpen ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CnAcceleratePopover, {
|
|
5493
|
+
accel,
|
|
5494
|
+
t,
|
|
5495
|
+
pushToast,
|
|
5496
|
+
onClose: closeAccel
|
|
5497
|
+
}) : null]
|
|
5498
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
5134
5499
|
type: "button",
|
|
5135
5500
|
className: GitHistoryPanel_module_default.refreshBtn,
|
|
5136
5501
|
disabled: cwd === void 0,
|
|
@@ -5141,7 +5506,7 @@ window.__ModuleLoader__.load({
|
|
|
5141
5506
|
size: 14,
|
|
5142
5507
|
"aria-hidden": "true"
|
|
5143
5508
|
})
|
|
5144
|
-
})
|
|
5509
|
+
})]
|
|
5145
5510
|
})]
|
|
5146
5511
|
}),
|
|
5147
5512
|
cwd === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
@@ -5152,7 +5517,8 @@ window.__ModuleLoader__.load({
|
|
|
5152
5517
|
cwd,
|
|
5153
5518
|
model,
|
|
5154
5519
|
t,
|
|
5155
|
-
pushToast
|
|
5520
|
+
pushToast,
|
|
5521
|
+
accelOn
|
|
5156
5522
|
}, cwd),
|
|
5157
5523
|
toasts.map((toast) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Toast, {
|
|
5158
5524
|
text: toast.text,
|
|
@@ -5760,7 +6126,7 @@ window.__ModuleLoader__.load({
|
|
|
5760
6126
|
}
|
|
5761
6127
|
//#endregion
|
|
5762
6128
|
//#region \0git-worktree-css:/home/runner/work/dsh-git-branch/dsh-git-branch/src/client/GitWorktreeCard.module.css?inline
|
|
5763
|
-
const css = "/* git-worktree bundle configuration on the Plugins page. The page owns the\n * chrome (title, icon, breadcrumb); this component renders either the\n * bundle-row summary line or the configuration form inside the page. Visual\n * language mirrors the settings section's own card chrome (layer fills,\n * footer buttons); tokens only, no literal colors. */\n\n/* The one-line preview on the bundle's row. */\n.summary_1281848784 {\n margin: 0;\n font-size: 13px;\n line-height: 1.5;\n color: var(--dsw-alias-label-tertiary);\n}\n\n/* The configuration form inside the bundle's page. */\n.page_1281848784 {\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n\n.note_1281848784 {\n margin: 0;\n font-size: 12px;\n line-height: 1.5;\n color: var(--dsw-alias-label-tertiary);\n}\n\n.field_1281848784 {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.fieldLabel_1281848784 {\n font-size: 12px;\n line-height: 1.5;\n color: var(--dsw-alias-label-secondary);\n}\n\n/* The read-only legacy storage root: text (not an input — the location is\n * historical and cannot be edited) plus its \"unused\" state on one row. */\n.rootRow_1281848784 {\n display: flex;\n align-items: baseline;\n gap: 8px;\n min-width: 0;\n}\n\n.rootValue_1281848784 {\n font-size: 13px;\n line-height: 1.5;\n color: var(--dsw-alias-label-primary);\n overflow-wrap: anywhere;\n}\n\n.rootUnused_1281848784 {\n flex: none;\n font-size: 11px;\n line-height: 1.5;\n color: var(--dsw-alias-label-tertiary);\n border: 0.5px solid var(--dsw-alias-border-l4);\n border-radius: 4px;\n padding: 0 5px;\n}\n\n.hint_1281848784 {\n margin: 0;\n font-size: 12px;\n line-height: 1.5;\n color: var(--dsw-alias-label-tertiary);\n}\n\n.footer_1281848784 {\n display: flex;\n align-items: center;\n justify-content: flex-end;\n gap: 8px;\n padding: 8px 0 4px;\n border-top: 0.5px solid var(--dsw-alias-border-l2);\n}\n\n.failed_1281848784 {\n flex: 1;\n min-width: 0;\n margin: 0;\n font-size: 12px;\n line-height: 1.5;\n color: var(--dsw-alias-label-error);\n}\n\n.discard_1281848784,\n.save_1281848784 {\n appearance: none;\n border: 0.5px solid transparent;\n border-radius: 8px;\n padding: 5px 14px;\n font: inherit;\n font-size: 13px;\n line-height: 1.5;\n cursor: pointer;\n}\n\n.discard_1281848784 {\n border-color: var(--dsw-alias-border-l2);\n background: none;\n color: var(--dsw-alias-label-secondary);\n}\n\n.discard_1281848784:hover:not(:disabled) {\n color: var(--dsw-alias-label-primary);\n border-color: var(--dsw-alias-label-dimmed);\n}\n\n.save_1281848784 {\n background: var(--dsw-alias-label-primary);\n color: var(--dsw-alias-bg-layer-3);\n}\n\n.discard_1281848784:disabled,\n.save_1281848784:disabled {\n opacity: 0.4;\n cursor: default;\n}\n\n.discard_1281848784:focus-visible,\n.save_1281848784:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n/* Sidebar-grouping switch: title + hint + caution beside a checkbox. */\n.toggleRow_1281848784 {\n margin-top: 10px;\n flex-direction: row;\n align-items: flex-start;\n gap: 10px;\n}\n\n.toggleText_1281848784 {\n flex: 1;\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n\n.toggleLabel_1281848784 {\n color: var(--dsw-alias-label-primary);\n font-weight: 500;\n}\n\n.toggleMark_1281848784 {\n margin-left: 4px;\n font-weight: 400;\n color: var(--dsw-alias-label-tertiary);\n}\n\n.toggleHint_1281848784 {\n color: var(--dsw-alias-label-tertiary);\n font-size: 12px;\n line-height: 18px;\n}\n\n.toggleControl_1281848784 {\n display: flex;\n align-items: center;\n gap: 6px;\n flex: none;\n margin-top: 2px;\n}\n\n.spinner_1281848784 {\n display: flex;\n color: var(--dsw-alias-label-tertiary);\n animation: spin 0.7s linear infinite;\n}\n\n@keyframes spin {\n to { transform: rotate(360deg); }\n}\n\n.toggle_1281848784 {\n width: 16px;\n height: 16px;\n accent-color: var(--dsw-alias-brand-primary);\n cursor: pointer;\n}\n\n.toggle_1281848784:disabled {\n cursor: progress;\n}\n\n/* Manager entry row: title + hint on the left (the toggle rows' text\n * column), the launcher button on the right — the same posture as the\n * grouping switch, minus the checkbox. (.field_1281848784 is columnar; the row\n * direction is restated here.) */\n.manageRow_1281848784 {\n display: flex;\n flex-direction: row;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n}\n\n.manage_1281848784 {\n appearance: none;\n border: 0.5px solid var(--dsw-alias-border-l4);\n border-radius: 8px;\n background: none;\n font: inherit;\n font-size: 13px;\n color: var(--dsw-alias-label-primary);\n padding: 6px 12px;\n cursor: pointer;\n}\n\n.manage_1281848784:hover {\n border-color: var(--dsw-alias-brand-primary);\n}\n\n.manage_1281848784:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: -2px;\n}\n\n/* Keep-cap row: the number input rides the toggle row's title column, and\n * stays disabled while the auto-prune switch is off (the cap means nothing\n * without the prune that enforces it). */\n.keepRow_1281848784 {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.keepControl_1281848784 {\n display: flex;\n justify-content: flex-start;\n}\n\n.keepInput_1281848784 {\n width: 96px;\n appearance: none;\n border: 0.5px solid var(--dsw-alias-border-l4);\n border-radius: 8px;\n background: var(--dsw-alias-bg-layer-2);\n font: inherit;\n font-size: 13px;\n color: var(--dsw-alias-label-primary);\n padding: 6px 10px;\n}\n\n.keepInput_1281848784:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: -2px;\n}\n\n.keepInput_1281848784:disabled {\n opacity: 0.45;\n cursor: not-allowed;\n}\n\n.keepBad_1281848784 {\n font-size: 12px;\n color: var(--dsw-alias-label-danger, #d64545);\n}\n\n.copyFilesRow_1281848784 {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.copyFilesInput_1281848784 {\n width: 100%;\n box-sizing: border-box;\n appearance: none;\n border: 0.5px solid var(--dsw-alias-border-l4);\n border-radius: 8px;\n background: var(--dsw-alias-bg-layer-2);\n font: inherit;\n font-family: monospace;\n font-size: 12px;\n line-height: 1.5;\n color: var(--dsw-alias-label-primary);\n padding: 6px 10px;\n resize: vertical;\n min-height: 64px;\n}\n\n.copyFilesInput_1281848784:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: -2px;\n}\n\n.copyFilesInput_1281848784:disabled {\n opacity: 0.45;\n cursor: not-allowed;\n}\n\n/* Write-through switches flip optimistically, so a rejected write shows up\n * as the control snapping back. This note sits in the switch's own text\n * column and says which one did not take. */\n.switchBad_1281848784 {\n font-size: 12px;\n line-height: 16px;\n color: var(--dsw-alias-label-danger, #d64545);\n}\n\n/* The auto-prune audit trail (清理记录): newest first, one run per line —\n * relative time, the removed directories with their belonging, then the\n * run's exceptions in the secondary tone. */\n.historyRow_1281848784 {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.history_1281848784 {\n margin: 0;\n padding: 0;\n list-style: none;\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.historyRun_1281848784 {\n display: flex;\n flex-direction: column;\n gap: 2px;\n padding: 4px 8px;\n border: 0.5px solid color-mix(in srgb, currentColor 12%, transparent);\n border-radius: 6px;\n}\n\n.historyTime_1281848784 {\n font-size: 12px;\n font-weight: 600;\n color: var(--dsw-alias-label-primary);\n}\n\n.historyLine_1281848784 {\n font-size: 12px;\n line-height: 16px;\n color: var(--dsw-alias-label-secondary);\n overflow-wrap: anywhere;\n}\n\n/* The fold toggle. A link, not a button chrome: it reveals rows in place,\n * it does not act on anything. */\n.historyMore_1281848784 {\n align-self: flex-start;\n padding: 0;\n border: none;\n background: none;\n font: inherit;\n font-size: 12px;\n line-height: 16px;\n color: var(--dsw-alias-label-link, var(--dsw-alias-label-secondary));\n cursor: pointer;\n}\n\n.historyMore_1281848784:hover {\n text-decoration: underline;\n}\n";
|
|
6129
|
+
const css = "/* git-worktree bundle configuration on the Plugins page. The page owns the\n * chrome (title, icon, breadcrumb); this component renders either the\n * bundle-row summary line or the configuration form inside the page. Visual\n * language mirrors the settings section's own card chrome (layer fills,\n * footer buttons); tokens only, no literal colors. */\n\n/* The one-line preview on the bundle's row. */\n.summary_1281848784 {\n margin: 0;\n font-size: 13px;\n line-height: 1.5;\n color: var(--dsw-alias-label-tertiary);\n}\n\n/* The configuration form inside the bundle's page. */\n.page_1281848784 {\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n\n.note_1281848784 {\n margin: 0;\n font-size: 12px;\n line-height: 1.5;\n color: var(--dsw-alias-label-tertiary);\n}\n\n.field_1281848784 {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.fieldLabel_1281848784 {\n font-size: 12px;\n line-height: 1.5;\n color: var(--dsw-alias-label-secondary);\n}\n\n/* The read-only legacy storage root: text (not an input — the location is\n * historical and cannot be edited) plus its \"unused\" state on one row. */\n.rootRow_1281848784 {\n display: flex;\n align-items: baseline;\n gap: 8px;\n min-width: 0;\n}\n\n.rootValue_1281848784 {\n font-size: 13px;\n line-height: 1.5;\n color: var(--dsw-alias-label-primary);\n overflow-wrap: anywhere;\n}\n\n.rootUnused_1281848784 {\n flex: none;\n font-size: 11px;\n line-height: 1.5;\n color: var(--dsw-alias-label-tertiary);\n border: 0.5px solid var(--dsw-alias-border-l4);\n border-radius: 4px;\n padding: 0 5px;\n}\n\n.hint_1281848784 {\n margin: 0;\n font-size: 12px;\n line-height: 1.5;\n color: var(--dsw-alias-label-tertiary);\n}\n\n.footer_1281848784 {\n display: flex;\n align-items: center;\n justify-content: flex-end;\n gap: 8px;\n padding: 8px 0 4px;\n border-top: 0.5px solid var(--dsw-alias-border-l2);\n}\n\n.failed_1281848784 {\n flex: 1;\n min-width: 0;\n margin: 0;\n font-size: 12px;\n line-height: 1.5;\n color: var(--dsw-alias-label-error);\n}\n\n.discard_1281848784,\n.save_1281848784 {\n appearance: none;\n border: 0.5px solid transparent;\n border-radius: 8px;\n padding: 5px 14px;\n font: inherit;\n font-size: 13px;\n line-height: 1.5;\n cursor: pointer;\n}\n\n.discard_1281848784 {\n border-color: var(--dsw-alias-border-l2);\n background: none;\n color: var(--dsw-alias-label-secondary);\n}\n\n.discard_1281848784:hover:not(:disabled) {\n color: var(--dsw-alias-label-primary);\n border-color: var(--dsw-alias-label-dimmed);\n}\n\n.save_1281848784 {\n background: var(--dsw-alias-label-primary);\n color: var(--dsw-alias-bg-layer-3);\n}\n\n.discard_1281848784:disabled,\n.save_1281848784:disabled {\n opacity: 0.4;\n cursor: default;\n}\n\n.discard_1281848784:focus-visible,\n.save_1281848784:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n/* Sidebar-grouping switch: title + hint + caution beside a checkbox. */\n.toggleRow_1281848784 {\n margin-top: 10px;\n flex-direction: row;\n align-items: flex-start;\n gap: 10px;\n}\n\n.toggleText_1281848784 {\n flex: 1;\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n\n.toggleLabel_1281848784 {\n color: var(--dsw-alias-label-primary);\n font-weight: 500;\n}\n\n.toggleMark_1281848784 {\n margin-left: 4px;\n font-weight: 400;\n color: var(--dsw-alias-label-tertiary);\n}\n\n.toggleHint_1281848784 {\n color: var(--dsw-alias-label-tertiary);\n font-size: 12px;\n line-height: 18px;\n}\n\n.toggleControl_1281848784 {\n display: flex;\n align-items: center;\n gap: 6px;\n flex: none;\n margin-top: 2px;\n}\n\n.spinner_1281848784 {\n display: flex;\n color: var(--dsw-alias-label-tertiary);\n animation: spin 0.7s linear infinite;\n}\n\n@keyframes spin {\n to { transform: rotate(360deg); }\n}\n\n.toggle_1281848784 {\n width: 16px;\n height: 16px;\n accent-color: var(--dsw-alias-brand-primary);\n cursor: pointer;\n}\n\n.toggle_1281848784:disabled {\n cursor: progress;\n}\n\n/* Manager entry row: title + hint on the left (the toggle rows' text\n * column), the launcher button on the right — the same posture as the\n * grouping switch, minus the checkbox. (.field_1281848784 is columnar; the row\n * direction is restated here.) */\n.manageRow_1281848784 {\n display: flex;\n flex-direction: row;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n}\n\n.manage_1281848784 {\n appearance: none;\n border: 0.5px solid var(--dsw-alias-border-l4);\n border-radius: 8px;\n background: none;\n font: inherit;\n font-size: 13px;\n color: var(--dsw-alias-label-primary);\n padding: 6px 12px;\n cursor: pointer;\n}\n\n.manage_1281848784:hover {\n border-color: var(--dsw-alias-brand-primary);\n}\n\n.manage_1281848784:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: -2px;\n}\n\n/* Keep-cap row: the number input rides the toggle row's title column, and\n * stays disabled while the auto-prune switch is off (the cap means nothing\n * without the prune that enforces it). */\n.keepRow_1281848784 {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.keepControl_1281848784 {\n display: flex;\n justify-content: flex-start;\n}\n\n.keepInput_1281848784 {\n width: 96px;\n appearance: none;\n border: 0.5px solid var(--dsw-alias-border-l4);\n border-radius: 8px;\n background: var(--dsw-alias-bg-layer-2);\n font: inherit;\n font-size: 13px;\n color: var(--dsw-alias-label-primary);\n padding: 6px 10px;\n}\n\n.keepInput_1281848784:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: -2px;\n}\n\n.keepInput_1281848784:disabled {\n opacity: 0.45;\n cursor: not-allowed;\n}\n\n.keepBad_1281848784 {\n font-size: 12px;\n color: var(--dsw-alias-label-danger, #d64545);\n}\n\n.copyFilesRow_1281848784 {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.copyFilesInput_1281848784 {\n width: 100%;\n box-sizing: border-box;\n appearance: none;\n border: 0.5px solid var(--dsw-alias-border-l4);\n border-radius: 8px;\n background: var(--dsw-alias-bg-layer-2);\n font: inherit;\n font-family: monospace;\n font-size: 12px;\n line-height: 1.5;\n color: var(--dsw-alias-label-primary);\n padding: 6px 10px;\n resize: vertical;\n min-height: 64px;\n}\n\n.copyFilesInput_1281848784:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: -2px;\n}\n\n.copyFilesInput_1281848784:disabled {\n opacity: 0.45;\n cursor: not-allowed;\n}\n\n/* Cn-accelerate URL row: a single-line URL text field (the keep row's input\n * posture at full width). The invalid-draft line reuses .keepBad_1281848784. */\n.accelInput_1281848784 {\n width: 100%;\n box-sizing: border-box;\n appearance: none;\n border: 0.5px solid var(--dsw-alias-border-l4);\n border-radius: 8px;\n background: var(--dsw-alias-bg-layer-2);\n font: inherit;\n font-family: monospace;\n font-size: 12px;\n color: var(--dsw-alias-label-primary);\n padding: 6px 10px;\n}\n\n.accelInput_1281848784:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: -2px;\n}\n\n.accelInput_1281848784:disabled {\n opacity: 0.45;\n cursor: not-allowed;\n}\n\n/* Write-through switches flip optimistically, so a rejected write shows up\n * as the control snapping back. This note sits in the switch's own text\n * column and says which one did not take. */\n.switchBad_1281848784 {\n font-size: 12px;\n line-height: 16px;\n color: var(--dsw-alias-label-danger, #d64545);\n}\n\n/* The auto-prune audit trail (清理记录): newest first, one run per line —\n * relative time, the removed directories with their belonging, then the\n * run's exceptions in the secondary tone. */\n.historyRow_1281848784 {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.history_1281848784 {\n margin: 0;\n padding: 0;\n list-style: none;\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.historyRun_1281848784 {\n display: flex;\n flex-direction: column;\n gap: 2px;\n padding: 4px 8px;\n border: 0.5px solid color-mix(in srgb, currentColor 12%, transparent);\n border-radius: 6px;\n}\n\n.historyTime_1281848784 {\n font-size: 12px;\n font-weight: 600;\n color: var(--dsw-alias-label-primary);\n}\n\n.historyLine_1281848784 {\n font-size: 12px;\n line-height: 16px;\n color: var(--dsw-alias-label-secondary);\n overflow-wrap: anywhere;\n}\n\n/* The fold toggle. A link, not a button chrome: it reveals rows in place,\n * it does not act on anything. */\n.historyMore_1281848784 {\n align-self: flex-start;\n padding: 0;\n border: none;\n background: none;\n font: inherit;\n font-size: 12px;\n line-height: 16px;\n color: var(--dsw-alias-label-link, var(--dsw-alias-label-secondary));\n cursor: pointer;\n}\n\n.historyMore_1281848784:hover {\n text-decoration: underline;\n}\n";
|
|
5764
6130
|
const tagId = "dsh-git-branch-manage/GitWorktreeCard.module.css";
|
|
5765
6131
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=\"" + tagId + "\"]") === null) {
|
|
5766
6132
|
const tag = document.createElement("style");
|
|
@@ -5799,6 +6165,7 @@ window.__ModuleLoader__.load({
|
|
|
5799
6165
|
"keepBad": "keepBad_1281848784",
|
|
5800
6166
|
"copyFilesRow": "copyFilesRow_1281848784",
|
|
5801
6167
|
"copyFilesInput": "copyFilesInput_1281848784",
|
|
6168
|
+
"accelInput": "accelInput_1281848784",
|
|
5802
6169
|
"switchBad": "switchBad_1281848784",
|
|
5803
6170
|
"historyRow": "historyRow_1281848784",
|
|
5804
6171
|
"history": "history_1281848784",
|
|
@@ -5875,13 +6242,14 @@ window.__ModuleLoader__.load({
|
|
|
5875
6242
|
const shownHistory = historyAll ? history : history.slice(0, HISTORY_PREVIEW);
|
|
5876
6243
|
const { t } = props;
|
|
5877
6244
|
const state = props.useGitWorktreeCard((snapshot) => snapshot);
|
|
6245
|
+
const accelState = props.useGitWorktreeAccel((snapshot) => snapshot);
|
|
5878
6246
|
if (!state.available) return null;
|
|
5879
6247
|
if (!onPage) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
5880
6248
|
className: GitWorktreeCard_module_default.summary,
|
|
5881
6249
|
children: t("cardDescription")
|
|
5882
6250
|
});
|
|
5883
6251
|
const lockInput = !state.writable;
|
|
5884
|
-
const lockActions = !state.dirty || state.saving || !state.keepWorktreesValid;
|
|
6252
|
+
const lockActions = !state.dirty && !accelState.dirty || state.saving || accelState.saving || !state.keepWorktreesValid || accelState.urlError !== null;
|
|
5885
6253
|
const legacyRootText = storage?.legacyRoot ?? (state.rootDir === "" ? "—" : state.rootDir);
|
|
5886
6254
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5887
6255
|
className: GitWorktreeCard_module_default.page,
|
|
@@ -6055,6 +6423,56 @@ window.__ModuleLoader__.load({
|
|
|
6055
6423
|
})
|
|
6056
6424
|
]
|
|
6057
6425
|
}),
|
|
6426
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
6427
|
+
className: `${GitWorktreeCard_module_default.field} ${GitWorktreeCard_module_default.toggleRow}`,
|
|
6428
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
6429
|
+
className: GitWorktreeCard_module_default.toggleText,
|
|
6430
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6431
|
+
className: GitWorktreeCard_module_default.toggleLabel,
|
|
6432
|
+
children: t("panel.cnAccelerate")
|
|
6433
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6434
|
+
className: GitWorktreeCard_module_default.toggleHint,
|
|
6435
|
+
children: t("accel.hint")
|
|
6436
|
+
})]
|
|
6437
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6438
|
+
className: GitWorktreeCard_module_default.toggleControl,
|
|
6439
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
6440
|
+
className: GitWorktreeCard_module_default.toggle,
|
|
6441
|
+
type: "checkbox",
|
|
6442
|
+
disabled: lockInput || accelState.saving,
|
|
6443
|
+
checked: accelState.enabled,
|
|
6444
|
+
onChange: (event) => {
|
|
6445
|
+
props.setAccelEnabled(event.target.checked);
|
|
6446
|
+
}
|
|
6447
|
+
})
|
|
6448
|
+
})]
|
|
6449
|
+
}),
|
|
6450
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
6451
|
+
className: `${GitWorktreeCard_module_default.field} ${GitWorktreeCard_module_default.copyFilesRow}`,
|
|
6452
|
+
htmlFor: "git-worktree-card-accel-url",
|
|
6453
|
+
children: [
|
|
6454
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6455
|
+
className: GitWorktreeCard_module_default.fieldLabel,
|
|
6456
|
+
children: t("accel.urlLabel")
|
|
6457
|
+
}),
|
|
6458
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
6459
|
+
id: "git-worktree-card-accel-url",
|
|
6460
|
+
className: GitWorktreeCard_module_default.accelInput,
|
|
6461
|
+
type: "text",
|
|
6462
|
+
spellCheck: false,
|
|
6463
|
+
disabled: lockInput || accelState.saving,
|
|
6464
|
+
value: accelState.urlText,
|
|
6465
|
+
onChange: (event) => {
|
|
6466
|
+
props.editAccelUrl(event.target.value);
|
|
6467
|
+
}
|
|
6468
|
+
}),
|
|
6469
|
+
accelState.urlError !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6470
|
+
className: GitWorktreeCard_module_default.keepBad,
|
|
6471
|
+
role: "alert",
|
|
6472
|
+
children: accelState.urlError === "empty" ? t("accel.urlEmpty") : t("accel.urlInvalid")
|
|
6473
|
+
}) : null
|
|
6474
|
+
]
|
|
6475
|
+
}),
|
|
6058
6476
|
history.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6059
6477
|
className: `${GitWorktreeCard_module_default.field} ${GitWorktreeCard_module_default.historyRow}`,
|
|
6060
6478
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
@@ -6121,19 +6539,30 @@ window.__ModuleLoader__.load({
|
|
|
6121
6539
|
role: "status",
|
|
6122
6540
|
children: t("cardSaveFailed")
|
|
6123
6541
|
}) : null,
|
|
6542
|
+
accelState.failed ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
6543
|
+
className: GitWorktreeCard_module_default.failed,
|
|
6544
|
+
role: "status",
|
|
6545
|
+
children: t("accel.saveFailed")
|
|
6546
|
+
}) : null,
|
|
6124
6547
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
6125
6548
|
type: "button",
|
|
6126
6549
|
className: GitWorktreeCard_module_default.discard,
|
|
6127
6550
|
disabled: lockActions,
|
|
6128
|
-
onClick:
|
|
6551
|
+
onClick: () => {
|
|
6552
|
+
props.discard();
|
|
6553
|
+
props.discardAccel();
|
|
6554
|
+
},
|
|
6129
6555
|
children: t("cardDiscard")
|
|
6130
6556
|
}),
|
|
6131
6557
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
6132
6558
|
type: "button",
|
|
6133
6559
|
className: GitWorktreeCard_module_default.save,
|
|
6134
6560
|
disabled: lockActions,
|
|
6135
|
-
onClick:
|
|
6136
|
-
|
|
6561
|
+
onClick: () => {
|
|
6562
|
+
props.save();
|
|
6563
|
+
props.saveAccel();
|
|
6564
|
+
},
|
|
6565
|
+
children: t(state.saving || accelState.saving ? "cardSaving" : "cardSave")
|
|
6137
6566
|
})
|
|
6138
6567
|
]
|
|
6139
6568
|
})
|
|
@@ -6330,7 +6759,18 @@ window.__ModuleLoader__.load({
|
|
|
6330
6759
|
"panel.stCopy": "copied",
|
|
6331
6760
|
"panel.stType": "type changed",
|
|
6332
6761
|
"panel.stUntrack": "untracked",
|
|
6333
|
-
"panel.stUnmerged": "conflict"
|
|
6762
|
+
"panel.stUnmerged": "conflict",
|
|
6763
|
+
"panel.cnAccelerate": "China acceleration",
|
|
6764
|
+
"accel.enable": "Enable acceleration",
|
|
6765
|
+
"accel.hint": "Accelerates https github.com remotes for fetch/update only; push is never proxied. The mirror sees repository content — do not use it for private or sensitive repositories.",
|
|
6766
|
+
"accel.urlLabel": "Acceleration URL",
|
|
6767
|
+
"accel.restoreDefault": "Restore default",
|
|
6768
|
+
"accel.urlInvalid": "Enter an http(s):// URL",
|
|
6769
|
+
"accel.urlEmpty": "Enter an acceleration URL",
|
|
6770
|
+
"accel.saveFailed": "The change did not save. Try again.",
|
|
6771
|
+
"accel.savedOn": "China acceleration enabled: {url}",
|
|
6772
|
+
"accel.savedOff": "China acceleration disabled",
|
|
6773
|
+
"accel.mirrorDownHint": "If the mirror is down, disable acceleration or change the URL"
|
|
6334
6774
|
};
|
|
6335
6775
|
/** 中文词典。 */
|
|
6336
6776
|
const zh = {
|
|
@@ -6520,7 +6960,18 @@ window.__ModuleLoader__.load({
|
|
|
6520
6960
|
"panel.stCopy": "复制",
|
|
6521
6961
|
"panel.stType": "类型变更",
|
|
6522
6962
|
"panel.stUntrack": "未跟踪",
|
|
6523
|
-
"panel.stUnmerged": "冲突"
|
|
6963
|
+
"panel.stUnmerged": "冲突",
|
|
6964
|
+
"panel.cnAccelerate": "国内加速",
|
|
6965
|
+
"accel.enable": "启用加速",
|
|
6966
|
+
"accel.hint": "仅加速 https 的 github.com 仓库地址,作用于提取/更新;推送不走加速。第三方加速站可看到仓库内容,请勿用于私有/敏感仓库。",
|
|
6967
|
+
"accel.urlLabel": "加速地址",
|
|
6968
|
+
"accel.restoreDefault": "恢复默认",
|
|
6969
|
+
"accel.urlInvalid": "请输入以 http(s):// 开头的地址",
|
|
6970
|
+
"accel.urlEmpty": "请输入加速地址",
|
|
6971
|
+
"accel.saveFailed": "保存未生效,请重试。",
|
|
6972
|
+
"accel.savedOn": "已启用国内加速:{url}",
|
|
6973
|
+
"accel.savedOff": "已关闭国内加速",
|
|
6974
|
+
"accel.mirrorDownHint": "若加速地址不可用,可停用国内加速或更换地址"
|
|
6524
6975
|
};
|
|
6525
6976
|
//#endregion
|
|
6526
6977
|
//#region src/client/index.ts
|
|
@@ -6771,6 +7222,11 @@ window.__ModuleLoader__.load({
|
|
|
6771
7222
|
});
|
|
6772
7223
|
const form = new CardForm(settingsForm);
|
|
6773
7224
|
const store = form.bind();
|
|
7225
|
+
const accelForm = new AccelForm(settingsForm);
|
|
7226
|
+
const accelFace = {
|
|
7227
|
+
store: accelForm.bind(),
|
|
7228
|
+
...accelForm.actions()
|
|
7229
|
+
};
|
|
6774
7230
|
const describeFace = ctx.configForms.describe();
|
|
6775
7231
|
let configDisposer;
|
|
6776
7232
|
const syncCardSeat = () => {
|
|
@@ -6781,8 +7237,12 @@ window.__ModuleLoader__.load({
|
|
|
6781
7237
|
key: PLUGIN_PACKAGE,
|
|
6782
7238
|
locale: NS,
|
|
6783
7239
|
inject: () => ({
|
|
6784
|
-
hooks: {
|
|
7240
|
+
hooks: {
|
|
7241
|
+
gitWorktreeCard: store,
|
|
7242
|
+
gitWorktreeAccel: accelFace.store
|
|
7243
|
+
},
|
|
6785
7244
|
...form.actions(),
|
|
7245
|
+
...accelForm.actions(),
|
|
6786
7246
|
manager: managerFace()
|
|
6787
7247
|
})
|
|
6788
7248
|
}, GitWorktreeCard));
|
|
@@ -6814,7 +7274,8 @@ window.__ModuleLoader__.load({
|
|
|
6814
7274
|
component: (tabProps) => (0, react.createElement)(GitPanelTab, {
|
|
6815
7275
|
ctx: betterCtx,
|
|
6816
7276
|
scope: tabProps.scope,
|
|
6817
|
-
t: panelT
|
|
7277
|
+
t: panelT,
|
|
7278
|
+
accel: accelFace
|
|
6818
7279
|
})
|
|
6819
7280
|
}), "git-worktree: better-sidebar tab");
|
|
6820
7281
|
});
|
package/lib/git.d.ts
CHANGED
|
@@ -207,6 +207,20 @@ export declare function renameBranch(exec: Exec, repoRoot: string, name: string,
|
|
|
207
207
|
* @returns the name of the branch that was deleted.
|
|
208
208
|
*/
|
|
209
209
|
export declare function deleteBranch(exec: Exec, repoRoot: string, name: string): Promise<string>;
|
|
210
|
+
/**
|
|
211
|
+
* argv prefix for network READ commands when cn-accelerate is on: the
|
|
212
|
+
* per-command `url.<base>.insteadOf=https://github.com/` rewrite. Per-command
|
|
213
|
+
* `git -c` (before the subcommand) rather than the user's global git config —
|
|
214
|
+
* the plugin must never persist settings on the user's behalf, and one
|
|
215
|
+
* injected value cannot leak into unrelated git runs. Rewrites
|
|
216
|
+
* `https://github.com/o/r.git` → `<base>https://github.com/o/r.git`, which is
|
|
217
|
+
* how public mirror prefixes consume GitHub URLs.
|
|
218
|
+
* @param prefix - the accelerate URL (raw or normalized); undefined = off.
|
|
219
|
+
* @returns the `-c` pair to prepend, or `[]` when off or the value is
|
|
220
|
+
* unusable (an invalid value fails SAFE to a direct connection — config
|
|
221
|
+
* validation rejects such values at load; this is the defensive path).
|
|
222
|
+
*/
|
|
223
|
+
export declare function accelArgs(prefix: string | undefined): readonly string[];
|
|
210
224
|
/**
|
|
211
225
|
* Sync remote-tracking refs: fetch every remote and prune tracking branches
|
|
212
226
|
* the remotes no longer carry (the CLI equivalent of IDEA's "synchronize
|
|
@@ -215,8 +229,10 @@ export declare function deleteBranch(exec: Exec, repoRoot: string, name: string)
|
|
|
215
229
|
* executor's hard timeout as a GitError.
|
|
216
230
|
* @param exec - executor seam.
|
|
217
231
|
* @param repoRoot - main worktree directory (fetch is repository-wide).
|
|
232
|
+
* @param accelPrefix - optional cn-accelerate mirror prefix (see
|
|
233
|
+
* {@link accelArgs}); only the READ half of the network commands gets it.
|
|
218
234
|
*/
|
|
219
|
-
export declare function fetchAll(exec: Exec, repoRoot: string): Promise<void>;
|
|
235
|
+
export declare function fetchAll(exec: Exec, repoRoot: string, accelPrefix?: string): Promise<void>;
|
|
220
236
|
/** Outcome of {@link updateBranch}: the branch touched and whether it moved. */
|
|
221
237
|
export interface UpdateOutcome {
|
|
222
238
|
branch: string;
|
|
@@ -233,10 +249,12 @@ export interface UpdateOutcome {
|
|
|
233
249
|
* branch ITS worktree holds.
|
|
234
250
|
* @param exec - executor seam.
|
|
235
251
|
* @param cwd - directory whose checked-out branch is updated.
|
|
252
|
+
* @param accelPrefix - optional cn-accelerate mirror prefix; feeds ONLY the
|
|
253
|
+
* fetch phase ({@link fetchAll}) — the `--ff-only` merge is local.
|
|
236
254
|
* @returns the branch name and whether HEAD actually moved (false = the
|
|
237
255
|
* upstream already contained it).
|
|
238
256
|
*/
|
|
239
|
-
export declare function updateBranch(exec: Exec, cwd: string): Promise<UpdateOutcome>;
|
|
257
|
+
export declare function updateBranch(exec: Exec, cwd: string, accelPrefix?: string): Promise<UpdateOutcome>;
|
|
240
258
|
/** Outcome of {@link pushBranch}: the branch touched and whether it moved. */
|
|
241
259
|
export interface PushOutcome {
|
|
242
260
|
branch: string;
|
|
@@ -249,7 +267,11 @@ export interface PushOutcome {
|
|
|
249
267
|
* git's own message rather than silently picking one), and `--force` is
|
|
250
268
|
* never passed — a diverged remote refuses rather than losing commits.
|
|
251
269
|
* Runs at the queried directory's own toplevel, so a linked-worktree
|
|
252
|
-
* session pushes the branch ITS worktree holds.
|
|
270
|
+
* session pushes the branch ITS worktree holds. The cn-accelerate rewrite is
|
|
271
|
+
* deliberately NEVER injected here (no {@link accelArgs} parameter exists):
|
|
272
|
+
* public mirrors have no `receive-pack`, and rewriting would turn a
|
|
273
|
+
* diagnosable direct failure into an opaque mirror failure while leaking the
|
|
274
|
+
* pushed content to a third party.
|
|
253
275
|
* @param exec - executor seam.
|
|
254
276
|
* @param cwd - directory whose checked-out branch is pushed.
|
|
255
277
|
* @returns the branch and whether the remote-tracking ref changed. A branch
|
package/lib/git.js
CHANGED
|
@@ -7,7 +7,7 @@ import { execFile } from 'node:child_process';
|
|
|
7
7
|
import { existsSync, readdirSync } from 'node:fs';
|
|
8
8
|
import { appendFile, mkdir, readFile, rm } from 'node:fs/promises';
|
|
9
9
|
import { basename, dirname, isAbsolute, join, normalize, resolve } from 'node:path';
|
|
10
|
-
import { localBranchName } from './normalize.js';
|
|
10
|
+
import { accelerateUrlError, localBranchName, normalizeAccelerateUrl } from './normalize.js';
|
|
11
11
|
/** Failure of one git invocation: non-zero exit with the stderr text. */
|
|
12
12
|
export class GitError extends Error {
|
|
13
13
|
args;
|
|
@@ -464,6 +464,24 @@ export async function deleteBranch(exec, repoRoot, name) {
|
|
|
464
464
|
await git(exec, repoRoot, ['branch', '-d', name]);
|
|
465
465
|
return name;
|
|
466
466
|
}
|
|
467
|
+
/**
|
|
468
|
+
* argv prefix for network READ commands when cn-accelerate is on: the
|
|
469
|
+
* per-command `url.<base>.insteadOf=https://github.com/` rewrite. Per-command
|
|
470
|
+
* `git -c` (before the subcommand) rather than the user's global git config —
|
|
471
|
+
* the plugin must never persist settings on the user's behalf, and one
|
|
472
|
+
* injected value cannot leak into unrelated git runs. Rewrites
|
|
473
|
+
* `https://github.com/o/r.git` → `<base>https://github.com/o/r.git`, which is
|
|
474
|
+
* how public mirror prefixes consume GitHub URLs.
|
|
475
|
+
* @param prefix - the accelerate URL (raw or normalized); undefined = off.
|
|
476
|
+
* @returns the `-c` pair to prepend, or `[]` when off or the value is
|
|
477
|
+
* unusable (an invalid value fails SAFE to a direct connection — config
|
|
478
|
+
* validation rejects such values at load; this is the defensive path).
|
|
479
|
+
*/
|
|
480
|
+
export function accelArgs(prefix) {
|
|
481
|
+
if (prefix === undefined || accelerateUrlError(prefix) !== undefined)
|
|
482
|
+
return [];
|
|
483
|
+
return ['-c', `url.${normalizeAccelerateUrl(prefix)}.insteadOf=https://github.com/`];
|
|
484
|
+
}
|
|
467
485
|
/**
|
|
468
486
|
* Sync remote-tracking refs: fetch every remote and prune tracking branches
|
|
469
487
|
* the remotes no longer carry (the CLI equivalent of IDEA's "synchronize
|
|
@@ -472,9 +490,11 @@ export async function deleteBranch(exec, repoRoot, name) {
|
|
|
472
490
|
* executor's hard timeout as a GitError.
|
|
473
491
|
* @param exec - executor seam.
|
|
474
492
|
* @param repoRoot - main worktree directory (fetch is repository-wide).
|
|
493
|
+
* @param accelPrefix - optional cn-accelerate mirror prefix (see
|
|
494
|
+
* {@link accelArgs}); only the READ half of the network commands gets it.
|
|
475
495
|
*/
|
|
476
|
-
export async function fetchAll(exec, repoRoot) {
|
|
477
|
-
await git(exec, repoRoot, ['fetch', '--all', '--prune'], NETWORK_TIMEOUT_MS);
|
|
496
|
+
export async function fetchAll(exec, repoRoot, accelPrefix) {
|
|
497
|
+
await git(exec, repoRoot, [...accelArgs(accelPrefix), 'fetch', '--all', '--prune'], NETWORK_TIMEOUT_MS);
|
|
478
498
|
}
|
|
479
499
|
/**
|
|
480
500
|
* Update the CURRENT checkout: fetch every remote, then fast-forward the
|
|
@@ -487,11 +507,13 @@ export async function fetchAll(exec, repoRoot) {
|
|
|
487
507
|
* branch ITS worktree holds.
|
|
488
508
|
* @param exec - executor seam.
|
|
489
509
|
* @param cwd - directory whose checked-out branch is updated.
|
|
510
|
+
* @param accelPrefix - optional cn-accelerate mirror prefix; feeds ONLY the
|
|
511
|
+
* fetch phase ({@link fetchAll}) — the `--ff-only` merge is local.
|
|
490
512
|
* @returns the branch name and whether HEAD actually moved (false = the
|
|
491
513
|
* upstream already contained it).
|
|
492
514
|
*/
|
|
493
|
-
export async function updateBranch(exec, cwd) {
|
|
494
|
-
await fetchAll(exec, cwd);
|
|
515
|
+
export async function updateBranch(exec, cwd, accelPrefix) {
|
|
516
|
+
await fetchAll(exec, cwd, accelPrefix);
|
|
495
517
|
const before = (await git(exec, cwd, ['rev-parse', 'HEAD'])).trim();
|
|
496
518
|
await git(exec, cwd, ['merge', '--ff-only', '@{u}']);
|
|
497
519
|
const after = (await git(exec, cwd, ['rev-parse', 'HEAD'])).trim();
|
|
@@ -505,7 +527,11 @@ export async function updateBranch(exec, cwd) {
|
|
|
505
527
|
* git's own message rather than silently picking one), and `--force` is
|
|
506
528
|
* never passed — a diverged remote refuses rather than losing commits.
|
|
507
529
|
* Runs at the queried directory's own toplevel, so a linked-worktree
|
|
508
|
-
* session pushes the branch ITS worktree holds.
|
|
530
|
+
* session pushes the branch ITS worktree holds. The cn-accelerate rewrite is
|
|
531
|
+
* deliberately NEVER injected here (no {@link accelArgs} parameter exists):
|
|
532
|
+
* public mirrors have no `receive-pack`, and rewriting would turn a
|
|
533
|
+
* diagnosable direct failure into an opaque mirror failure while leaking the
|
|
534
|
+
* pushed content to a third party.
|
|
509
535
|
* @param exec - executor seam.
|
|
510
536
|
* @param cwd - directory whose checked-out branch is pushed.
|
|
511
537
|
* @returns the branch and whether the remote-tracking ref changed. A branch
|
package/lib/index.d.ts
CHANGED
|
@@ -25,6 +25,11 @@ export interface Config {
|
|
|
25
25
|
/** Post-create configuration files to copy when .worktreeinclude is absent;
|
|
26
26
|
* absent = none. */
|
|
27
27
|
postCreateCopyFiles?: Volatile<string[]> | string[];
|
|
28
|
+
/** cn-accelerate mirror rewrite for network READ commands (fetch/update);
|
|
29
|
+
* absent = off. PUSH is never rewritten. */
|
|
30
|
+
cnAccelerate?: Volatile<boolean> | boolean;
|
|
31
|
+
/** cn-accelerate mirror prefix; absent = the shipped default. */
|
|
32
|
+
cnAccelerateUrl?: Volatile<string> | string;
|
|
28
33
|
}
|
|
29
34
|
/** The shipped prune cap (whole storage root, valid git worktrees only). */
|
|
30
35
|
export declare const KEEP_WORKTREES_DEFAULT = 30;
|
|
@@ -36,6 +41,13 @@ export declare const KEEP_WORKTREES_DEFAULT = 30;
|
|
|
36
41
|
export declare const Config: z;
|
|
37
42
|
/** Reject stale or misspelled config keys before defaults can hide them. */
|
|
38
43
|
export declare function validateConfig(config: Config): void;
|
|
44
|
+
/**
|
|
45
|
+
* The accelerate prefix must be an http(s) URL without whitespace — it is
|
|
46
|
+
* concatenated with `https://github.com/` into one `url.<base>.insteadOf`
|
|
47
|
+
* config value, so a space would smuggle arguments and a non-http scheme is
|
|
48
|
+
* not a fetchable remote. Absent = the shipped default, which passes.
|
|
49
|
+
*/
|
|
50
|
+
export declare function validateCnAccelerateUrl(value: string | undefined): void;
|
|
39
51
|
/**
|
|
40
52
|
* The prune cap must be an integer >= 1: 0 would read as "create then
|
|
41
53
|
* immediately self-delete" (the fresh worktree is excluded, but every other
|
|
@@ -60,6 +72,10 @@ export interface SectionConfig {
|
|
|
60
72
|
keepWorktrees?: number;
|
|
61
73
|
/** Post-create configuration files to copy when .worktreeinclude is absent. */
|
|
62
74
|
postCreateCopyFiles?: string[];
|
|
75
|
+
/** cn-accelerate mirror rewrite for network READ commands; absent = off. */
|
|
76
|
+
cnAccelerate?: boolean;
|
|
77
|
+
/** cn-accelerate mirror prefix; absent = the shipped default. */
|
|
78
|
+
cnAccelerateUrl?: string;
|
|
63
79
|
}
|
|
64
80
|
/** The section-shaped view of a config: absent keys stay absent
|
|
65
81
|
* (`exactOptionalPropertyTypes`) except the switches, which spell their
|
package/lib/index.js
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
import { homedir } from 'node:os';
|
|
9
9
|
import z from '@deepseek-ai/schemastery';
|
|
10
10
|
import { childProcessExec } from './git.js';
|
|
11
|
+
import { ACCEL_DEFAULT_URL, accelerateUrlError } from './normalize.js';
|
|
11
12
|
import { handleAiCommitMessage, handleChanges, handleCommit, handleCommitFiles, handleCreateBranch, handleCreateWorktree, handleDeleteBranch, handleEnsureDirectory, handleFetch, handleGroupWorktrees, handleInspectWorktree, handleLog, handlePathExists, handlePurgeDirectory, handleRemoveWorktree, handleRenameBranch, handleStage, handleStatus, handleSwitch, handleUpdate, handlePush, handleWorktreesAll, } from './routes.js';
|
|
12
13
|
import { validateRootDir } from './settings.js';
|
|
13
14
|
import { ROUTE_AI_COMMIT_MESSAGE, ROUTE_BRANCH, ROUTE_BRANCH_DELETE, ROUTE_BRANCH_RENAME, ROUTE_CHANGES, ROUTE_COMMIT, ROUTE_COMMIT_FILES, ROUTE_ENSURE_DIRECTORY, ROUTE_EXISTS, ROUTE_FETCH, ROUTE_GROUP, ROUTE_INSPECT, ROUTE_LOG, ROUTE_PUSH, ROUTE_PURGE, ROUTE_REMOVE, ROUTE_STAGE, ROUTE_STATUS, ROUTE_SWITCH, ROUTE_UPDATE, ROUTE_WORKTREE, ROUTE_WORKTREES_ALL } from './wire.js';
|
|
@@ -37,10 +38,12 @@ export const Config = z.object({
|
|
|
37
38
|
autoPruneWorktrees: z.boolean().default(false).volatile(),
|
|
38
39
|
keepWorktrees: z.number().default(KEEP_WORKTREES_DEFAULT).volatile(),
|
|
39
40
|
postCreateCopyFiles: z.array(z.string()).default([]).volatile(),
|
|
41
|
+
cnAccelerate: z.boolean().default(false).volatile(),
|
|
42
|
+
cnAccelerateUrl: z.string().default(ACCEL_DEFAULT_URL).volatile(),
|
|
40
43
|
});
|
|
41
44
|
/** Reject stale or misspelled config keys before defaults can hide them. */
|
|
42
45
|
export function validateConfig(config) {
|
|
43
|
-
const unknown = Object.keys(config).find(key => key !== 'rootDir' && key !== 'groupSidebar' && key !== 'fetchBeforeCreate' && key !== 'autoPruneWorktrees' && key !== 'keepWorktrees' && key !== 'postCreateCopyFiles');
|
|
46
|
+
const unknown = Object.keys(config).find(key => key !== 'rootDir' && key !== 'groupSidebar' && key !== 'fetchBeforeCreate' && key !== 'autoPruneWorktrees' && key !== 'keepWorktrees' && key !== 'postCreateCopyFiles' && key !== 'cnAccelerate' && key !== 'cnAccelerateUrl');
|
|
44
47
|
if (unknown !== undefined) {
|
|
45
48
|
throw new Error(`GitWorktreeConfig: unknown key "${unknown}"`);
|
|
46
49
|
}
|
|
@@ -66,9 +69,27 @@ export function validateConfig(config) {
|
|
|
66
69
|
throw new Error('GitWorktreeConfig: "postCreateCopyFiles" must be an array of strings');
|
|
67
70
|
}
|
|
68
71
|
}
|
|
72
|
+
const cnAccelerate = readVolatile(config.cnAccelerate);
|
|
73
|
+
if (cnAccelerate !== undefined && typeof cnAccelerate !== 'boolean') {
|
|
74
|
+
throw new Error('GitWorktreeConfig: "cnAccelerate" must be a boolean');
|
|
75
|
+
}
|
|
76
|
+
validateCnAccelerateUrl(readVolatile(config.cnAccelerateUrl));
|
|
69
77
|
validateKeepWorktrees(readVolatile(config.keepWorktrees));
|
|
70
78
|
validateRootDir(root);
|
|
71
79
|
}
|
|
80
|
+
/**
|
|
81
|
+
* The accelerate prefix must be an http(s) URL without whitespace — it is
|
|
82
|
+
* concatenated with `https://github.com/` into one `url.<base>.insteadOf`
|
|
83
|
+
* config value, so a space would smuggle arguments and a non-http scheme is
|
|
84
|
+
* not a fetchable remote. Absent = the shipped default, which passes.
|
|
85
|
+
*/
|
|
86
|
+
export function validateCnAccelerateUrl(value) {
|
|
87
|
+
if (value === undefined)
|
|
88
|
+
return;
|
|
89
|
+
if (typeof value !== 'string' || accelerateUrlError(value) !== undefined) {
|
|
90
|
+
throw new Error('GitWorktreeConfig: "cnAccelerateUrl" must be an http(s) URL without whitespace');
|
|
91
|
+
}
|
|
92
|
+
}
|
|
72
93
|
/**
|
|
73
94
|
* The prune cap must be an integer >= 1: 0 would read as "create then
|
|
74
95
|
* immediately self-delete" (the fresh worktree is excluded, but every other
|
|
@@ -94,14 +115,18 @@ export function sectionOf(config) {
|
|
|
94
115
|
const fetchBeforeCreate = readVolatile(config.fetchBeforeCreate);
|
|
95
116
|
const autoPruneWorktrees = readVolatile(config.autoPruneWorktrees);
|
|
96
117
|
const keepWorktrees = readVolatile(config.keepWorktrees);
|
|
118
|
+
const cnAccelerate = readVolatile(config.cnAccelerate);
|
|
119
|
+
const cnAccelerateUrl = readVolatile(config.cnAccelerateUrl);
|
|
97
120
|
return {
|
|
98
121
|
...(rootDir === undefined ? {} : { rootDir }),
|
|
99
122
|
...(postCreateCopyFiles === undefined ? {} : { postCreateCopyFiles }),
|
|
123
|
+
...(cnAccelerateUrl === undefined ? {} : { cnAccelerateUrl }),
|
|
100
124
|
// The composition layer spells the shipped defaults so a user-layer
|
|
101
125
|
// unset can always fall back to them.
|
|
102
126
|
groupSidebar: groupSidebar ?? true,
|
|
103
127
|
fetchBeforeCreate: fetchBeforeCreate ?? false,
|
|
104
128
|
autoPruneWorktrees: autoPruneWorktrees ?? false,
|
|
129
|
+
cnAccelerate: cnAccelerate ?? false,
|
|
105
130
|
keepWorktrees: keepWorktrees ?? KEEP_WORKTREES_DEFAULT,
|
|
106
131
|
};
|
|
107
132
|
}
|
|
@@ -119,6 +144,8 @@ export function apply(ctx, config = {}) {
|
|
|
119
144
|
sectionRootDir: () => readVolatile(currentConfig().rootDir),
|
|
120
145
|
sectionFetchBeforeCreate: () => readVolatile(currentConfig().fetchBeforeCreate),
|
|
121
146
|
sectionPostCreateCopyFiles: () => readVolatile(currentConfig().postCreateCopyFiles),
|
|
147
|
+
sectionCnAccelerate: () => readVolatile(currentConfig().cnAccelerate),
|
|
148
|
+
sectionCnAccelerateUrl: () => readVolatile(currentConfig().cnAccelerateUrl),
|
|
122
149
|
home: () => homedir(),
|
|
123
150
|
envHome: () => process.env.DSH_HOME,
|
|
124
151
|
});
|
package/lib/normalize.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Pure
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* Pure name/URL normalization shared by the host (git command assembly,
|
|
3
|
+
* config validation) and the client (forms). Windows-forbidden characters
|
|
4
|
+
* govern worktree directory placement: a worktree folder name must survive
|
|
5
|
+
* on every platform the host runs on.
|
|
5
6
|
*/
|
|
6
7
|
/**
|
|
7
8
|
* Turn a branch display name into a single safe directory segment.
|
|
@@ -56,3 +57,27 @@ export type BranchNameIssue = 'empty' | 'leadingDash' | 'illegal';
|
|
|
56
57
|
* @returns the issue kind, or null when the name is acceptable.
|
|
57
58
|
*/
|
|
58
59
|
export declare function branchNameIssue(name: string): BranchNameIssue | null;
|
|
60
|
+
/**
|
|
61
|
+
* Shipped default for the cn-accelerate mirror prefix (the design doc's
|
|
62
|
+
* `cnAccelerateUrl` default and the popover's「恢复默认」target).
|
|
63
|
+
*/
|
|
64
|
+
export declare const ACCEL_DEFAULT_URL = "https://gh-proxy.com/";
|
|
65
|
+
/** Why a user-typed accelerate URL is not acceptable yet (undefined = fine). */
|
|
66
|
+
export type AccelerateUrlIssue = 'empty' | 'invalid';
|
|
67
|
+
/**
|
|
68
|
+
* Pre-flight check of the cn-accelerate prefix against what the rewrite can
|
|
69
|
+
* actually use: non-empty after trim, `http://` or `https://` scheme, and no
|
|
70
|
+
* whitespace anywhere (a space would break the `url.<base>.insteadOf=<target>`
|
|
71
|
+
* git config value and smuggle a second argument).
|
|
72
|
+
* @param value - raw draft text.
|
|
73
|
+
* @returns the issue kind, or undefined when the value is acceptable.
|
|
74
|
+
*/
|
|
75
|
+
export declare function accelerateUrlError(value: string): AccelerateUrlIssue | undefined;
|
|
76
|
+
/**
|
|
77
|
+
* Normalize an accepted accelerate URL: trim, and append the trailing `/`
|
|
78
|
+
* the prefix concatenation requires (`https://gh-proxy.com/` + `o/r.git`
|
|
79
|
+
* must stay one valid URL). Callers pass the result to `url.<base>.insteadOf`.
|
|
80
|
+
* @param value - an accepted (non-empty, http(s), whitespace-free) URL.
|
|
81
|
+
* @returns the prefix ending in `/`.
|
|
82
|
+
*/
|
|
83
|
+
export declare function normalizeAccelerateUrl(value: string): string;
|
package/lib/normalize.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Pure
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* Pure name/URL normalization shared by the host (git command assembly,
|
|
3
|
+
* config validation) and the client (forms). Windows-forbidden characters
|
|
4
|
+
* govern worktree directory placement: a worktree folder name must survive
|
|
5
|
+
* on every platform the host runs on.
|
|
5
6
|
*/
|
|
6
7
|
/**
|
|
7
8
|
* Turn a branch display name into a single safe directory segment.
|
|
@@ -91,3 +92,37 @@ export function branchNameIssue(name) {
|
|
|
91
92
|
return 'illegal';
|
|
92
93
|
return null;
|
|
93
94
|
}
|
|
95
|
+
/**
|
|
96
|
+
* Shipped default for the cn-accelerate mirror prefix (the design doc's
|
|
97
|
+
* `cnAccelerateUrl` default and the popover's「恢复默认」target).
|
|
98
|
+
*/
|
|
99
|
+
export const ACCEL_DEFAULT_URL = 'https://gh-proxy.com/';
|
|
100
|
+
/**
|
|
101
|
+
* Pre-flight check of the cn-accelerate prefix against what the rewrite can
|
|
102
|
+
* actually use: non-empty after trim, `http://` or `https://` scheme, and no
|
|
103
|
+
* whitespace anywhere (a space would break the `url.<base>.insteadOf=<target>`
|
|
104
|
+
* git config value and smuggle a second argument).
|
|
105
|
+
* @param value - raw draft text.
|
|
106
|
+
* @returns the issue kind, or undefined when the value is acceptable.
|
|
107
|
+
*/
|
|
108
|
+
export function accelerateUrlError(value) {
|
|
109
|
+
const trimmed = value.trim();
|
|
110
|
+
if (trimmed === '')
|
|
111
|
+
return 'empty';
|
|
112
|
+
if (/\s/.test(trimmed))
|
|
113
|
+
return 'invalid';
|
|
114
|
+
if (!trimmed.startsWith('http://') && !trimmed.startsWith('https://'))
|
|
115
|
+
return 'invalid';
|
|
116
|
+
return undefined;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Normalize an accepted accelerate URL: trim, and append the trailing `/`
|
|
120
|
+
* the prefix concatenation requires (`https://gh-proxy.com/` + `o/r.git`
|
|
121
|
+
* must stay one valid URL). Callers pass the result to `url.<base>.insteadOf`.
|
|
122
|
+
* @param value - an accepted (non-empty, http(s), whitespace-free) URL.
|
|
123
|
+
* @returns the prefix ending in `/`.
|
|
124
|
+
*/
|
|
125
|
+
export function normalizeAccelerateUrl(value) {
|
|
126
|
+
const trimmed = value.trim();
|
|
127
|
+
return trimmed.endsWith('/') ? trimmed : `${trimmed}/`;
|
|
128
|
+
}
|
package/lib/routes.d.ts
CHANGED
|
@@ -14,6 +14,10 @@ export interface RouteDeps {
|
|
|
14
14
|
sectionFetchBeforeCreate: () => boolean | undefined;
|
|
15
15
|
/** Post-create configuration files to copy when .worktreeinclude is absent. */
|
|
16
16
|
sectionPostCreateCopyFiles?: () => string[] | undefined;
|
|
17
|
+
/** Whether the cn-accelerate mirror rewrite is on (absent = off). */
|
|
18
|
+
sectionCnAccelerate?: () => boolean | undefined;
|
|
19
|
+
/** The cn-accelerate mirror prefix (absent = the shipped default). */
|
|
20
|
+
sectionCnAccelerateUrl?: () => string | undefined;
|
|
17
21
|
/** User home directory seam. */
|
|
18
22
|
home: () => string;
|
|
19
23
|
/** `$DSH_HOME` environment value seam. */
|
package/lib/routes.js
CHANGED
|
@@ -6,7 +6,7 @@ import { basename, dirname, join, resolve } from 'node:path';
|
|
|
6
6
|
import { mkdir, readdir, rm, stat } from 'node:fs/promises';
|
|
7
7
|
import { runPostCreateCopy } from './copy-files.js';
|
|
8
8
|
import { appendWorktreeExclude, fsExcludeSeams, GitError, addWorktree, addWorktreeCutout, collectAiDiff, commitFiles, commitIndex, createBranch, cutoutBranchName, deleteBranch, fetchAll, fsDirExists, inspectWorktree, isAbsoluteDir, isStageablePath, logHistory, probeRepo, probeWorkspaceGit, removeWorktree, renameBranch, resolveBranch, pushBranch, stagePaths, switchBranch, updateBranch, workingChanges } from './git.js';
|
|
9
|
-
import { isAbsoluteConfigPath, sanitizeBranchDir } from './normalize.js';
|
|
9
|
+
import { ACCEL_DEFAULT_URL, accelerateUrlError, isAbsoluteConfigPath, normalizeAccelerateUrl, sanitizeBranchDir } from './normalize.js';
|
|
10
10
|
import { resolveRootDir } from './settings.js';
|
|
11
11
|
import { COMMIT_MESSAGE_LIMIT } from './wire.js';
|
|
12
12
|
/** Real fs-backed one-level directory listing (names only). Plain FILES are
|
|
@@ -39,6 +39,18 @@ export async function fsMkdirRecursive(path) {
|
|
|
39
39
|
function fail(status, error) {
|
|
40
40
|
return { status, body: { error } };
|
|
41
41
|
}
|
|
42
|
+
/**
|
|
43
|
+
* The rewrite prefix for one request's network READ commands: the cn-accelerate
|
|
44
|
+
* mirror prefix when the switch is on and the URL setting is usable (the
|
|
45
|
+
* shipped default fills an absent URL), else undefined — no injection and the
|
|
46
|
+
* direct connection runs unchanged. PUSH never consults this helper.
|
|
47
|
+
*/
|
|
48
|
+
function accelPrefix(deps) {
|
|
49
|
+
if (deps.sectionCnAccelerate?.() !== true)
|
|
50
|
+
return undefined;
|
|
51
|
+
const url = deps.sectionCnAccelerateUrl?.() ?? ACCEL_DEFAULT_URL;
|
|
52
|
+
return accelerateUrlError(url) === undefined ? normalizeAccelerateUrl(url) : undefined;
|
|
53
|
+
}
|
|
42
54
|
/** Upper bound on distinct paths one /group request may probe. */
|
|
43
55
|
const GROUP_PATHS_LIMIT = 256;
|
|
44
56
|
/** Probes per in-flight batch — polite on Windows, where each git call is a
|
|
@@ -414,7 +426,7 @@ export async function handleCreateWorktree(deps, body) {
|
|
|
414
426
|
const resolved = await resolveBranch(deps.exec, facts.repoRoot, branch);
|
|
415
427
|
if (resolved?.remote !== undefined) {
|
|
416
428
|
try {
|
|
417
|
-
await fetchAll(deps.exec, facts.repoRoot);
|
|
429
|
+
await fetchAll(deps.exec, facts.repoRoot, accelPrefix(deps));
|
|
418
430
|
}
|
|
419
431
|
catch (error) {
|
|
420
432
|
fetchWarning = error instanceof GitError ? error.stderr.trim() : error instanceof Error ? error.message : String(error);
|
|
@@ -649,7 +661,7 @@ export async function handleFetch(deps, body) {
|
|
|
649
661
|
const facts = await probeRepo(deps.exec, repoPath, deps.dirExists);
|
|
650
662
|
if (facts === undefined)
|
|
651
663
|
return fail(400, `"${repoPath}" is not inside a git repository`);
|
|
652
|
-
await fetchAll(deps.exec, facts.repoRoot);
|
|
664
|
+
await fetchAll(deps.exec, facts.repoRoot, accelPrefix(deps));
|
|
653
665
|
const result = { remote: 'all' };
|
|
654
666
|
return { status: 200, body: result };
|
|
655
667
|
}
|
|
@@ -682,7 +694,7 @@ export async function handleUpdate(deps, body) {
|
|
|
682
694
|
// session "update current branch" means the branch that worktree holds.
|
|
683
695
|
// `repoRoot` would fast-forward the main checkout behind the user's back
|
|
684
696
|
// (both are the same directory when the session IS the main checkout).
|
|
685
|
-
const outcome = await updateBranch(deps.exec, facts.toplevel);
|
|
697
|
+
const outcome = await updateBranch(deps.exec, facts.toplevel, accelPrefix(deps));
|
|
686
698
|
const result = { branch: outcome.branch, updated: outcome.updated };
|
|
687
699
|
return { status: 200, body: result };
|
|
688
700
|
}
|
package/package.json
CHANGED