dsh-git-branch-manage 1.0.4 → 1.0.6
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 +1368 -248
- package/lib/git.d.ts +64 -4
- package/lib/git.js +79 -8
- package/lib/index.d.ts +16 -0
- package/lib/index.js +31 -3
- package/lib/normalize.d.ts +28 -3
- package/lib/normalize.js +38 -3
- package/lib/routes.d.ts +22 -3
- package/lib/routes.js +94 -8
- package/lib/wire.d.ts +31 -0
- package/lib/wire.js +2 -0
- 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
|
/**
|
|
@@ -97,6 +127,8 @@ window.__ModuleLoader__.load({
|
|
|
97
127
|
const ROUTE_LOG = `${ROUTE_PREFIX}/log`;
|
|
98
128
|
/** POST ROUTE_PREFIX/commit-files — the files one commit changed. */
|
|
99
129
|
const ROUTE_COMMIT_FILES = `${ROUTE_PREFIX}/commit-files`;
|
|
130
|
+
/** POST ROUTE_PREFIX/tag — create a tag at an existing commit. */
|
|
131
|
+
const ROUTE_TAG = `${ROUTE_PREFIX}/tag`;
|
|
100
132
|
/** POST ROUTE_PREFIX/ai-commit-message — one-shot LLM commit message from
|
|
101
133
|
* the staged diff (mounted only where the host provides the llm service). */
|
|
102
134
|
const ROUTE_AI_COMMIT_MESSAGE = `${ROUTE_PREFIX}/ai-commit-message`;
|
|
@@ -358,16 +390,19 @@ window.__ModuleLoader__.load({
|
|
|
358
390
|
/**
|
|
359
391
|
* One page of commit history of the checkout.
|
|
360
392
|
* @param repoPath - absolute directory inside the repository.
|
|
361
|
-
* @param
|
|
362
|
-
* @param skip - offset into the history.
|
|
363
|
-
* @param ref - optional starting rev (a branch name); absent = HEAD.
|
|
393
|
+
* @param query - paging and the branch/author/time narrowing.
|
|
364
394
|
*/
|
|
365
|
-
function requestLog(repoPath,
|
|
395
|
+
function requestLog(repoPath, query = {}) {
|
|
396
|
+
const { limit, skip, ref, all, author, since, until } = query;
|
|
366
397
|
return post(ROUTE_LOG, {
|
|
367
398
|
repoPath,
|
|
368
399
|
...limit === void 0 ? {} : { limit },
|
|
369
400
|
...skip === void 0 ? {} : { skip },
|
|
370
|
-
...ref === void 0 || ref === "" ? {} : { ref }
|
|
401
|
+
...ref === void 0 || ref === "" ? {} : { ref },
|
|
402
|
+
...all === true ? { all } : {},
|
|
403
|
+
...author === void 0 || author === "" ? {} : { author },
|
|
404
|
+
...since === void 0 ? {} : { since },
|
|
405
|
+
...until === void 0 ? {} : { until }
|
|
371
406
|
});
|
|
372
407
|
}
|
|
373
408
|
/**
|
|
@@ -382,6 +417,22 @@ window.__ModuleLoader__.load({
|
|
|
382
417
|
});
|
|
383
418
|
}
|
|
384
419
|
/**
|
|
420
|
+
* Create a tag pointing at one commit (annotated when a message is given,
|
|
421
|
+
* lightweight otherwise). Local only: pushing tags is a separate release step.
|
|
422
|
+
* @param repoPath - absolute directory inside the repository.
|
|
423
|
+
* @param name - the new tag name.
|
|
424
|
+
* @param target - the commit to tag; absent = HEAD.
|
|
425
|
+
* @param message - annotation message; absent = lightweight tag.
|
|
426
|
+
*/
|
|
427
|
+
function requestCreateTag(repoPath, name, target, message) {
|
|
428
|
+
return post(ROUTE_TAG, {
|
|
429
|
+
repoPath,
|
|
430
|
+
name,
|
|
431
|
+
...target === void 0 || target === "" ? {} : { target },
|
|
432
|
+
...message === void 0 || message.trim() === "" ? {} : { message }
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
385
436
|
* Generate a commit message from the checkout's diff with the requesting
|
|
386
437
|
* session's own model (a one-shot host llm call).
|
|
387
438
|
* @param body - the repo path plus the session's model selection and an
|
|
@@ -3401,6 +3452,151 @@ window.__ModuleLoader__.load({
|
|
|
3401
3452
|
}
|
|
3402
3453
|
};
|
|
3403
3454
|
//#endregion
|
|
3455
|
+
//#region src/client/accel-form.ts
|
|
3456
|
+
/** The fields this form edits. */
|
|
3457
|
+
const ACCEL_ENABLED_FIELD = "cnAccelerate";
|
|
3458
|
+
const ACCEL_URL_FIELD = "cnAccelerateUrl";
|
|
3459
|
+
/**
|
|
3460
|
+
* Stages the cn-accelerate edits over the `dsh-git-branch` Host entry.
|
|
3461
|
+
*
|
|
3462
|
+
* Publishes through a snapshot store like {@link CardForm}: the consumer
|
|
3463
|
+
* reads through a snapshot selector while the scope and the local drafts
|
|
3464
|
+
* change underneath; every projection is rebuilt from the two together.
|
|
3465
|
+
*/
|
|
3466
|
+
var AccelForm = class {
|
|
3467
|
+
scope;
|
|
3468
|
+
snapshotValue;
|
|
3469
|
+
listeners = /* @__PURE__ */ new Set();
|
|
3470
|
+
enabledDraft;
|
|
3471
|
+
urlDraft;
|
|
3472
|
+
saving = false;
|
|
3473
|
+
failed = false;
|
|
3474
|
+
/**
|
|
3475
|
+
* @param scope - the bound configuration form for the `dsh-git-branch` Host entry.
|
|
3476
|
+
*/
|
|
3477
|
+
constructor(scope) {
|
|
3478
|
+
this.scope = scope;
|
|
3479
|
+
this.snapshotValue = this.project();
|
|
3480
|
+
scope.subscribe(() => {
|
|
3481
|
+
this.publish();
|
|
3482
|
+
});
|
|
3483
|
+
}
|
|
3484
|
+
/** @returns the store a consumer reads through its bound selector. */
|
|
3485
|
+
bind() {
|
|
3486
|
+
return {
|
|
3487
|
+
getSnapshot: () => this.snapshotValue,
|
|
3488
|
+
subscribe: (listener) => {
|
|
3489
|
+
this.listeners.add(listener);
|
|
3490
|
+
return () => {
|
|
3491
|
+
this.listeners.delete(listener);
|
|
3492
|
+
};
|
|
3493
|
+
},
|
|
3494
|
+
set: (next) => {
|
|
3495
|
+
this.store(next);
|
|
3496
|
+
}
|
|
3497
|
+
};
|
|
3498
|
+
}
|
|
3499
|
+
/** @returns the stage, restore, save, and discard actions bound to this form. */
|
|
3500
|
+
actions() {
|
|
3501
|
+
return {
|
|
3502
|
+
setAccelEnabled: (value) => {
|
|
3503
|
+
this.enabledDraft = value;
|
|
3504
|
+
this.failed = false;
|
|
3505
|
+
this.publish();
|
|
3506
|
+
},
|
|
3507
|
+
editAccelUrl: (text) => {
|
|
3508
|
+
this.urlDraft = text;
|
|
3509
|
+
this.failed = false;
|
|
3510
|
+
this.publish();
|
|
3511
|
+
},
|
|
3512
|
+
restoreAccelDefault: () => {
|
|
3513
|
+
this.urlDraft = ACCEL_DEFAULT_URL;
|
|
3514
|
+
this.failed = false;
|
|
3515
|
+
this.publish();
|
|
3516
|
+
},
|
|
3517
|
+
saveAccel: () => this.save(),
|
|
3518
|
+
discardAccel: () => {
|
|
3519
|
+
if (this.enabledDraft === void 0 && this.urlDraft === void 0 && !this.failed) return;
|
|
3520
|
+
this.enabledDraft = void 0;
|
|
3521
|
+
this.urlDraft = void 0;
|
|
3522
|
+
this.failed = false;
|
|
3523
|
+
this.publish();
|
|
3524
|
+
}
|
|
3525
|
+
};
|
|
3526
|
+
}
|
|
3527
|
+
/** The URL as the user sees it: the live draft, else the stored value over
|
|
3528
|
+
* the shipped default. */
|
|
3529
|
+
effectiveUrlText() {
|
|
3530
|
+
const value = this.scope.getSnapshot().value?.[ACCEL_URL_FIELD];
|
|
3531
|
+
return this.urlDraft ?? value ?? "https://gh-proxy.com/";
|
|
3532
|
+
}
|
|
3533
|
+
/**
|
|
3534
|
+
* Write the switch and the normalized URL TOGETHER, then re-seed from what
|
|
3535
|
+
* the Host accepted. The URL is validated first — an unusable draft blocks
|
|
3536
|
+
* the save (and the surfaces disable the button on the same error), so the
|
|
3537
|
+
* switch can never land alone against a mirror the user has not confirmed.
|
|
3538
|
+
* A save that did not land keeps its drafts so the user can correct them.
|
|
3539
|
+
*/
|
|
3540
|
+
async save() {
|
|
3541
|
+
if (this.enabledDraft === void 0 && this.urlDraft === void 0 || this.saving) return;
|
|
3542
|
+
const urlText = this.effectiveUrlText();
|
|
3543
|
+
if (accelerateUrlError(urlText) !== void 0) return;
|
|
3544
|
+
const intendedEnabled = this.enabledDraft ?? this.scope.getSnapshot().value?.["cnAccelerate"] ?? false;
|
|
3545
|
+
const intendedUrl = normalizeAccelerateUrl(urlText);
|
|
3546
|
+
this.saving = true;
|
|
3547
|
+
this.failed = false;
|
|
3548
|
+
this.publish();
|
|
3549
|
+
let landed = true;
|
|
3550
|
+
try {
|
|
3551
|
+
await this.scope.set(ACCEL_ENABLED_FIELD, intendedEnabled);
|
|
3552
|
+
await this.scope.set(ACCEL_URL_FIELD, intendedUrl);
|
|
3553
|
+
const user = this.userLayer();
|
|
3554
|
+
if (user?.["cnAccelerate"] !== intendedEnabled || user?.["cnAccelerateUrl"] !== intendedUrl) landed = false;
|
|
3555
|
+
} catch (_settingsWriteFailure) {
|
|
3556
|
+
landed = false;
|
|
3557
|
+
}
|
|
3558
|
+
if (landed) {
|
|
3559
|
+
this.enabledDraft = void 0;
|
|
3560
|
+
this.urlDraft = void 0;
|
|
3561
|
+
}
|
|
3562
|
+
this.saving = false;
|
|
3563
|
+
this.failed = !landed;
|
|
3564
|
+
this.publish();
|
|
3565
|
+
}
|
|
3566
|
+
/** The raw user layer narrowed to a record; the wire answer is `unknown`. */
|
|
3567
|
+
userLayer() {
|
|
3568
|
+
const user = this.scope.getSnapshot().user;
|
|
3569
|
+
return typeof user === "object" && user !== null ? user : void 0;
|
|
3570
|
+
}
|
|
3571
|
+
project() {
|
|
3572
|
+
const snapshot = this.scope.getSnapshot();
|
|
3573
|
+
const storedEnabled = snapshot.value?.["cnAccelerate"] ?? false;
|
|
3574
|
+
const storedUrl = snapshot.value?.["cnAccelerateUrl"] ?? "https://gh-proxy.com/";
|
|
3575
|
+
const urlText = this.effectiveUrlText();
|
|
3576
|
+
const enabledDirty = this.enabledDraft !== void 0 && this.enabledDraft !== storedEnabled;
|
|
3577
|
+
const urlDirty = this.urlDraft !== void 0 && this.urlDraft !== storedUrl;
|
|
3578
|
+
return {
|
|
3579
|
+
available: snapshot.status === "ready",
|
|
3580
|
+
writable: snapshot.writable,
|
|
3581
|
+
enabled: this.enabledDraft ?? storedEnabled,
|
|
3582
|
+
urlText,
|
|
3583
|
+
dirty: enabledDirty || urlDirty,
|
|
3584
|
+
saving: this.saving,
|
|
3585
|
+
failed: this.failed,
|
|
3586
|
+
urlError: accelerateUrlError(urlText) ?? null
|
|
3587
|
+
};
|
|
3588
|
+
}
|
|
3589
|
+
/** Replace the snapshot reference and notify, only when the fact moved. */
|
|
3590
|
+
store(next) {
|
|
3591
|
+
if (next === this.snapshotValue) return;
|
|
3592
|
+
this.snapshotValue = next;
|
|
3593
|
+
for (const listener of this.listeners) listener();
|
|
3594
|
+
}
|
|
3595
|
+
publish() {
|
|
3596
|
+
this.store(this.project());
|
|
3597
|
+
}
|
|
3598
|
+
};
|
|
3599
|
+
//#endregion
|
|
3404
3600
|
//#region src/client/git-panel-data.ts
|
|
3405
3601
|
/**
|
|
3406
3602
|
* Classify raw %D decoration segments: `HEAD -> main` (and bare `HEAD`) is
|
|
@@ -3475,11 +3671,97 @@ window.__ModuleLoader__.load({
|
|
|
3475
3671
|
unstaged
|
|
3476
3672
|
};
|
|
3477
3673
|
}
|
|
3478
|
-
/**
|
|
3479
|
-
|
|
3480
|
-
|
|
3481
|
-
|
|
3482
|
-
|
|
3674
|
+
/** Day and week windows, in ms (7 days, not a calendar week). */
|
|
3675
|
+
const DAY_MS = 864e5;
|
|
3676
|
+
const WEEK_MS = 7 * DAY_MS;
|
|
3677
|
+
/**
|
|
3678
|
+
* Turn the picker's choice into the `since`/`until` bounds the history call
|
|
3679
|
+
* ships. `day`/`week` are relative to `now` — resolved per request, so a
|
|
3680
|
+
* panel left open an hour does not keep asking for the window it opened
|
|
3681
|
+
* with. `custom` carries the two bounds the date inputs produced (either may
|
|
3682
|
+
* be absent, meaning "open on that side").
|
|
3683
|
+
*/
|
|
3684
|
+
function historyRangeBound(range, now, custom = {}) {
|
|
3685
|
+
if (range === "day") return { since: now - DAY_MS };
|
|
3686
|
+
if (range === "week") return { since: now - WEEK_MS };
|
|
3687
|
+
if (range === "custom") return {
|
|
3688
|
+
...custom.since === void 0 ? {} : { since: custom.since },
|
|
3689
|
+
...custom.until === void 0 ? {} : { until: custom.until }
|
|
3690
|
+
};
|
|
3691
|
+
return {};
|
|
3692
|
+
}
|
|
3693
|
+
/** No filter: every ref, every author, every date. */
|
|
3694
|
+
const EMPTY_HISTORY_FILTERS = {
|
|
3695
|
+
branch: "",
|
|
3696
|
+
author: "",
|
|
3697
|
+
range: "all",
|
|
3698
|
+
customSince: "",
|
|
3699
|
+
customUntil: ""
|
|
3700
|
+
};
|
|
3701
|
+
/** 'YYYY-MM-DD' as a date input emits it. */
|
|
3702
|
+
const DAY_PATTERN = /^(\d{4})-(\d{2})-(\d{2})$/;
|
|
3703
|
+
/** Midnight of a 'YYYY-MM-DD' day in the BROWSER's zone, or undefined. Read
|
|
3704
|
+
* field by field on purpose: `new Date('2026-10-15')` is parsed as UTC
|
|
3705
|
+
* midnight, which is the previous day west of Greenwich — a custom window
|
|
3706
|
+
* would then start a day early for half the world. */
|
|
3707
|
+
function parseDayStart(value) {
|
|
3708
|
+
const match = DAY_PATTERN.exec(value.trim());
|
|
3709
|
+
if (match === null) return void 0;
|
|
3710
|
+
const [, year, month, day] = match;
|
|
3711
|
+
const date = new Date(Number(year), Number(month) - 1, Number(day));
|
|
3712
|
+
return Number.isFinite(date.getTime()) ? date.getTime() : void 0;
|
|
3713
|
+
}
|
|
3714
|
+
/** Last millisecond of a 'YYYY-MM-DD' day in the browser's zone, or undefined.
|
|
3715
|
+
* The end is inclusive: a custom window ending today must cover commits made
|
|
3716
|
+
* earlier today. */
|
|
3717
|
+
function parseDayEnd(value) {
|
|
3718
|
+
const start = parseDayStart(value);
|
|
3719
|
+
return start === void 0 ? void 0 : start + DAY_MS - 1;
|
|
3720
|
+
}
|
|
3721
|
+
/** The window the filters currently describe, relative to `now`. */
|
|
3722
|
+
function historyFilterBound(filters, now) {
|
|
3723
|
+
if (filters.range !== "custom") return historyRangeBound(filters.range, now);
|
|
3724
|
+
const since = parseDayStart(filters.customSince);
|
|
3725
|
+
const until = parseDayEnd(filters.customUntil);
|
|
3726
|
+
return {
|
|
3727
|
+
...since === void 0 ? {} : { since },
|
|
3728
|
+
...until === void 0 ? {} : { until }
|
|
3729
|
+
};
|
|
3730
|
+
}
|
|
3731
|
+
/**
|
|
3732
|
+
* The history request for one filter state. A chosen branch pins `ref` (and
|
|
3733
|
+
* cancels `all`, which would otherwise widen the walk to every ref); leaving
|
|
3734
|
+
* it blank asks for `all` — the card is meant to show the whole repository,
|
|
3735
|
+
* not just the checked-out branch, and merge edges only exist when the
|
|
3736
|
+
* branches on both sides of a merge are in the page.
|
|
3737
|
+
*/
|
|
3738
|
+
function buildHistoryQuery(state) {
|
|
3739
|
+
const author = state.author.trim();
|
|
3740
|
+
const base = {
|
|
3741
|
+
...author === "" ? {} : { author },
|
|
3742
|
+
...state.bound
|
|
3743
|
+
};
|
|
3744
|
+
if (state.branch !== "") return {
|
|
3745
|
+
...base,
|
|
3746
|
+
ref: state.branch
|
|
3747
|
+
};
|
|
3748
|
+
return {
|
|
3749
|
+
...base,
|
|
3750
|
+
all: true
|
|
3751
|
+
};
|
|
3752
|
+
}
|
|
3753
|
+
/** The full paged request for one filter state, resolved against `now`. */
|
|
3754
|
+
function historyQueryFor(filters, now, limit, skip) {
|
|
3755
|
+
return {
|
|
3756
|
+
...buildHistoryQuery({
|
|
3757
|
+
branch: filters.branch,
|
|
3758
|
+
author: filters.author,
|
|
3759
|
+
now,
|
|
3760
|
+
bound: historyFilterBound(filters, now)
|
|
3761
|
+
}),
|
|
3762
|
+
limit,
|
|
3763
|
+
skip
|
|
3764
|
+
};
|
|
3483
3765
|
}
|
|
3484
3766
|
/**
|
|
3485
3767
|
* Case-insensitive substring filter over the branch name — the branch card's
|
|
@@ -3565,7 +3847,7 @@ window.__ModuleLoader__.load({
|
|
|
3565
3847
|
}
|
|
3566
3848
|
//#endregion
|
|
3567
3849
|
//#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";
|
|
3850
|
+
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/* The head carries the title plus three filters (branch, author, time). The\n * row wraps at narrow widths rather than squeezing the date inputs into\n * unreadable slivers. */\n.historyFilters_427795137 {\n margin-left: auto;\n display: flex;\n align-items: center;\n gap: 6px;\n flex-wrap: wrap;\n justify-content: flex-end;\n}\n\n.filterSelect_427795137,\n.filterInput_427795137,\n.filterDate_427795137 {\n height: 26px;\n border: 0.5px solid var(--dsw-alias-border-l4);\n border-radius: 7px;\n background: var(--dsw-alias-bg-layer-3);\n color: var(--dsw-alias-label-primary);\n font: inherit;\n font-size: 12px;\n padding: 0 6px;\n outline: none;\n}\n\n.filterSelect_427795137 {\n max-width: 150px;\n}\n\n.filterInput_427795137 {\n width: 120px;\n}\n\n.filterInput_427795137::placeholder {\n color: var(--dsw-alias-label-tertiary);\n}\n\n.filterDate_427795137 {\n width: 122px;\n color-scheme: light dark;\n}\n\n.filterDash_427795137 {\n color: var(--dsw-alias-label-tertiary);\n font-size: 12px;\n}\n\n.commitList_427795137 {\n /* Keep the history body gap between blocks, never between graph rows.\n * A non-shrinking block also prevents a long history from compressing rows\n * inside the card's scrolling flex body. */\n flex: none;\n}\n\n.commitRow_427795137 {\n position: relative;\n display: grid;\n grid-template-columns: var(--graph-w, 22px) minmax(0, 1fr) auto;\n column-gap: 8px;\n align-items: center;\n padding: 0 8px 0 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/* The SVG fills the actual grid row, including the text cells' padding.\n * Its paths scale vertically; the separate dot keeps a constant radius. */\n.commitGraph_427795137 {\n position: relative;\n align-self: stretch;\n min-height: 44px;\n}\n\n.commitGraphSvg_427795137 {\n position: absolute;\n inset: 0;\n width: 100%;\n height: 100%;\n display: block;\n overflow: visible;\n}\n\n.commitDot_427795137 {\n position: absolute;\n top: 50%;\n width: 8px;\n height: 8px;\n box-sizing: border-box;\n border: 2px solid currentColor;\n border-radius: 50%;\n transform: translate(-50%, -50%);\n pointer-events: none;\n}\n\n.commitExpandGraph_427795137 {\n position: absolute;\n top: 0;\n left: 0;\n height: 100%;\n display: block;\n pointer-events: none;\n}\n\n.commitMain_427795137 {\n padding: 6px 0;\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 min-width: 0;\n}\n\n.commitAuthor_427795137 {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.commitSide_427795137 {\n padding: 6px 0;\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/* The absolute stamp column: tabular figures so every row's digits line up\n * under the one above, which is what makes the column scannable. */\n.commitDate_427795137 {\n font-variant-numeric: tabular-nums;\n white-space: nowrap;\n}\n\n.commitHash_427795137 {\n font-family: ui-monospace, Consolas, monospace;\n}\n\n.commitExpand_427795137 {\n position: relative;\n grid-column: 1 / -1;\n /* Align the expanded file list with the subject column, whatever width the\n * graph band needs in this repository. */\n padding: 2px 4px 8px calc(var(--graph-w, 22px) + 8px);\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
3851
|
const tagId$2 = "dsh-git-branch-manage/GitHistoryPanel.module.css";
|
|
3570
3852
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=\"" + tagId$2 + "\"]") === null) {
|
|
3571
3853
|
const tag = document.createElement("style");
|
|
@@ -3585,6 +3867,26 @@ window.__ModuleLoader__.load({
|
|
|
3585
3867
|
"track": "track_427795137",
|
|
3586
3868
|
"topbarActions": "topbarActions_427795137",
|
|
3587
3869
|
"refreshBtn": "refreshBtn_427795137",
|
|
3870
|
+
"menu": "menu_427795137",
|
|
3871
|
+
"cnWrap": "cnWrap_427795137",
|
|
3872
|
+
"cnBtn": "cnBtn_427795137",
|
|
3873
|
+
"cnBtnOn": "cnBtnOn_427795137",
|
|
3874
|
+
"cnDot": "cnDot_427795137",
|
|
3875
|
+
"cnPop": "cnPop_427795137",
|
|
3876
|
+
"cnPopHead": "cnPopHead_427795137",
|
|
3877
|
+
"cnPopTitle": "cnPopTitle_427795137",
|
|
3878
|
+
"cnSwitchRow": "cnSwitchRow_427795137",
|
|
3879
|
+
"cnSwitch": "cnSwitch_427795137",
|
|
3880
|
+
"cnHint": "cnHint_427795137",
|
|
3881
|
+
"cnFieldHead": "cnFieldHead_427795137",
|
|
3882
|
+
"cnFieldLabel": "cnFieldLabel_427795137",
|
|
3883
|
+
"cnLink": "cnLink_427795137",
|
|
3884
|
+
"cnInput": "cnInput_427795137",
|
|
3885
|
+
"cnInputBad": "cnInputBad_427795137",
|
|
3886
|
+
"cnError": "cnError_427795137",
|
|
3887
|
+
"cnFooter": "cnFooter_427795137",
|
|
3888
|
+
"cnGhost": "cnGhost_427795137",
|
|
3889
|
+
"cnSave": "cnSave_427795137",
|
|
3588
3890
|
"scroll": "scroll_427795137",
|
|
3589
3891
|
"section": "section_427795137",
|
|
3590
3892
|
"sectionFill": "sectionFill_427795137",
|
|
@@ -3623,7 +3925,6 @@ window.__ModuleLoader__.load({
|
|
|
3623
3925
|
"split": "split_427795137",
|
|
3624
3926
|
"splitMain": "splitMain_427795137",
|
|
3625
3927
|
"splitCaret": "splitCaret_427795137",
|
|
3626
|
-
"menu": "menu_427795137",
|
|
3627
3928
|
"menuItem": "menuItem_427795137",
|
|
3628
3929
|
"menuSub": "menuSub_427795137",
|
|
3629
3930
|
"treeHead": "treeHead_427795137",
|
|
@@ -3648,13 +3949,18 @@ window.__ModuleLoader__.load({
|
|
|
3648
3949
|
"searchBox": "searchBox_427795137",
|
|
3649
3950
|
"searchBoxHead": "searchBoxHead_427795137",
|
|
3650
3951
|
"searchInput": "searchInput_427795137",
|
|
3952
|
+
"historyFilters": "historyFilters_427795137",
|
|
3953
|
+
"filterSelect": "filterSelect_427795137",
|
|
3954
|
+
"filterInput": "filterInput_427795137",
|
|
3955
|
+
"filterDate": "filterDate_427795137",
|
|
3956
|
+
"filterDash": "filterDash_427795137",
|
|
3957
|
+
"commitList": "commitList_427795137",
|
|
3651
3958
|
"commitRow": "commitRow_427795137",
|
|
3652
3959
|
"commitRowOpen": "commitRowOpen_427795137",
|
|
3653
3960
|
"commitGraph": "commitGraph_427795137",
|
|
3654
|
-
"
|
|
3655
|
-
"commitRowLast": "commitRowLast_427795137",
|
|
3961
|
+
"commitGraphSvg": "commitGraphSvg_427795137",
|
|
3656
3962
|
"commitDot": "commitDot_427795137",
|
|
3657
|
-
"
|
|
3963
|
+
"commitExpandGraph": "commitExpandGraph_427795137",
|
|
3658
3964
|
"commitMain": "commitMain_427795137",
|
|
3659
3965
|
"commitSubject": "commitSubject_427795137",
|
|
3660
3966
|
"commitSubjectText": "commitSubjectText_427795137",
|
|
@@ -3665,7 +3971,9 @@ window.__ModuleLoader__.load({
|
|
|
3665
3971
|
"refRemote": "refRemote_427795137",
|
|
3666
3972
|
"refLocal": "refLocal_427795137",
|
|
3667
3973
|
"commitMeta": "commitMeta_427795137",
|
|
3974
|
+
"commitAuthor": "commitAuthor_427795137",
|
|
3668
3975
|
"commitSide": "commitSide_427795137",
|
|
3976
|
+
"commitDate": "commitDate_427795137",
|
|
3669
3977
|
"commitHash": "commitHash_427795137",
|
|
3670
3978
|
"commitExpand": "commitExpand_427795137",
|
|
3671
3979
|
"commitExpandTitle": "commitExpandTitle_427795137",
|
|
@@ -3679,6 +3987,67 @@ window.__ModuleLoader__.load({
|
|
|
3679
3987
|
"moreRow": "moreRow_427795137"
|
|
3680
3988
|
};
|
|
3681
3989
|
//#endregion
|
|
3990
|
+
//#region src/client/panel/FloatMenu.tsx
|
|
3991
|
+
/**
|
|
3992
|
+
* FloatMenu: a body-portalled, fixed-positioned popup menu anchored to a
|
|
3993
|
+
* viewport point — the shared shell behind the branch list's context menu and
|
|
3994
|
+
* the history rows' commit menu.
|
|
3995
|
+
*
|
|
3996
|
+
* Owns the three things every such menu needs and neither caller should
|
|
3997
|
+
* re-implement: the portal (a scrolling card body would otherwise clip it),
|
|
3998
|
+
* dismissal (pointer-down outside + Escape), and viewport clamping in a
|
|
3999
|
+
* LAYOUT effect (measuring after paint would flash a menu off-screen, and the
|
|
4000
|
+
* clamp must run before the browser shows the frame).
|
|
4001
|
+
*
|
|
4002
|
+
* Position arrives as the anchor point in client coordinates — the caller
|
|
4003
|
+
* derives it from the row it was opened on, so the menu opens where the user
|
|
4004
|
+
* clicked rather than where the row happens to be after a re-render.
|
|
4005
|
+
*
|
|
4006
|
+
* @module git-worktree/client/panel/FloatMenu
|
|
4007
|
+
*/
|
|
4008
|
+
/** Distance kept from the viewport edges, in px. */
|
|
4009
|
+
const VIEWPORT_MARGIN = 8;
|
|
4010
|
+
/**
|
|
4011
|
+
* One floating menu. Rendered into `document.body`, positioned on the layout
|
|
4012
|
+
* pass and clamped into the viewport: flip above the anchor when the menu
|
|
4013
|
+
* would overflow the bottom, clamp the left edge otherwise.
|
|
4014
|
+
*/
|
|
4015
|
+
function FloatMenu(props) {
|
|
4016
|
+
const { anchor, role = "menu", label, onClose, children } = props;
|
|
4017
|
+
const ref = (0, react.useRef)(null);
|
|
4018
|
+
(0, react.useEffect)(() => {
|
|
4019
|
+
const onPointerDown = (event) => {
|
|
4020
|
+
if (ref.current !== null && !ref.current.contains(event.target)) onClose();
|
|
4021
|
+
};
|
|
4022
|
+
const onKeyDown = (event) => {
|
|
4023
|
+
if (event.key === "Escape") onClose();
|
|
4024
|
+
};
|
|
4025
|
+
document.addEventListener("pointerdown", onPointerDown, true);
|
|
4026
|
+
document.addEventListener("keydown", onKeyDown);
|
|
4027
|
+
return () => {
|
|
4028
|
+
document.removeEventListener("pointerdown", onPointerDown, true);
|
|
4029
|
+
document.removeEventListener("keydown", onKeyDown);
|
|
4030
|
+
};
|
|
4031
|
+
}, [onClose]);
|
|
4032
|
+
(0, react.useLayoutEffect)(() => {
|
|
4033
|
+
const el = ref.current;
|
|
4034
|
+
if (el === null) return;
|
|
4035
|
+
const rect = el.getBoundingClientRect();
|
|
4036
|
+
const left = Math.max(VIEWPORT_MARGIN, Math.min(anchor.left, window.innerWidth - rect.width - VIEWPORT_MARGIN));
|
|
4037
|
+
let top = anchor.top;
|
|
4038
|
+
if (top + rect.height > window.innerHeight - VIEWPORT_MARGIN) top = Math.max(VIEWPORT_MARGIN, anchor.top - rect.height - VIEWPORT_MARGIN);
|
|
4039
|
+
el.style.left = `${String(Math.round(left))}px`;
|
|
4040
|
+
el.style.top = `${String(Math.round(top))}px`;
|
|
4041
|
+
}, [anchor]);
|
|
4042
|
+
return (0, react_dom.createPortal)(/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4043
|
+
ref,
|
|
4044
|
+
className: `${GitHistoryPanel_module_default.menu} ${GitHistoryPanel_module_default.floatMenu}`,
|
|
4045
|
+
role,
|
|
4046
|
+
...label === void 0 ? {} : { "aria-label": label },
|
|
4047
|
+
children
|
|
4048
|
+
}), document.body);
|
|
4049
|
+
}
|
|
4050
|
+
//#endregion
|
|
3682
4051
|
//#region src/client/panel/BranchSection.tsx
|
|
3683
4052
|
/**
|
|
3684
4053
|
* BranchSection: the panel's middle block — the branch list with an IDEA-style
|
|
@@ -3716,35 +4085,9 @@ window.__ModuleLoader__.load({
|
|
|
3716
4085
|
function BranchFloatMenu(props) {
|
|
3717
4086
|
const { state, t, busy, onAction, onClose } = props;
|
|
3718
4087
|
const { branch } = state;
|
|
3719
|
-
|
|
3720
|
-
|
|
3721
|
-
|
|
3722
|
-
if (ref.current !== null && !ref.current.contains(event.target)) onClose();
|
|
3723
|
-
};
|
|
3724
|
-
const onKeyDown = (event) => {
|
|
3725
|
-
if (event.key === "Escape") onClose();
|
|
3726
|
-
};
|
|
3727
|
-
document.addEventListener("pointerdown", onPointerDown, true);
|
|
3728
|
-
document.addEventListener("keydown", onKeyDown);
|
|
3729
|
-
return () => {
|
|
3730
|
-
document.removeEventListener("pointerdown", onPointerDown, true);
|
|
3731
|
-
document.removeEventListener("keydown", onKeyDown);
|
|
3732
|
-
};
|
|
3733
|
-
}, [onClose]);
|
|
3734
|
-
(0, react.useLayoutEffect)(() => {
|
|
3735
|
-
const el = ref.current;
|
|
3736
|
-
if (el === null) return;
|
|
3737
|
-
const rect = el.getBoundingClientRect();
|
|
3738
|
-
const left = Math.max(8, Math.min(state.rect.left, window.innerWidth - rect.width - 8));
|
|
3739
|
-
let top = state.rect.top;
|
|
3740
|
-
if (top + rect.height > window.innerHeight - 8) top = Math.max(8, state.rect.top - rect.height - 8);
|
|
3741
|
-
el.style.left = `${String(Math.round(left))}px`;
|
|
3742
|
-
el.style.top = `${String(Math.round(top))}px`;
|
|
3743
|
-
}, [state]);
|
|
3744
|
-
return (0, react_dom.createPortal)(/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3745
|
-
ref,
|
|
3746
|
-
className: `${GitHistoryPanel_module_default.menu} ${GitHistoryPanel_module_default.floatMenu}`,
|
|
3747
|
-
role: "menu",
|
|
4088
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(FloatMenu, {
|
|
4089
|
+
anchor: state.rect,
|
|
4090
|
+
onClose,
|
|
3748
4091
|
children: [
|
|
3749
4092
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3750
4093
|
className: GitHistoryPanel_module_default.menuHead,
|
|
@@ -3799,7 +4142,7 @@ window.__ModuleLoader__.load({
|
|
|
3799
4142
|
children: t("panel.branchCopy")
|
|
3800
4143
|
})
|
|
3801
4144
|
]
|
|
3802
|
-
})
|
|
4145
|
+
});
|
|
3803
4146
|
}
|
|
3804
4147
|
/** The branches section. */
|
|
3805
4148
|
function BranchSection(props) {
|
|
@@ -4116,73 +4459,390 @@ window.__ModuleLoader__.load({
|
|
|
4116
4459
|
});
|
|
4117
4460
|
}
|
|
4118
4461
|
//#endregion
|
|
4119
|
-
//#region src/client/panel/
|
|
4462
|
+
//#region src/client/panel/CnAcceleratePopover.tsx
|
|
4120
4463
|
/**
|
|
4121
|
-
*
|
|
4122
|
-
* the subject, its refs badges, the author and the relative time, and the
|
|
4123
|
-
* file count with the short hash; hovering a row raises the framework
|
|
4124
|
-
* Tooltip (subject · author <email> · absolute time · hash); CLICKING a row
|
|
4125
|
-
* expands the commit's changed files INLINE below it (exactly one open at a
|
|
4126
|
-
* time), loading them on first open. The search input filters the loaded
|
|
4127
|
-
* page client-side; "load more" fetches the next page.
|
|
4464
|
+
* CnAcceleratePopover: the 国内加速 popover under the topbar button.
|
|
4128
4465
|
*
|
|
4129
|
-
*
|
|
4466
|
+
* This is the SHORTCUT surface of the same staged form the settings card row
|
|
4467
|
+
* edits ({@link ../accel-form}): enable switch + mirror URL stage together
|
|
4468
|
+
* and commit together on 保存. Closing the popover — Esc, outside click, 取消
|
|
4469
|
+
* — discards the staged edits through the owner's close handler, so an
|
|
4470
|
+
* abandoned draft can never land half-written.
|
|
4471
|
+
*
|
|
4472
|
+
* Layout follows docs/design/cn-accelerate.md §2.2: title + switch → security
|
|
4473
|
+
* hint → URL field with 恢复默认 → error line → footer.
|
|
4474
|
+
*
|
|
4475
|
+
* @module git-worktree/client/panel/CnAcceleratePopover
|
|
4130
4476
|
*/
|
|
4131
|
-
function
|
|
4132
|
-
|
|
4133
|
-
|
|
4134
|
-
|
|
4135
|
-
|
|
4136
|
-
|
|
4137
|
-
|
|
4138
|
-
|
|
4139
|
-
|
|
4140
|
-
|
|
4477
|
+
function CnAcceleratePopover(props) {
|
|
4478
|
+
const { accel, t, pushToast, onClose } = props;
|
|
4479
|
+
const rootRef = (0, react.useRef)(null);
|
|
4480
|
+
const state = (0, react.useSyncExternalStore)(accel.store.subscribe, accel.store.getSnapshot);
|
|
4481
|
+
(0, react.useEffect)(() => {
|
|
4482
|
+
const onKeyDown = (event) => {
|
|
4483
|
+
if (event.key === "Escape") onClose();
|
|
4484
|
+
};
|
|
4485
|
+
const onPointerDown = (event) => {
|
|
4486
|
+
if (rootRef.current !== null && !rootRef.current.contains(event.target)) onClose();
|
|
4487
|
+
};
|
|
4488
|
+
document.addEventListener("keydown", onKeyDown);
|
|
4489
|
+
document.addEventListener("pointerdown", onPointerDown);
|
|
4490
|
+
return () => {
|
|
4491
|
+
document.removeEventListener("keydown", onKeyDown);
|
|
4492
|
+
document.removeEventListener("pointerdown", onPointerDown);
|
|
4493
|
+
};
|
|
4494
|
+
}, [onClose]);
|
|
4495
|
+
const locked = state.saving || !state.writable;
|
|
4496
|
+
const onSave = (0, react.useCallback)(() => {
|
|
4497
|
+
if (state.saving || state.urlError !== null || !state.writable) return;
|
|
4498
|
+
(async () => {
|
|
4499
|
+
await accel.saveAccel();
|
|
4500
|
+
const after = accel.store.getSnapshot();
|
|
4501
|
+
if (after.failed) {
|
|
4502
|
+
pushToast(t("accel.saveFailed"), "error");
|
|
4503
|
+
return;
|
|
4504
|
+
}
|
|
4505
|
+
pushToast(after.enabled ? t("accel.savedOn", { url: after.urlText }) : t("accel.savedOff"), "success");
|
|
4506
|
+
onClose();
|
|
4507
|
+
})();
|
|
4508
|
+
}, [
|
|
4509
|
+
accel,
|
|
4510
|
+
onClose,
|
|
4511
|
+
pushToast,
|
|
4512
|
+
state.saving,
|
|
4513
|
+
state.urlError,
|
|
4514
|
+
state.writable,
|
|
4515
|
+
t
|
|
4516
|
+
]);
|
|
4141
4517
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4142
|
-
className: GitHistoryPanel_module_default.
|
|
4518
|
+
className: GitHistoryPanel_module_default.cnPop,
|
|
4519
|
+
ref: rootRef,
|
|
4520
|
+
role: "dialog",
|
|
4521
|
+
"aria-label": t("panel.cnAccelerate"),
|
|
4143
4522
|
children: [
|
|
4144
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
4145
|
-
className:
|
|
4146
|
-
|
|
4147
|
-
|
|
4523
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4524
|
+
className: GitHistoryPanel_module_default.cnPopHead,
|
|
4525
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4526
|
+
className: GitHistoryPanel_module_default.cnPopTitle,
|
|
4527
|
+
children: t("panel.cnAccelerate")
|
|
4528
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
4529
|
+
className: GitHistoryPanel_module_default.cnSwitchRow,
|
|
4530
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("accel.enable") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
4531
|
+
type: "checkbox",
|
|
4532
|
+
className: GitHistoryPanel_module_default.cnSwitch,
|
|
4533
|
+
checked: state.enabled,
|
|
4534
|
+
disabled: locked,
|
|
4535
|
+
onChange: (event) => accel.setAccelEnabled(event.target.checked)
|
|
4536
|
+
})]
|
|
4537
|
+
})]
|
|
4148
4538
|
}),
|
|
4149
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("
|
|
4150
|
-
className: GitHistoryPanel_module_default.
|
|
4151
|
-
|
|
4152
|
-
children: file.oldPath === void 0 ? file.path : `${file.oldPath} → ${file.path}`
|
|
4539
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
4540
|
+
className: GitHistoryPanel_module_default.cnHint,
|
|
4541
|
+
children: t("accel.hint")
|
|
4153
4542
|
}),
|
|
4154
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
4155
|
-
className: GitHistoryPanel_module_default.
|
|
4156
|
-
children:
|
|
4157
|
-
|
|
4158
|
-
|
|
4159
|
-
|
|
4160
|
-
|
|
4161
|
-
|
|
4162
|
-
|
|
4163
|
-
|
|
4164
|
-
|
|
4165
|
-
|
|
4166
|
-
|
|
4167
|
-
|
|
4168
|
-
|
|
4543
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4544
|
+
className: GitHistoryPanel_module_default.cnFieldHead,
|
|
4545
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4546
|
+
className: GitHistoryPanel_module_default.cnFieldLabel,
|
|
4547
|
+
children: t("accel.urlLabel")
|
|
4548
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4549
|
+
type: "button",
|
|
4550
|
+
className: GitHistoryPanel_module_default.cnLink,
|
|
4551
|
+
disabled: locked,
|
|
4552
|
+
onClick: () => accel.restoreAccelDefault(),
|
|
4553
|
+
children: t("accel.restoreDefault")
|
|
4554
|
+
})]
|
|
4555
|
+
}),
|
|
4556
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
4557
|
+
type: "text",
|
|
4558
|
+
className: state.urlError !== null ? GitHistoryPanel_module_default.cnInputBad : GitHistoryPanel_module_default.cnInput,
|
|
4559
|
+
value: state.urlText,
|
|
4560
|
+
spellCheck: false,
|
|
4561
|
+
disabled: locked,
|
|
4562
|
+
"aria-invalid": state.urlError !== null,
|
|
4563
|
+
onChange: (event) => accel.editAccelUrl(event.target.value),
|
|
4564
|
+
onKeyDown: (event) => {
|
|
4565
|
+
if (event.key === "Enter") onSave();
|
|
4566
|
+
}
|
|
4567
|
+
}),
|
|
4568
|
+
state.urlError !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4569
|
+
className: GitHistoryPanel_module_default.cnError,
|
|
4570
|
+
children: state.urlError === "empty" ? t("accel.urlEmpty") : t("accel.urlInvalid")
|
|
4571
|
+
}) : null,
|
|
4572
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4573
|
+
className: GitHistoryPanel_module_default.cnFooter,
|
|
4574
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4575
|
+
type: "button",
|
|
4576
|
+
className: GitHistoryPanel_module_default.cnGhost,
|
|
4577
|
+
onClick: onClose,
|
|
4578
|
+
children: t("cancel")
|
|
4579
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4580
|
+
type: "button",
|
|
4581
|
+
className: GitHistoryPanel_module_default.cnSave,
|
|
4582
|
+
disabled: state.saving || state.urlError !== null || !state.writable,
|
|
4583
|
+
onClick: onSave,
|
|
4584
|
+
children: t("cardSave")
|
|
4585
|
+
})]
|
|
4586
|
+
})
|
|
4587
|
+
]
|
|
4588
|
+
});
|
|
4589
|
+
}
|
|
4590
|
+
//#endregion
|
|
4591
|
+
//#region src/client/git-graph.ts
|
|
4592
|
+
/** Lazily extend `lanes` so `index` exists. */
|
|
4593
|
+
function ensureLane(lanes, index) {
|
|
4594
|
+
while (lanes.length <= index) lanes.push(null);
|
|
4595
|
+
}
|
|
4596
|
+
/** First free lane, appended when every existing lane is busy. */
|
|
4597
|
+
function takeLane(lanes) {
|
|
4598
|
+
const free = lanes.indexOf(null);
|
|
4599
|
+
if (free >= 0) return free;
|
|
4600
|
+
lanes.push(null);
|
|
4601
|
+
return lanes.length - 1;
|
|
4602
|
+
}
|
|
4603
|
+
/**
|
|
4604
|
+
* Compute one row per commit, in the order given.
|
|
4605
|
+
* @param commits - newest-first commits (git log order).
|
|
4606
|
+
* @returns one {@link GraphRow} per input commit, same order.
|
|
4607
|
+
*/
|
|
4608
|
+
function buildGraph(commits) {
|
|
4609
|
+
/** Lane state at the TOP edge of the current row. */
|
|
4610
|
+
const lanes = [];
|
|
4611
|
+
const rows = [];
|
|
4612
|
+
for (const commit of commits) {
|
|
4613
|
+
const incoming = [];
|
|
4614
|
+
for (let index = 0; index < lanes.length; index += 1) if (lanes[index] === commit.hash) incoming.push(index);
|
|
4615
|
+
const converge = incoming[0];
|
|
4616
|
+
const lane = converge === void 0 ? takeLane(lanes) : converge;
|
|
4617
|
+
ensureLane(lanes, lane);
|
|
4618
|
+
if (converge === void 0) incoming.push(lane);
|
|
4619
|
+
const top = lanes.slice();
|
|
4620
|
+
for (const index of incoming) lanes[index] = null;
|
|
4621
|
+
const parentLanes = [];
|
|
4622
|
+
for (let position = 0; position < commit.parents.length; position += 1) {
|
|
4623
|
+
const parent = commit.parents[position];
|
|
4624
|
+
let target;
|
|
4625
|
+
if (position === 0) {
|
|
4626
|
+
target = lane;
|
|
4627
|
+
ensureLane(lanes, target);
|
|
4628
|
+
} else {
|
|
4629
|
+
const waiting = lanes.indexOf(parent);
|
|
4630
|
+
target = waiting >= 0 ? waiting : takeLane(lanes);
|
|
4631
|
+
}
|
|
4632
|
+
lanes[target] = parent;
|
|
4633
|
+
if (!parentLanes.includes(target)) parentLanes.push(target);
|
|
4634
|
+
}
|
|
4635
|
+
const edges = [];
|
|
4636
|
+
for (let index = 0; index < Math.max(top.length, lanes.length); index += 1) {
|
|
4637
|
+
const above = top[index] ?? null;
|
|
4638
|
+
const below = lanes[index] ?? null;
|
|
4639
|
+
if (above === null) continue;
|
|
4640
|
+
if (below === above && index !== lane) {
|
|
4641
|
+
edges.push({
|
|
4642
|
+
kind: "through",
|
|
4643
|
+
from: index,
|
|
4644
|
+
to: index
|
|
4645
|
+
});
|
|
4646
|
+
continue;
|
|
4647
|
+
}
|
|
4648
|
+
edges.push({
|
|
4649
|
+
kind: "in",
|
|
4650
|
+
from: index,
|
|
4651
|
+
to: lane
|
|
4652
|
+
});
|
|
4653
|
+
}
|
|
4654
|
+
for (const target of parentLanes) edges.push({
|
|
4655
|
+
kind: "out",
|
|
4656
|
+
from: lane,
|
|
4657
|
+
to: target
|
|
4658
|
+
});
|
|
4659
|
+
rows.push({
|
|
4660
|
+
lane,
|
|
4661
|
+
lanes: Math.max(top.length, lanes.length),
|
|
4662
|
+
edges
|
|
4663
|
+
});
|
|
4664
|
+
}
|
|
4665
|
+
return trimWidths(rows);
|
|
4666
|
+
}
|
|
4667
|
+
/**
|
|
4668
|
+
* Narrow each row to the lanes it actually uses: a lane released midway (or
|
|
4669
|
+
* one opened only by a later commit) would otherwise reserve dead space in
|
|
4670
|
+
* every row above it.
|
|
4671
|
+
*/
|
|
4672
|
+
function trimWidths(rows) {
|
|
4673
|
+
const widths = rows.map((row) => {
|
|
4674
|
+
let width = 0;
|
|
4675
|
+
for (const edge of row.edges) width = Math.max(width, edge.from + 1, edge.to + 1);
|
|
4676
|
+
return Math.max(width, row.lane + 1);
|
|
4677
|
+
});
|
|
4678
|
+
return rows.map((row, index) => ({
|
|
4679
|
+
...row,
|
|
4680
|
+
lanes: widths[index] ?? 1
|
|
4681
|
+
}));
|
|
4682
|
+
}
|
|
4683
|
+
/** The widest row of a graph — how many lane columns the gutter needs. */
|
|
4684
|
+
function graphWidth(rows) {
|
|
4685
|
+
return rows.reduce((widest, row) => Math.max(widest, row.lanes), 1);
|
|
4686
|
+
}
|
|
4687
|
+
/** Vertical coordinate system for curves, scaled to the actual row height. */
|
|
4688
|
+
const GRAPH_BAND = 44;
|
|
4689
|
+
const LINE_WIDTH = 1.6;
|
|
4690
|
+
const LANE_COLORS = [
|
|
4691
|
+
"#4c6bf5",
|
|
4692
|
+
"#2f9e44",
|
|
4693
|
+
"#e8590c",
|
|
4694
|
+
"#9c36b5",
|
|
4695
|
+
"#0c8599",
|
|
4696
|
+
"#d6336c",
|
|
4697
|
+
"#f08c00",
|
|
4698
|
+
"#5f3dc4"
|
|
4699
|
+
];
|
|
4700
|
+
function laneColor(lane) {
|
|
4701
|
+
return LANE_COLORS[(lane % LANE_COLORS.length + LANE_COLORS.length) % LANE_COLORS.length];
|
|
4702
|
+
}
|
|
4703
|
+
function cx(lane) {
|
|
4704
|
+
return lane * 14 + 7 + 2;
|
|
4705
|
+
}
|
|
4706
|
+
function bend(from, fromY, to, toY) {
|
|
4707
|
+
const x0 = cx(from);
|
|
4708
|
+
const x1 = cx(to);
|
|
4709
|
+
if (x0 === x1) return `M ${String(x0)} ${String(fromY)} L ${String(x1)} ${String(toY)}`;
|
|
4710
|
+
const mid = (toY - fromY) / 2;
|
|
4711
|
+
return `M ${String(x0)} ${String(fromY)} C ${String(x0)} ${String(fromY + mid)} ${String(x1)} ${String(toY - mid)} ${String(x1)} ${String(toY)}`;
|
|
4712
|
+
}
|
|
4713
|
+
function CommitGraph({ row, width, merge }) {
|
|
4714
|
+
const mid = GRAPH_BAND / 2;
|
|
4715
|
+
const dotColor = laneColor(row.lane);
|
|
4716
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
|
|
4717
|
+
className: GitHistoryPanel_module_default.commitGraphSvg,
|
|
4718
|
+
width,
|
|
4719
|
+
height: "100%",
|
|
4720
|
+
viewBox: `0 0 ${String(width)} ${String(GRAPH_BAND)}`,
|
|
4721
|
+
preserveAspectRatio: "none",
|
|
4722
|
+
"aria-hidden": "true",
|
|
4723
|
+
focusable: "false",
|
|
4724
|
+
children: row.edges.map((edge, index) => {
|
|
4725
|
+
const [fromY, toY] = edge.kind === "through" ? [0, GRAPH_BAND] : edge.kind === "in" ? [0, mid] : [mid, GRAPH_BAND];
|
|
4726
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
4727
|
+
d: bend(edge.from, fromY, edge.to, toY),
|
|
4728
|
+
fill: "none",
|
|
4729
|
+
stroke: laneColor(edge.kind === "out" ? edge.to : edge.from),
|
|
4730
|
+
strokeWidth: LINE_WIDTH,
|
|
4731
|
+
strokeLinecap: "round",
|
|
4732
|
+
vectorEffect: "non-scaling-stroke"
|
|
4733
|
+
}, `${edge.kind}:${String(edge.from)}:${String(edge.to)}:${String(index)}`);
|
|
4734
|
+
})
|
|
4735
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4736
|
+
className: GitHistoryPanel_module_default.commitDot,
|
|
4737
|
+
style: {
|
|
4738
|
+
left: cx(row.lane),
|
|
4739
|
+
color: dotColor,
|
|
4740
|
+
backgroundColor: merge ? "transparent" : dotColor
|
|
4741
|
+
},
|
|
4742
|
+
"aria-hidden": "true"
|
|
4743
|
+
})] });
|
|
4744
|
+
}
|
|
4745
|
+
/** Only bottom-edge lanes continue through an expanded file list. Incoming
|
|
4746
|
+
* lanes already terminate at the dot and must not be resurrected below it. */
|
|
4747
|
+
function CommitGraphContinuation({ row, width }) {
|
|
4748
|
+
const lanes = [...new Set(row.edges.filter((edge) => edge.kind !== "in").map((edge) => edge.to))];
|
|
4749
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
|
|
4750
|
+
className: GitHistoryPanel_module_default.commitExpandGraph,
|
|
4751
|
+
width,
|
|
4752
|
+
height: "100%",
|
|
4753
|
+
"aria-hidden": "true",
|
|
4754
|
+
focusable: "false",
|
|
4755
|
+
children: lanes.map((lane) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("line", {
|
|
4756
|
+
x1: cx(lane),
|
|
4757
|
+
x2: cx(lane),
|
|
4758
|
+
y1: "0",
|
|
4759
|
+
y2: "100%",
|
|
4760
|
+
stroke: laneColor(lane),
|
|
4761
|
+
strokeWidth: LINE_WIDTH,
|
|
4762
|
+
strokeLinecap: "round"
|
|
4763
|
+
}, lane))
|
|
4764
|
+
});
|
|
4765
|
+
}
|
|
4766
|
+
//#endregion
|
|
4767
|
+
//#region src/client/panel/HistorySection.tsx
|
|
4768
|
+
/**
|
|
4769
|
+
* HistorySection: the panel's third block — the commit graph. Every row
|
|
4770
|
+
* draws its lane picture (vertical rails, curved merges, hollow dots for
|
|
4771
|
+
* merge commits) from `buildGraph`, then the subject, its refs badges, the
|
|
4772
|
+
* author, and the absolute commit time. CLICKING a row expands the commit's
|
|
4773
|
+
* changed files INLINE below it (exactly one open at a time), loading them on
|
|
4774
|
+
* first open; RIGHT-CLICKING it opens the commit menu (copy hash, copy
|
|
4775
|
+
* subject, new branch here, tag here), which is where the history card's two
|
|
4776
|
+
* writing verbs live.
|
|
4777
|
+
*
|
|
4778
|
+
* The head carries the three filters the card asks for, in this order —
|
|
4779
|
+
* author, branch (a ref to pin the walk on, or every ref), and a time window
|
|
4780
|
+
* (all / last 24h / last 7 days / a custom day range). They are NOT applied
|
|
4781
|
+
* here: this section is presentational, so it hands each change up and the
|
|
4782
|
+
* data owner refetches.
|
|
4783
|
+
* Filtering client-side would have been wrong for a paged list — the page
|
|
4784
|
+
* holds 50 commits, and a needle outside it would silently report "no match".
|
|
4785
|
+
*
|
|
4786
|
+
* @module git-worktree/client/panel/HistorySection
|
|
4787
|
+
*/
|
|
4788
|
+
/** `YYYY/MM/DD HH:mm` in the browser's zone — the reference screenshot's
|
|
4789
|
+
* right-hand time column. Field-by-field on purpose: `toLocaleString` varies
|
|
4790
|
+
* with the user's locale and would break the column's alignment. */
|
|
4791
|
+
function formatStamp(date) {
|
|
4792
|
+
if (!Number.isFinite(date) || date <= 0) return "";
|
|
4793
|
+
const at = new Date(date);
|
|
4794
|
+
const pad = (value) => String(value).padStart(2, "0");
|
|
4795
|
+
return `${String(at.getFullYear())}/${pad(at.getMonth() + 1)}/${pad(at.getDate())} ${pad(at.getHours())}:${pad(at.getMinutes())}`;
|
|
4796
|
+
}
|
|
4797
|
+
function statusTitle$1(status, t) {
|
|
4798
|
+
if (status === "A") return t("panel.stAdd");
|
|
4799
|
+
if (status === "D") return t("panel.stDel");
|
|
4800
|
+
if (status === "R") return t("panel.stRen");
|
|
4801
|
+
if (status === "C") return t("panel.stCopy");
|
|
4802
|
+
if (status === "T") return t("panel.stType");
|
|
4803
|
+
return t("panel.stMod");
|
|
4804
|
+
}
|
|
4805
|
+
function FileRow(props) {
|
|
4806
|
+
const { file, t } = props;
|
|
4807
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4808
|
+
className: GitHistoryPanel_module_default.fileRow,
|
|
4809
|
+
children: [
|
|
4810
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4811
|
+
className: `${GitHistoryPanel_module_default.statusBadge} ${GitHistoryPanel_module_default[`status${file.status}`]}`,
|
|
4812
|
+
title: statusTitle$1(file.status, t),
|
|
4813
|
+
children: file.status
|
|
4814
|
+
}),
|
|
4815
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4816
|
+
className: GitHistoryPanel_module_default.filePath,
|
|
4817
|
+
title: file.oldPath === void 0 ? file.path : `${file.oldPath} → ${file.path}`,
|
|
4818
|
+
children: file.oldPath === void 0 ? file.path : `${file.oldPath} → ${file.path}`
|
|
4819
|
+
}),
|
|
4820
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4821
|
+
className: GitHistoryPanel_module_default.fileNums,
|
|
4822
|
+
children: file.binary === true ? t("panel.binary") : file.insertions !== void 0 || file.deletions !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
4823
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", {
|
|
4824
|
+
className: GitHistoryPanel_module_default.fileAdd,
|
|
4825
|
+
children: `+${String(file.insertions ?? 0)}`
|
|
4826
|
+
}),
|
|
4827
|
+
" ",
|
|
4828
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", {
|
|
4829
|
+
className: GitHistoryPanel_module_default.fileDel,
|
|
4830
|
+
children: `-${String(file.deletions ?? 0)}`
|
|
4831
|
+
})
|
|
4832
|
+
] }) : ""
|
|
4833
|
+
})
|
|
4834
|
+
]
|
|
4169
4835
|
});
|
|
4170
4836
|
}
|
|
4171
4837
|
/** One history row; `ref` and the tooltip's hover handlers arrive through
|
|
4172
4838
|
* the anchor share when the row is wrapped in a Tooltip. */
|
|
4173
4839
|
const CommitRow = (0, react.forwardRef)(function CommitRow(props, ref) {
|
|
4174
|
-
const { commit,
|
|
4840
|
+
const { commit, graph, graphWidth, open, files, t, onToggle, onMenu } = props;
|
|
4175
4841
|
const badges = classifyRefs(commit.refs);
|
|
4176
|
-
const
|
|
4177
|
-
const absolute = Number.isFinite(commit.date) && commit.date > 0 ? `${when.toLocaleDateString()} ${when.toLocaleTimeString()}` : "";
|
|
4842
|
+
const stamp = formatStamp(commit.date);
|
|
4178
4843
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4179
4844
|
ref,
|
|
4180
|
-
className: [
|
|
4181
|
-
GitHistoryPanel_module_default.commitRow,
|
|
4182
|
-
first ? GitHistoryPanel_module_default.commitRowFirst : "",
|
|
4183
|
-
last ? GitHistoryPanel_module_default.commitRowLast : "",
|
|
4184
|
-
open ? GitHistoryPanel_module_default.commitRowOpen : ""
|
|
4185
|
-
].filter((name) => name !== "").join(" "),
|
|
4845
|
+
className: [GitHistoryPanel_module_default.commitRow, open ? GitHistoryPanel_module_default.commitRowOpen : ""].filter((name) => name !== "").join(" "),
|
|
4186
4846
|
role: "button",
|
|
4187
4847
|
tabIndex: 0,
|
|
4188
4848
|
"aria-expanded": open,
|
|
@@ -4192,6 +4852,13 @@ window.__ModuleLoader__.load({
|
|
|
4192
4852
|
onFocus: props.onFocus,
|
|
4193
4853
|
onBlur: props.onBlur,
|
|
4194
4854
|
onClick: () => onToggle(commit.hash),
|
|
4855
|
+
onContextMenu: (event) => {
|
|
4856
|
+
event.preventDefault();
|
|
4857
|
+
onMenu(commit, {
|
|
4858
|
+
left: event.clientX,
|
|
4859
|
+
top: event.clientY
|
|
4860
|
+
});
|
|
4861
|
+
},
|
|
4195
4862
|
onKeyDown: (event) => {
|
|
4196
4863
|
if (event.key === "Enter" || event.key === " ") {
|
|
4197
4864
|
event.preventDefault();
|
|
@@ -4201,7 +4868,11 @@ window.__ModuleLoader__.load({
|
|
|
4201
4868
|
children: [
|
|
4202
4869
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4203
4870
|
className: GitHistoryPanel_module_default.commitGraph,
|
|
4204
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
4871
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CommitGraph, {
|
|
4872
|
+
row: graph,
|
|
4873
|
+
width: graphWidth,
|
|
4874
|
+
merge: commit.parents.length > 1
|
|
4875
|
+
})
|
|
4205
4876
|
}),
|
|
4206
4877
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4207
4878
|
className: GitHistoryPanel_module_default.commitMain,
|
|
@@ -4217,37 +4888,48 @@ window.__ModuleLoader__.load({
|
|
|
4217
4888
|
children: badge.text
|
|
4218
4889
|
}, `${badge.kind}:${badge.text}`))
|
|
4219
4890
|
})]
|
|
4220
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.
|
|
4891
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4221
4892
|
className: GitHistoryPanel_module_default.commitMeta,
|
|
4222
|
-
children:
|
|
4893
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4894
|
+
className: GitHistoryPanel_module_default.commitAuthor,
|
|
4895
|
+
children: commit.authorName
|
|
4896
|
+
})
|
|
4223
4897
|
})]
|
|
4224
4898
|
}),
|
|
4225
4899
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4226
4900
|
className: GitHistoryPanel_module_default.commitSide,
|
|
4227
4901
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4228
|
-
className: GitHistoryPanel_module_default.
|
|
4229
|
-
|
|
4902
|
+
className: GitHistoryPanel_module_default.commitDate,
|
|
4903
|
+
title: stamp,
|
|
4904
|
+
children: stamp
|
|
4230
4905
|
})
|
|
4231
4906
|
}),
|
|
4232
4907
|
open ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4233
4908
|
className: GitHistoryPanel_module_default.commitExpand,
|
|
4234
|
-
children: [
|
|
4235
|
-
|
|
4236
|
-
|
|
4237
|
-
|
|
4238
|
-
|
|
4239
|
-
|
|
4240
|
-
|
|
4241
|
-
|
|
4242
|
-
|
|
4243
|
-
|
|
4244
|
-
|
|
4245
|
-
|
|
4246
|
-
|
|
4247
|
-
|
|
4248
|
-
|
|
4249
|
-
|
|
4250
|
-
|
|
4909
|
+
children: [
|
|
4910
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(CommitGraphContinuation, {
|
|
4911
|
+
row: graph,
|
|
4912
|
+
width: graphWidth
|
|
4913
|
+
}),
|
|
4914
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4915
|
+
className: GitHistoryPanel_module_default.commitExpandTitle,
|
|
4916
|
+
children: t("panel.filesTitle", {
|
|
4917
|
+
n: files?.kind === "loaded" ? files.files.length : 0,
|
|
4918
|
+
author: commit.authorName,
|
|
4919
|
+
date: stamp
|
|
4920
|
+
})
|
|
4921
|
+
}),
|
|
4922
|
+
files === void 0 || files.kind === "loading" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4923
|
+
className: GitHistoryPanel_module_default.commitExpandTitle,
|
|
4924
|
+
children: t("manager.loading")
|
|
4925
|
+
}) : files.kind === "failed" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4926
|
+
className: GitHistoryPanel_module_default.errorText,
|
|
4927
|
+
children: files.message
|
|
4928
|
+
}) : files.files.map((file) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FileRow, {
|
|
4929
|
+
file,
|
|
4930
|
+
t
|
|
4931
|
+
}, `${file.status}:${file.path}`))
|
|
4932
|
+
]
|
|
4251
4933
|
}) : null
|
|
4252
4934
|
]
|
|
4253
4935
|
});
|
|
@@ -4259,70 +4941,303 @@ window.__ModuleLoader__.load({
|
|
|
4259
4941
|
return `${commit.subject} · ${commit.authorName} <${commit.authorEmail}> · ${absolute} · ${commit.short}`;
|
|
4260
4942
|
}
|
|
4261
4943
|
function HistorySection(props) {
|
|
4262
|
-
const { t, commits, loaded, loadError, loading, hasMore, loadingMore, expanded, filesByHash, onLoadMore, onToggle,
|
|
4263
|
-
const [
|
|
4264
|
-
const
|
|
4265
|
-
const
|
|
4944
|
+
const { t, commits, loaded, loadError, loading, hasMore, loadingMore, expanded, filesByHash, filters, branches, busy, onLoadMore, onToggle, onFilters, onCreateBranch, onCreateTag, pushToast } = props;
|
|
4945
|
+
const [menu, setMenu] = (0, react.useState)(null);
|
|
4946
|
+
const [dialog, setDialog] = (0, react.useState)(null);
|
|
4947
|
+
const [draft, setDraft] = (0, react.useState)("");
|
|
4948
|
+
const [message, setMessage] = (0, react.useState)("");
|
|
4949
|
+
const [authorDraft, setAuthorDraft] = (0, react.useState)(filters.author);
|
|
4950
|
+
const graph = (0, react.useMemo)(() => buildGraph(commits), [commits]);
|
|
4951
|
+
const bandWidth = (0, react.useMemo)(() => graphWidth(graph) * 14 + 4, [graph]);
|
|
4952
|
+
const openMenu = (commit, rect) => {
|
|
4953
|
+
setMenu({
|
|
4954
|
+
commit,
|
|
4955
|
+
rect
|
|
4956
|
+
});
|
|
4957
|
+
};
|
|
4958
|
+
const openDialog = (kind, commit) => {
|
|
4959
|
+
setMenu(null);
|
|
4960
|
+
setDraft("");
|
|
4961
|
+
setMessage("");
|
|
4962
|
+
setDialog({
|
|
4963
|
+
kind,
|
|
4964
|
+
commit
|
|
4965
|
+
});
|
|
4966
|
+
};
|
|
4967
|
+
const copy = (text) => {
|
|
4968
|
+
setMenu(null);
|
|
4969
|
+
navigator.clipboard.writeText(text).then(() => pushToast(t("hover.copied"), "success"), () => pushToast(t("errorGeneric", { message: t("hover.copied") }), "error"));
|
|
4970
|
+
};
|
|
4971
|
+
const nameIssue = draft.trim() === "" ? "empty" : branchNameIssue(draft.trim());
|
|
4972
|
+
const duplicateBranch = dialog !== null && dialog.kind === "branch" && branches.includes(draft.trim());
|
|
4973
|
+
const dialogError = nameIssue === "empty" ? t("menuNewBranchPlaceholder") : nameIssue !== null ? t("menuNewBranchBad") : duplicateBranch ? t("menuNewBranchExists") : void 0;
|
|
4974
|
+
const submitDialog = () => {
|
|
4975
|
+
if (dialog === null || nameIssue !== null || duplicateBranch) return;
|
|
4976
|
+
const name = draft.trim();
|
|
4977
|
+
if (dialog.kind === "branch") onCreateBranch(name, dialog.commit.hash);
|
|
4978
|
+
else onCreateTag(name, dialog.commit.hash, message.trim());
|
|
4979
|
+
setDialog(null);
|
|
4980
|
+
setDraft("");
|
|
4981
|
+
setMessage("");
|
|
4982
|
+
};
|
|
4983
|
+
/** Commit a pending author needle — Enter or blur, never per keystroke: the
|
|
4984
|
+
* filter is a server-side walk, and one request per character would queue
|
|
4985
|
+
* dozens of `git log` runs behind a single word. */
|
|
4986
|
+
const commitAuthor = () => {
|
|
4987
|
+
const author = authorDraft.trim();
|
|
4988
|
+
if (author === filters.author) return;
|
|
4989
|
+
onFilters({
|
|
4990
|
+
...filters,
|
|
4991
|
+
author
|
|
4992
|
+
});
|
|
4993
|
+
};
|
|
4994
|
+
const filterBar = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4995
|
+
className: GitHistoryPanel_module_default.historyFilters,
|
|
4996
|
+
children: [
|
|
4997
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
4998
|
+
type: "text",
|
|
4999
|
+
className: GitHistoryPanel_module_default.filterInput,
|
|
5000
|
+
value: authorDraft,
|
|
5001
|
+
placeholder: t("panel.historyAuthor"),
|
|
5002
|
+
"aria-label": t("panel.historyAuthor"),
|
|
5003
|
+
onChange: (event) => setAuthorDraft(event.target.value),
|
|
5004
|
+
onBlur: commitAuthor,
|
|
5005
|
+
onKeyDown: (event) => {
|
|
5006
|
+
if (event.key === "Enter") {
|
|
5007
|
+
event.preventDefault();
|
|
5008
|
+
commitAuthor();
|
|
5009
|
+
}
|
|
5010
|
+
}
|
|
5011
|
+
}),
|
|
5012
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
5013
|
+
className: GitHistoryPanel_module_default.filterSelect,
|
|
5014
|
+
value: filters.branch,
|
|
5015
|
+
"aria-label": t("panel.historyBranch"),
|
|
5016
|
+
onChange: (event) => onFilters({
|
|
5017
|
+
...filters,
|
|
5018
|
+
branch: event.target.value
|
|
5019
|
+
}),
|
|
5020
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
5021
|
+
value: "",
|
|
5022
|
+
children: t("panel.historyAllRefs")
|
|
5023
|
+
}), branches.map((name) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
5024
|
+
value: name,
|
|
5025
|
+
children: name
|
|
5026
|
+
}, name))]
|
|
5027
|
+
}),
|
|
5028
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
5029
|
+
className: GitHistoryPanel_module_default.filterSelect,
|
|
5030
|
+
value: filters.range,
|
|
5031
|
+
"aria-label": t("panel.historyTime"),
|
|
5032
|
+
onChange: (event) => onFilters({
|
|
5033
|
+
...filters,
|
|
5034
|
+
range: event.target.value
|
|
5035
|
+
}),
|
|
5036
|
+
children: [
|
|
5037
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
5038
|
+
value: "all",
|
|
5039
|
+
children: t("panel.rangeAll")
|
|
5040
|
+
}),
|
|
5041
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
5042
|
+
value: "day",
|
|
5043
|
+
children: t("panel.rangeDay")
|
|
5044
|
+
}),
|
|
5045
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
5046
|
+
value: "week",
|
|
5047
|
+
children: t("panel.rangeWeek")
|
|
5048
|
+
}),
|
|
5049
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
5050
|
+
value: "custom",
|
|
5051
|
+
children: t("panel.rangeCustom")
|
|
5052
|
+
})
|
|
5053
|
+
]
|
|
5054
|
+
}),
|
|
5055
|
+
filters.range === "custom" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
5056
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
5057
|
+
type: "date",
|
|
5058
|
+
className: GitHistoryPanel_module_default.filterDate,
|
|
5059
|
+
value: filters.customSince,
|
|
5060
|
+
"aria-label": t("panel.rangeSince"),
|
|
5061
|
+
onChange: (event) => onFilters({
|
|
5062
|
+
...filters,
|
|
5063
|
+
customSince: event.target.value
|
|
5064
|
+
})
|
|
5065
|
+
}),
|
|
5066
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5067
|
+
className: GitHistoryPanel_module_default.filterDash,
|
|
5068
|
+
"aria-hidden": "true",
|
|
5069
|
+
children: "–"
|
|
5070
|
+
}),
|
|
5071
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
5072
|
+
type: "date",
|
|
5073
|
+
className: GitHistoryPanel_module_default.filterDate,
|
|
5074
|
+
value: filters.customUntil,
|
|
5075
|
+
"aria-label": t("panel.rangeUntil"),
|
|
5076
|
+
onChange: (event) => onFilters({
|
|
5077
|
+
...filters,
|
|
5078
|
+
customUntil: event.target.value
|
|
5079
|
+
})
|
|
5080
|
+
})
|
|
5081
|
+
] }) : null
|
|
5082
|
+
]
|
|
5083
|
+
});
|
|
5084
|
+
const rows = commits.map((commit, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
5085
|
+
label: () => tooltipLabel(commit),
|
|
5086
|
+
side: "bottom",
|
|
5087
|
+
portal: true,
|
|
5088
|
+
maxWidth: 380,
|
|
5089
|
+
delayMs: 250,
|
|
5090
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CommitRow, {
|
|
5091
|
+
commit,
|
|
5092
|
+
graph: graph[index] ?? {
|
|
5093
|
+
lane: 0,
|
|
5094
|
+
lanes: 1,
|
|
5095
|
+
edges: []
|
|
5096
|
+
},
|
|
5097
|
+
graphWidth: bandWidth,
|
|
5098
|
+
open: expanded === commit.hash,
|
|
5099
|
+
files: filesByHash[commit.hash],
|
|
5100
|
+
t,
|
|
5101
|
+
onToggle,
|
|
5102
|
+
onMenu: openMenu
|
|
5103
|
+
})
|
|
5104
|
+
}, commit.hash));
|
|
5105
|
+
const dialogTitle = dialog === null ? "" : dialog.kind === "branch" ? t("createFlyAskFrom", { branch: dialog.commit.short }) : t("panel.tagTitle");
|
|
4266
5106
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
4267
5107
|
className: `${GitHistoryPanel_module_default.section} ${GitHistoryPanel_module_default.sectionFill} ${GitHistoryPanel_module_default.sectionHistory}`,
|
|
4268
5108
|
"aria-label": t("panel.history"),
|
|
4269
|
-
children: [
|
|
4270
|
-
|
|
4271
|
-
|
|
4272
|
-
|
|
4273
|
-
|
|
4274
|
-
|
|
4275
|
-
|
|
4276
|
-
|
|
4277
|
-
|
|
4278
|
-
|
|
4279
|
-
|
|
4280
|
-
|
|
4281
|
-
className: GitHistoryPanel_module_default.
|
|
4282
|
-
|
|
4283
|
-
|
|
4284
|
-
|
|
4285
|
-
|
|
4286
|
-
})
|
|
4287
|
-
|
|
4288
|
-
|
|
4289
|
-
|
|
4290
|
-
|
|
4291
|
-
|
|
4292
|
-
|
|
4293
|
-
|
|
4294
|
-
|
|
4295
|
-
|
|
4296
|
-
|
|
4297
|
-
|
|
4298
|
-
|
|
4299
|
-
|
|
4300
|
-
|
|
4301
|
-
|
|
4302
|
-
|
|
4303
|
-
|
|
4304
|
-
|
|
4305
|
-
|
|
4306
|
-
|
|
4307
|
-
|
|
4308
|
-
|
|
4309
|
-
|
|
4310
|
-
|
|
4311
|
-
|
|
4312
|
-
|
|
4313
|
-
|
|
4314
|
-
|
|
4315
|
-
|
|
4316
|
-
|
|
4317
|
-
|
|
4318
|
-
|
|
4319
|
-
|
|
4320
|
-
|
|
4321
|
-
|
|
4322
|
-
|
|
4323
|
-
|
|
4324
|
-
|
|
4325
|
-
|
|
5109
|
+
children: [
|
|
5110
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5111
|
+
className: GitHistoryPanel_module_default.historyHead,
|
|
5112
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
|
|
5113
|
+
className: GitHistoryPanel_module_default.sectionTitle,
|
|
5114
|
+
children: t("panel.history")
|
|
5115
|
+
}), filterBar]
|
|
5116
|
+
}),
|
|
5117
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5118
|
+
className: `${GitHistoryPanel_module_default.sectionBody} ${GitHistoryPanel_module_default.sectionBodyScroll}`,
|
|
5119
|
+
style: { "--graph-w": `${String(bandWidth)}px` },
|
|
5120
|
+
children: [loadError !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5121
|
+
className: GitHistoryPanel_module_default.errorText,
|
|
5122
|
+
children: loadError
|
|
5123
|
+
}) : !loaded && loading ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5124
|
+
className: GitHistoryPanel_module_default.empty,
|
|
5125
|
+
children: t("manager.loading")
|
|
5126
|
+
}) : commits.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5127
|
+
className: GitHistoryPanel_module_default.empty,
|
|
5128
|
+
children: t("panel.noCommits")
|
|
5129
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5130
|
+
className: GitHistoryPanel_module_default.commitList,
|
|
5131
|
+
children: rows
|
|
5132
|
+
}), hasMore ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5133
|
+
className: GitHistoryPanel_module_default.moreRow,
|
|
5134
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
5135
|
+
type: "button",
|
|
5136
|
+
className: GitHistoryPanel_module_default.act,
|
|
5137
|
+
disabled: loadingMore,
|
|
5138
|
+
onClick: onLoadMore,
|
|
5139
|
+
children: t("panel.loadMore")
|
|
5140
|
+
})
|
|
5141
|
+
}) : null]
|
|
5142
|
+
}),
|
|
5143
|
+
menu !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(FloatMenu, {
|
|
5144
|
+
anchor: menu.rect,
|
|
5145
|
+
onClose: () => setMenu(null),
|
|
5146
|
+
children: [
|
|
5147
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5148
|
+
className: GitHistoryPanel_module_default.menuHead,
|
|
5149
|
+
children: menu.commit.subject
|
|
5150
|
+
}),
|
|
5151
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
5152
|
+
type: "button",
|
|
5153
|
+
className: GitHistoryPanel_module_default.menuItem,
|
|
5154
|
+
role: "menuitem",
|
|
5155
|
+
onClick: () => copy(menu.commit.hash),
|
|
5156
|
+
children: t("panel.commitCopyHash")
|
|
5157
|
+
}),
|
|
5158
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
5159
|
+
type: "button",
|
|
5160
|
+
className: GitHistoryPanel_module_default.menuItem,
|
|
5161
|
+
role: "menuitem",
|
|
5162
|
+
onClick: () => copy(menu.commit.subject),
|
|
5163
|
+
children: t("panel.commitCopySubject")
|
|
5164
|
+
}),
|
|
5165
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
5166
|
+
type: "button",
|
|
5167
|
+
className: GitHistoryPanel_module_default.menuItem,
|
|
5168
|
+
role: "menuitem",
|
|
5169
|
+
disabled: busy,
|
|
5170
|
+
onClick: () => openDialog("branch", menu.commit),
|
|
5171
|
+
children: t("panel.branchNewFrom")
|
|
5172
|
+
}),
|
|
5173
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
5174
|
+
type: "button",
|
|
5175
|
+
className: GitHistoryPanel_module_default.menuItem,
|
|
5176
|
+
role: "menuitem",
|
|
5177
|
+
disabled: busy,
|
|
5178
|
+
onClick: () => openDialog("tag", menu.commit),
|
|
5179
|
+
children: t("panel.branchTag")
|
|
5180
|
+
})
|
|
5181
|
+
]
|
|
5182
|
+
}) : null,
|
|
5183
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
|
|
5184
|
+
open: dialog !== null,
|
|
5185
|
+
onClose: () => setDialog(null),
|
|
5186
|
+
title: dialogTitle,
|
|
5187
|
+
closeLabel: t("close"),
|
|
5188
|
+
footer: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
5189
|
+
size: "sm",
|
|
5190
|
+
variant: "ghost",
|
|
5191
|
+
onClick: () => setDialog(null),
|
|
5192
|
+
children: t("cancel")
|
|
5193
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
5194
|
+
size: "sm",
|
|
5195
|
+
variant: "primary",
|
|
5196
|
+
disabled: busy || nameIssue !== null || duplicateBranch,
|
|
5197
|
+
onClick: submitDialog,
|
|
5198
|
+
children: t("actionConfirm")
|
|
5199
|
+
})] }),
|
|
5200
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5201
|
+
className: GitHistoryPanel_module_default.formField,
|
|
5202
|
+
children: [
|
|
5203
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
|
|
5204
|
+
className: GitHistoryPanel_module_default.formLabel,
|
|
5205
|
+
htmlFor: "git-panel-commit-name",
|
|
5206
|
+
children: dialog !== null && dialog.kind === "tag" ? t("panel.tagName") : t("panel.branchNew")
|
|
5207
|
+
}),
|
|
5208
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
5209
|
+
id: "git-panel-commit-name",
|
|
5210
|
+
className: GitHistoryPanel_module_default.formInput,
|
|
5211
|
+
"data-modal-autofocus": true,
|
|
5212
|
+
value: draft,
|
|
5213
|
+
onChange: (event) => setDraft(event.target.value),
|
|
5214
|
+
onKeyDown: (event) => {
|
|
5215
|
+
if (event.key === "Enter") submitDialog();
|
|
5216
|
+
}
|
|
5217
|
+
}),
|
|
5218
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5219
|
+
className: GitHistoryPanel_module_default.formError,
|
|
5220
|
+
children: dialogError ?? ""
|
|
5221
|
+
})
|
|
5222
|
+
]
|
|
5223
|
+
}), dialog !== null && dialog.kind === "tag" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5224
|
+
className: GitHistoryPanel_module_default.formField,
|
|
5225
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
|
|
5226
|
+
className: GitHistoryPanel_module_default.formLabel,
|
|
5227
|
+
htmlFor: "git-panel-tag-message",
|
|
5228
|
+
children: t("panel.tagMessage")
|
|
5229
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
5230
|
+
id: "git-panel-tag-message",
|
|
5231
|
+
className: GitHistoryPanel_module_default.formInput,
|
|
5232
|
+
value: message,
|
|
5233
|
+
onChange: (event) => setMessage(event.target.value),
|
|
5234
|
+
onKeyDown: (event) => {
|
|
5235
|
+
if (event.key === "Enter") submitDialog();
|
|
5236
|
+
}
|
|
5237
|
+
})]
|
|
5238
|
+
}) : null]
|
|
5239
|
+
})
|
|
5240
|
+
]
|
|
4326
5241
|
});
|
|
4327
5242
|
}
|
|
4328
5243
|
//#endregion
|
|
@@ -4584,52 +5499,6 @@ window.__ModuleLoader__.load({
|
|
|
4584
5499
|
});
|
|
4585
5500
|
}
|
|
4586
5501
|
//#endregion
|
|
4587
|
-
//#region src/client/relative-time.ts
|
|
4588
|
-
/**
|
|
4589
|
-
* Compact relative time for trailing labels, as a structured bucket the
|
|
4590
|
-
* renderer localizes ("now"/"5min"/"3h"/"2d"/"4mo"/"1y" in en).
|
|
4591
|
-
*/
|
|
4592
|
-
function relativeTime(updatedAt, now) {
|
|
4593
|
-
const MIN = 6e4;
|
|
4594
|
-
const HOUR = 36e5;
|
|
4595
|
-
const DAY = 864e5;
|
|
4596
|
-
const diff = Math.max(0, now - updatedAt);
|
|
4597
|
-
if (diff < MIN) return {
|
|
4598
|
-
unit: "now",
|
|
4599
|
-
n: 0
|
|
4600
|
-
};
|
|
4601
|
-
if (diff < HOUR) return {
|
|
4602
|
-
unit: "minutes",
|
|
4603
|
-
n: Math.floor(diff / MIN)
|
|
4604
|
-
};
|
|
4605
|
-
if (diff < DAY) return {
|
|
4606
|
-
unit: "hours",
|
|
4607
|
-
n: Math.floor(diff / HOUR)
|
|
4608
|
-
};
|
|
4609
|
-
if (diff < 30 * DAY) return {
|
|
4610
|
-
unit: "days",
|
|
4611
|
-
n: Math.floor(diff / DAY)
|
|
4612
|
-
};
|
|
4613
|
-
if (diff < 365 * DAY) return {
|
|
4614
|
-
unit: "months",
|
|
4615
|
-
n: Math.floor(diff / (30 * DAY))
|
|
4616
|
-
};
|
|
4617
|
-
return {
|
|
4618
|
-
unit: "years",
|
|
4619
|
-
n: Math.floor(diff / (365 * DAY))
|
|
4620
|
-
};
|
|
4621
|
-
}
|
|
4622
|
-
/** Localized compact relative time ("刚刚"/"5分钟" in zh, "now"/"5min" in en). */
|
|
4623
|
-
function timeLabel(updatedAt, now, t) {
|
|
4624
|
-
const { unit, n } = relativeTime(updatedAt, now);
|
|
4625
|
-
if (unit === "now") return t("time.now");
|
|
4626
|
-
if (unit === "minutes") return t("time.minutes", { n });
|
|
4627
|
-
if (unit === "hours") return t("time.hours", { n });
|
|
4628
|
-
if (unit === "days") return t("time.days", { n });
|
|
4629
|
-
if (unit === "months") return t("time.months", { n });
|
|
4630
|
-
return t("time.years", { n });
|
|
4631
|
-
}
|
|
4632
|
-
//#endregion
|
|
4633
5502
|
//#region src/client/GitHistoryPanel.tsx
|
|
4634
5503
|
/**
|
|
4635
5504
|
* GitHistoryPanel: the git-management tab for the dsh-better-sidebar right
|
|
@@ -4649,6 +5518,9 @@ window.__ModuleLoader__.load({
|
|
|
4649
5518
|
*
|
|
4650
5519
|
* @module git-worktree/client/GitHistoryPanel
|
|
4651
5520
|
*/
|
|
5521
|
+
/** One history page. Kept next to the loader: `hasMore` is decided by
|
|
5522
|
+
* comparing the returned length against it. */
|
|
5523
|
+
const HISTORY_PAGE = 50;
|
|
4652
5524
|
/** One git failure → its message text (the git stderr rides error.message). */
|
|
4653
5525
|
function failureText(error) {
|
|
4654
5526
|
return error instanceof Error ? error.message : String(error);
|
|
@@ -4656,7 +5528,7 @@ window.__ModuleLoader__.load({
|
|
|
4656
5528
|
/** Data owner for the three sections. Remounts when `cwd` changes (the page
|
|
4657
5529
|
* keys the mount by cwd), so no state crosses a repository boundary. */
|
|
4658
5530
|
const GitHistoryPanelData = (0, react.forwardRef)(function GitHistoryPanelData(props, ref) {
|
|
4659
|
-
const { cwd, model, t, pushToast } = props;
|
|
5531
|
+
const { cwd, model, t, pushToast, accelOn } = props;
|
|
4660
5532
|
const [status, setStatus] = (0, react.useState)(null);
|
|
4661
5533
|
const [statusError, setStatusError] = (0, react.useState)();
|
|
4662
5534
|
const [changes, setChanges] = (0, react.useState)([]);
|
|
@@ -4674,11 +5546,15 @@ window.__ModuleLoader__.load({
|
|
|
4674
5546
|
const [commitMenuOpen, setCommitMenuOpen] = (0, react.useState)(false);
|
|
4675
5547
|
const [commitDraft, setCommitDraft] = (0, react.useState)("");
|
|
4676
5548
|
const [aiBusy, setAiBusy] = (0, react.useState)(false);
|
|
5549
|
+
/** The history card's filters. They live HERE, not in the section, because
|
|
5550
|
+
* they are request parameters: the section renders them and hands every
|
|
5551
|
+
* change up, and the loader below re-walks the log with them. */
|
|
5552
|
+
const [historyFilters, setHistoryFilters] = (0, react.useState)(EMPTY_HISTORY_FILTERS);
|
|
4677
5553
|
/** Response-sequence guard: a refresh raced against a mutation or a cwd
|
|
4678
5554
|
* change must never land an older snapshot over a newer one. */
|
|
4679
5555
|
const seqRef = (0, react.useRef)(0);
|
|
4680
5556
|
const loadHistoryPage = (0, react.useCallback)(async (skip) => {
|
|
4681
|
-
const result = await requestLog(cwd,
|
|
5557
|
+
const result = await requestLog(cwd, historyQueryFor(historyFilters, Date.now(), HISTORY_PAGE, skip));
|
|
4682
5558
|
if (!result.ok) {
|
|
4683
5559
|
setHistoryError(result.error);
|
|
4684
5560
|
return;
|
|
@@ -4686,9 +5562,9 @@ window.__ModuleLoader__.load({
|
|
|
4686
5562
|
setHistoryError(void 0);
|
|
4687
5563
|
return {
|
|
4688
5564
|
commits: result.commits,
|
|
4689
|
-
complete: result.commits.length <
|
|
5565
|
+
complete: result.commits.length < HISTORY_PAGE
|
|
4690
5566
|
};
|
|
4691
|
-
}, [cwd]);
|
|
5567
|
+
}, [cwd, historyFilters]);
|
|
4692
5568
|
const loadAll = (0, react.useCallback)(async () => {
|
|
4693
5569
|
const seq = seqRef.current + 1;
|
|
4694
5570
|
seqRef.current = seq;
|
|
@@ -4850,12 +5726,19 @@ window.__ModuleLoader__.load({
|
|
|
4850
5726
|
pushToast,
|
|
4851
5727
|
t
|
|
4852
5728
|
]);
|
|
5729
|
+
/** Fetch/update failure toast text: the git failure line, plus the
|
|
5730
|
+
* mirror-unavailable hint while cn-accelerate is on (push is never
|
|
5731
|
+
* accelerated, so its failures stay hint-free). */
|
|
5732
|
+
const syncFailureText = (0, react.useCallback)((message) => {
|
|
5733
|
+
const generic = t("errorGeneric", { message });
|
|
5734
|
+
return accelOn() ? `${generic} ${t("accel.mirrorDownHint")}` : generic;
|
|
5735
|
+
}, [accelOn, t]);
|
|
4853
5736
|
const onFetch = (0, react.useCallback)(() => {
|
|
4854
5737
|
setSyncBusy(true);
|
|
4855
5738
|
(async () => {
|
|
4856
5739
|
try {
|
|
4857
5740
|
const result = await requestFetch(cwd);
|
|
4858
|
-
if (!result.ok) pushToast(
|
|
5741
|
+
if (!result.ok) pushToast(syncFailureText(result.error), "error");
|
|
4859
5742
|
else {
|
|
4860
5743
|
pushToast(t("fetchDone"), "success");
|
|
4861
5744
|
emitGitMutated();
|
|
@@ -4869,6 +5752,7 @@ window.__ModuleLoader__.load({
|
|
|
4869
5752
|
cwd,
|
|
4870
5753
|
loadAll,
|
|
4871
5754
|
pushToast,
|
|
5755
|
+
syncFailureText,
|
|
4872
5756
|
t
|
|
4873
5757
|
]);
|
|
4874
5758
|
const onUpdate = (0, react.useCallback)(() => {
|
|
@@ -4876,7 +5760,7 @@ window.__ModuleLoader__.load({
|
|
|
4876
5760
|
(async () => {
|
|
4877
5761
|
try {
|
|
4878
5762
|
const result = await requestUpdate(cwd);
|
|
4879
|
-
if (!result.ok) pushToast(
|
|
5763
|
+
if (!result.ok) pushToast(syncFailureText(result.error), "error");
|
|
4880
5764
|
else {
|
|
4881
5765
|
pushToast(result.updated ? t("updateDone", { branch: result.branch }) : t("updateUpToDate"), "success");
|
|
4882
5766
|
emitGitMutated();
|
|
@@ -4890,6 +5774,7 @@ window.__ModuleLoader__.load({
|
|
|
4890
5774
|
cwd,
|
|
4891
5775
|
loadAll,
|
|
4892
5776
|
pushToast,
|
|
5777
|
+
syncFailureText,
|
|
4893
5778
|
t
|
|
4894
5779
|
]);
|
|
4895
5780
|
const onPush = (0, react.useCallback)(() => {
|
|
@@ -4969,6 +5854,23 @@ window.__ModuleLoader__.load({
|
|
|
4969
5854
|
runGuarded,
|
|
4970
5855
|
t
|
|
4971
5856
|
]);
|
|
5857
|
+
/** Tag one commit. Runs against the repository (a tag is a repository-wide
|
|
5858
|
+
* ref), so the history card can tag a commit that no checkout has. Only
|
|
5859
|
+
* local: pushing tags is a release step, not a side effect of a right-click. */
|
|
5860
|
+
const onCreateTag = (0, react.useCallback)((name, target, message) => {
|
|
5861
|
+
runGuarded(async () => {
|
|
5862
|
+
const result = await requestCreateTag(cwd, name, target, message);
|
|
5863
|
+
if (!result.ok) throw new Error(result.error);
|
|
5864
|
+
pushToast(t("panel.tagDone", { tag: result.tag }), "success");
|
|
5865
|
+
await loadAll();
|
|
5866
|
+
});
|
|
5867
|
+
}, [
|
|
5868
|
+
cwd,
|
|
5869
|
+
loadAll,
|
|
5870
|
+
pushToast,
|
|
5871
|
+
runGuarded,
|
|
5872
|
+
t
|
|
5873
|
+
]);
|
|
4972
5874
|
const onToggleCommit = (0, react.useCallback)((hash) => {
|
|
4973
5875
|
if (expanded === hash) {
|
|
4974
5876
|
setExpanded(null);
|
|
@@ -5019,7 +5921,10 @@ window.__ModuleLoader__.load({
|
|
|
5019
5921
|
loadHistoryPage,
|
|
5020
5922
|
loadingMore
|
|
5021
5923
|
]);
|
|
5022
|
-
const
|
|
5924
|
+
const onHistoryFilters = (0, react.useCallback)((next) => {
|
|
5925
|
+
setHistoryFilters(next);
|
|
5926
|
+
setExpanded(null);
|
|
5927
|
+
}, []);
|
|
5023
5928
|
if (statusError !== void 0 && repo === null) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5024
5929
|
className: GitHistoryPanel_module_default.errorText,
|
|
5025
5930
|
children: t("panel.loadFailed", { message: statusError })
|
|
@@ -5088,9 +5993,15 @@ window.__ModuleLoader__.load({
|
|
|
5088
5993
|
loadingMore,
|
|
5089
5994
|
expanded,
|
|
5090
5995
|
filesByHash,
|
|
5996
|
+
filters: historyFilters,
|
|
5997
|
+
branches: rows.locals.map((row) => row.name),
|
|
5998
|
+
busy: sectionBusy,
|
|
5091
5999
|
onLoadMore,
|
|
5092
6000
|
onToggle: onToggleCommit,
|
|
5093
|
-
|
|
6001
|
+
onFilters: onHistoryFilters,
|
|
6002
|
+
onCreateBranch: onCreate,
|
|
6003
|
+
onCreateTag,
|
|
6004
|
+
pushToast
|
|
5094
6005
|
})
|
|
5095
6006
|
]
|
|
5096
6007
|
});
|
|
@@ -5098,7 +6009,7 @@ window.__ModuleLoader__.load({
|
|
|
5098
6009
|
/** The registered better-sidebar tab's content: chrome + data owner, keyed
|
|
5099
6010
|
* by cwd so a session switch starts every section from scratch. */
|
|
5100
6011
|
function GitPanelTab(props) {
|
|
5101
|
-
const { ctx, scope, t } = props;
|
|
6012
|
+
const { ctx, scope, t, accel } = props;
|
|
5102
6013
|
const subscribe = (0, react.useCallback)((listener) => ctx.sessions.list.subscribe(listener), [ctx]);
|
|
5103
6014
|
const cwd = (0, react.useSyncExternalStore)(subscribe, () => scope.cwd ?? ctx.sessions.list.getSnapshot().byId[scope.sessionId]?.cwd);
|
|
5104
6015
|
const model = (0, react.useSyncExternalStore)(subscribe, () => {
|
|
@@ -5117,6 +6028,13 @@ window.__ModuleLoader__.load({
|
|
|
5117
6028
|
tone
|
|
5118
6029
|
}]);
|
|
5119
6030
|
}, []);
|
|
6031
|
+
const accelState = (0, react.useSyncExternalStore)(accel.store.subscribe, accel.store.getSnapshot);
|
|
6032
|
+
const [accelOpen, setAccelOpen] = (0, react.useState)(false);
|
|
6033
|
+
const accelOn = (0, react.useCallback)(() => accel.store.getSnapshot().enabled, [accel]);
|
|
6034
|
+
const closeAccel = (0, react.useCallback)(() => {
|
|
6035
|
+
accel.discardAccel();
|
|
6036
|
+
setAccelOpen(false);
|
|
6037
|
+
}, [accel]);
|
|
5120
6038
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5121
6039
|
className: GitHistoryPanel_module_default.page,
|
|
5122
6040
|
children: [
|
|
@@ -5128,9 +6046,35 @@ window.__ModuleLoader__.load({
|
|
|
5128
6046
|
className: GitHistoryPanel_module_default.repoName,
|
|
5129
6047
|
children: cwd
|
|
5130
6048
|
})
|
|
5131
|
-
}) : null, /* @__PURE__ */ (0, react_jsx_runtime.
|
|
6049
|
+
}) : null, /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5132
6050
|
className: GitHistoryPanel_module_default.topbarActions,
|
|
5133
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.
|
|
6051
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6052
|
+
className: GitHistoryPanel_module_default.cnWrap,
|
|
6053
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
6054
|
+
type: "button",
|
|
6055
|
+
className: accelState.enabled ? GitHistoryPanel_module_default.cnBtnOn : GitHistoryPanel_module_default.cnBtn,
|
|
6056
|
+
title: t("panel.cnAccelerate"),
|
|
6057
|
+
"aria-label": t("panel.cnAccelerate"),
|
|
6058
|
+
"aria-expanded": accelOpen,
|
|
6059
|
+
onClick: () => accelOpen ? closeAccel() : setAccelOpen(true),
|
|
6060
|
+
children: [
|
|
6061
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6062
|
+
"aria-hidden": "true",
|
|
6063
|
+
children: "⚡"
|
|
6064
|
+
}),
|
|
6065
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("panel.cnAccelerate") }),
|
|
6066
|
+
accelState.enabled ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6067
|
+
className: GitHistoryPanel_module_default.cnDot,
|
|
6068
|
+
"aria-hidden": "true"
|
|
6069
|
+
}) : null
|
|
6070
|
+
]
|
|
6071
|
+
}), accelOpen ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CnAcceleratePopover, {
|
|
6072
|
+
accel,
|
|
6073
|
+
t,
|
|
6074
|
+
pushToast,
|
|
6075
|
+
onClose: closeAccel
|
|
6076
|
+
}) : null]
|
|
6077
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
5134
6078
|
type: "button",
|
|
5135
6079
|
className: GitHistoryPanel_module_default.refreshBtn,
|
|
5136
6080
|
disabled: cwd === void 0,
|
|
@@ -5141,7 +6085,7 @@ window.__ModuleLoader__.load({
|
|
|
5141
6085
|
size: 14,
|
|
5142
6086
|
"aria-hidden": "true"
|
|
5143
6087
|
})
|
|
5144
|
-
})
|
|
6088
|
+
})]
|
|
5145
6089
|
})]
|
|
5146
6090
|
}),
|
|
5147
6091
|
cwd === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
@@ -5152,7 +6096,8 @@ window.__ModuleLoader__.load({
|
|
|
5152
6096
|
cwd,
|
|
5153
6097
|
model,
|
|
5154
6098
|
t,
|
|
5155
|
-
pushToast
|
|
6099
|
+
pushToast,
|
|
6100
|
+
accelOn
|
|
5156
6101
|
}, cwd),
|
|
5157
6102
|
toasts.map((toast) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Toast, {
|
|
5158
6103
|
text: toast.text,
|
|
@@ -5203,6 +6148,52 @@ window.__ModuleLoader__.load({
|
|
|
5203
6148
|
}
|
|
5204
6149
|
}
|
|
5205
6150
|
//#endregion
|
|
6151
|
+
//#region src/client/relative-time.ts
|
|
6152
|
+
/**
|
|
6153
|
+
* Compact relative time for trailing labels, as a structured bucket the
|
|
6154
|
+
* renderer localizes ("now"/"5min"/"3h"/"2d"/"4mo"/"1y" in en).
|
|
6155
|
+
*/
|
|
6156
|
+
function relativeTime(updatedAt, now) {
|
|
6157
|
+
const MIN = 6e4;
|
|
6158
|
+
const HOUR = 36e5;
|
|
6159
|
+
const DAY = 864e5;
|
|
6160
|
+
const diff = Math.max(0, now - updatedAt);
|
|
6161
|
+
if (diff < MIN) return {
|
|
6162
|
+
unit: "now",
|
|
6163
|
+
n: 0
|
|
6164
|
+
};
|
|
6165
|
+
if (diff < HOUR) return {
|
|
6166
|
+
unit: "minutes",
|
|
6167
|
+
n: Math.floor(diff / MIN)
|
|
6168
|
+
};
|
|
6169
|
+
if (diff < DAY) return {
|
|
6170
|
+
unit: "hours",
|
|
6171
|
+
n: Math.floor(diff / HOUR)
|
|
6172
|
+
};
|
|
6173
|
+
if (diff < 30 * DAY) return {
|
|
6174
|
+
unit: "days",
|
|
6175
|
+
n: Math.floor(diff / DAY)
|
|
6176
|
+
};
|
|
6177
|
+
if (diff < 365 * DAY) return {
|
|
6178
|
+
unit: "months",
|
|
6179
|
+
n: Math.floor(diff / (30 * DAY))
|
|
6180
|
+
};
|
|
6181
|
+
return {
|
|
6182
|
+
unit: "years",
|
|
6183
|
+
n: Math.floor(diff / (365 * DAY))
|
|
6184
|
+
};
|
|
6185
|
+
}
|
|
6186
|
+
/** Localized compact relative time ("刚刚"/"5分钟" in zh, "now"/"5min" in en). */
|
|
6187
|
+
function timeLabel(updatedAt, now, t) {
|
|
6188
|
+
const { unit, n } = relativeTime(updatedAt, now);
|
|
6189
|
+
if (unit === "now") return t("time.now");
|
|
6190
|
+
if (unit === "minutes") return t("time.minutes", { n });
|
|
6191
|
+
if (unit === "hours") return t("time.hours", { n });
|
|
6192
|
+
if (unit === "days") return t("time.days", { n });
|
|
6193
|
+
if (unit === "months") return t("time.months", { n });
|
|
6194
|
+
return t("time.years", { n });
|
|
6195
|
+
}
|
|
6196
|
+
//#endregion
|
|
5206
6197
|
//#region src/client/concurrency.ts
|
|
5207
6198
|
/**
|
|
5208
6199
|
* Bounded-concurrency map for the browser half's fan-out calls.
|
|
@@ -5760,7 +6751,7 @@ window.__ModuleLoader__.load({
|
|
|
5760
6751
|
}
|
|
5761
6752
|
//#endregion
|
|
5762
6753
|
//#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";
|
|
6754
|
+
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
6755
|
const tagId = "dsh-git-branch-manage/GitWorktreeCard.module.css";
|
|
5765
6756
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=\"" + tagId + "\"]") === null) {
|
|
5766
6757
|
const tag = document.createElement("style");
|
|
@@ -5799,6 +6790,7 @@ window.__ModuleLoader__.load({
|
|
|
5799
6790
|
"keepBad": "keepBad_1281848784",
|
|
5800
6791
|
"copyFilesRow": "copyFilesRow_1281848784",
|
|
5801
6792
|
"copyFilesInput": "copyFilesInput_1281848784",
|
|
6793
|
+
"accelInput": "accelInput_1281848784",
|
|
5802
6794
|
"switchBad": "switchBad_1281848784",
|
|
5803
6795
|
"historyRow": "historyRow_1281848784",
|
|
5804
6796
|
"history": "history_1281848784",
|
|
@@ -5875,13 +6867,14 @@ window.__ModuleLoader__.load({
|
|
|
5875
6867
|
const shownHistory = historyAll ? history : history.slice(0, HISTORY_PREVIEW);
|
|
5876
6868
|
const { t } = props;
|
|
5877
6869
|
const state = props.useGitWorktreeCard((snapshot) => snapshot);
|
|
6870
|
+
const accelState = props.useGitWorktreeAccel((snapshot) => snapshot);
|
|
5878
6871
|
if (!state.available) return null;
|
|
5879
6872
|
if (!onPage) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
5880
6873
|
className: GitWorktreeCard_module_default.summary,
|
|
5881
6874
|
children: t("cardDescription")
|
|
5882
6875
|
});
|
|
5883
6876
|
const lockInput = !state.writable;
|
|
5884
|
-
const lockActions = !state.dirty || state.saving || !state.keepWorktreesValid;
|
|
6877
|
+
const lockActions = !state.dirty && !accelState.dirty || state.saving || accelState.saving || !state.keepWorktreesValid || accelState.urlError !== null;
|
|
5885
6878
|
const legacyRootText = storage?.legacyRoot ?? (state.rootDir === "" ? "—" : state.rootDir);
|
|
5886
6879
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5887
6880
|
className: GitWorktreeCard_module_default.page,
|
|
@@ -6055,6 +7048,56 @@ window.__ModuleLoader__.load({
|
|
|
6055
7048
|
})
|
|
6056
7049
|
]
|
|
6057
7050
|
}),
|
|
7051
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
7052
|
+
className: `${GitWorktreeCard_module_default.field} ${GitWorktreeCard_module_default.toggleRow}`,
|
|
7053
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
7054
|
+
className: GitWorktreeCard_module_default.toggleText,
|
|
7055
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
7056
|
+
className: GitWorktreeCard_module_default.toggleLabel,
|
|
7057
|
+
children: t("panel.cnAccelerate")
|
|
7058
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
7059
|
+
className: GitWorktreeCard_module_default.toggleHint,
|
|
7060
|
+
children: t("accel.hint")
|
|
7061
|
+
})]
|
|
7062
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
7063
|
+
className: GitWorktreeCard_module_default.toggleControl,
|
|
7064
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
7065
|
+
className: GitWorktreeCard_module_default.toggle,
|
|
7066
|
+
type: "checkbox",
|
|
7067
|
+
disabled: lockInput || accelState.saving,
|
|
7068
|
+
checked: accelState.enabled,
|
|
7069
|
+
onChange: (event) => {
|
|
7070
|
+
props.setAccelEnabled(event.target.checked);
|
|
7071
|
+
}
|
|
7072
|
+
})
|
|
7073
|
+
})]
|
|
7074
|
+
}),
|
|
7075
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
7076
|
+
className: `${GitWorktreeCard_module_default.field} ${GitWorktreeCard_module_default.copyFilesRow}`,
|
|
7077
|
+
htmlFor: "git-worktree-card-accel-url",
|
|
7078
|
+
children: [
|
|
7079
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
7080
|
+
className: GitWorktreeCard_module_default.fieldLabel,
|
|
7081
|
+
children: t("accel.urlLabel")
|
|
7082
|
+
}),
|
|
7083
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
7084
|
+
id: "git-worktree-card-accel-url",
|
|
7085
|
+
className: GitWorktreeCard_module_default.accelInput,
|
|
7086
|
+
type: "text",
|
|
7087
|
+
spellCheck: false,
|
|
7088
|
+
disabled: lockInput || accelState.saving,
|
|
7089
|
+
value: accelState.urlText,
|
|
7090
|
+
onChange: (event) => {
|
|
7091
|
+
props.editAccelUrl(event.target.value);
|
|
7092
|
+
}
|
|
7093
|
+
}),
|
|
7094
|
+
accelState.urlError !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
7095
|
+
className: GitWorktreeCard_module_default.keepBad,
|
|
7096
|
+
role: "alert",
|
|
7097
|
+
children: accelState.urlError === "empty" ? t("accel.urlEmpty") : t("accel.urlInvalid")
|
|
7098
|
+
}) : null
|
|
7099
|
+
]
|
|
7100
|
+
}),
|
|
6058
7101
|
history.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6059
7102
|
className: `${GitWorktreeCard_module_default.field} ${GitWorktreeCard_module_default.historyRow}`,
|
|
6060
7103
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
@@ -6121,19 +7164,30 @@ window.__ModuleLoader__.load({
|
|
|
6121
7164
|
role: "status",
|
|
6122
7165
|
children: t("cardSaveFailed")
|
|
6123
7166
|
}) : null,
|
|
7167
|
+
accelState.failed ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
7168
|
+
className: GitWorktreeCard_module_default.failed,
|
|
7169
|
+
role: "status",
|
|
7170
|
+
children: t("accel.saveFailed")
|
|
7171
|
+
}) : null,
|
|
6124
7172
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
6125
7173
|
type: "button",
|
|
6126
7174
|
className: GitWorktreeCard_module_default.discard,
|
|
6127
7175
|
disabled: lockActions,
|
|
6128
|
-
onClick:
|
|
7176
|
+
onClick: () => {
|
|
7177
|
+
props.discard();
|
|
7178
|
+
props.discardAccel();
|
|
7179
|
+
},
|
|
6129
7180
|
children: t("cardDiscard")
|
|
6130
7181
|
}),
|
|
6131
7182
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
6132
7183
|
type: "button",
|
|
6133
7184
|
className: GitWorktreeCard_module_default.save,
|
|
6134
7185
|
disabled: lockActions,
|
|
6135
|
-
onClick:
|
|
6136
|
-
|
|
7186
|
+
onClick: () => {
|
|
7187
|
+
props.save();
|
|
7188
|
+
props.saveAccel();
|
|
7189
|
+
},
|
|
7190
|
+
children: t(state.saving || accelState.saving ? "cardSaving" : "cardSave")
|
|
6137
7191
|
})
|
|
6138
7192
|
]
|
|
6139
7193
|
})
|
|
@@ -6318,6 +7372,23 @@ window.__ModuleLoader__.load({
|
|
|
6318
7372
|
"panel.createDone": "Created branch {branch}",
|
|
6319
7373
|
"panel.history": "Commit history",
|
|
6320
7374
|
"panel.historySearch": "Search commits / authors",
|
|
7375
|
+
"panel.historyBranch": "Branch",
|
|
7376
|
+
"panel.historyAllRefs": "All branches",
|
|
7377
|
+
"panel.historyAuthor": "Author",
|
|
7378
|
+
"panel.historyTime": "Time",
|
|
7379
|
+
"panel.rangeAll": "All time",
|
|
7380
|
+
"panel.rangeDay": "Last 24 hours",
|
|
7381
|
+
"panel.rangeWeek": "Last 7 days",
|
|
7382
|
+
"panel.rangeCustom": "Custom range",
|
|
7383
|
+
"panel.rangeSince": "From",
|
|
7384
|
+
"panel.rangeUntil": "To",
|
|
7385
|
+
"panel.commitCopyHash": "Copy hash",
|
|
7386
|
+
"panel.commitCopySubject": "Copy message",
|
|
7387
|
+
"panel.branchTag": "Tag this commit…",
|
|
7388
|
+
"panel.tagTitle": "New tag at {branch}",
|
|
7389
|
+
"panel.tagName": "Tag name",
|
|
7390
|
+
"panel.tagMessage": "Message (optional)",
|
|
7391
|
+
"panel.tagDone": "Tagged {tag}",
|
|
6321
7392
|
"panel.noCommits": "This repository has no commits yet",
|
|
6322
7393
|
"panel.noMatches": "No matching commits",
|
|
6323
7394
|
"panel.loadMore": "Load more",
|
|
@@ -6330,7 +7401,18 @@ window.__ModuleLoader__.load({
|
|
|
6330
7401
|
"panel.stCopy": "copied",
|
|
6331
7402
|
"panel.stType": "type changed",
|
|
6332
7403
|
"panel.stUntrack": "untracked",
|
|
6333
|
-
"panel.stUnmerged": "conflict"
|
|
7404
|
+
"panel.stUnmerged": "conflict",
|
|
7405
|
+
"panel.cnAccelerate": "China acceleration",
|
|
7406
|
+
"accel.enable": "Enable acceleration",
|
|
7407
|
+
"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.",
|
|
7408
|
+
"accel.urlLabel": "Acceleration URL",
|
|
7409
|
+
"accel.restoreDefault": "Restore default",
|
|
7410
|
+
"accel.urlInvalid": "Enter an http(s):// URL",
|
|
7411
|
+
"accel.urlEmpty": "Enter an acceleration URL",
|
|
7412
|
+
"accel.saveFailed": "The change did not save. Try again.",
|
|
7413
|
+
"accel.savedOn": "China acceleration enabled: {url}",
|
|
7414
|
+
"accel.savedOff": "China acceleration disabled",
|
|
7415
|
+
"accel.mirrorDownHint": "If the mirror is down, disable acceleration or change the URL"
|
|
6334
7416
|
};
|
|
6335
7417
|
/** 中文词典。 */
|
|
6336
7418
|
const zh = {
|
|
@@ -6508,6 +7590,23 @@ window.__ModuleLoader__.load({
|
|
|
6508
7590
|
"panel.createDone": "已创建分支 {branch}",
|
|
6509
7591
|
"panel.history": "提交历史",
|
|
6510
7592
|
"panel.historySearch": "搜索提交 / 作者",
|
|
7593
|
+
"panel.historyBranch": "分支",
|
|
7594
|
+
"panel.historyAllRefs": "所有分支",
|
|
7595
|
+
"panel.historyAuthor": "提交人",
|
|
7596
|
+
"panel.historyTime": "时间",
|
|
7597
|
+
"panel.rangeAll": "全部",
|
|
7598
|
+
"panel.rangeDay": "近 24 小时",
|
|
7599
|
+
"panel.rangeWeek": "近 7 天",
|
|
7600
|
+
"panel.rangeCustom": "自定义范围",
|
|
7601
|
+
"panel.rangeSince": "从",
|
|
7602
|
+
"panel.rangeUntil": "到",
|
|
7603
|
+
"panel.commitCopyHash": "复制哈希",
|
|
7604
|
+
"panel.commitCopySubject": "复制消息",
|
|
7605
|
+
"panel.branchTag": "添加标签…",
|
|
7606
|
+
"panel.tagTitle": "在 {branch} 上新标签",
|
|
7607
|
+
"panel.tagName": "标签名称",
|
|
7608
|
+
"panel.tagMessage": "消息(可选)",
|
|
7609
|
+
"panel.tagDone": "已标记 {tag}",
|
|
6511
7610
|
"panel.noCommits": "仓库还没有提交",
|
|
6512
7611
|
"panel.noMatches": "没有匹配的提交",
|
|
6513
7612
|
"panel.loadMore": "加载更多",
|
|
@@ -6520,7 +7619,18 @@ window.__ModuleLoader__.load({
|
|
|
6520
7619
|
"panel.stCopy": "复制",
|
|
6521
7620
|
"panel.stType": "类型变更",
|
|
6522
7621
|
"panel.stUntrack": "未跟踪",
|
|
6523
|
-
"panel.stUnmerged": "冲突"
|
|
7622
|
+
"panel.stUnmerged": "冲突",
|
|
7623
|
+
"panel.cnAccelerate": "国内加速",
|
|
7624
|
+
"accel.enable": "启用加速",
|
|
7625
|
+
"accel.hint": "仅加速 https 的 github.com 仓库地址,作用于提取/更新;推送不走加速。第三方加速站可看到仓库内容,请勿用于私有/敏感仓库。",
|
|
7626
|
+
"accel.urlLabel": "加速地址",
|
|
7627
|
+
"accel.restoreDefault": "恢复默认",
|
|
7628
|
+
"accel.urlInvalid": "请输入以 http(s):// 开头的地址",
|
|
7629
|
+
"accel.urlEmpty": "请输入加速地址",
|
|
7630
|
+
"accel.saveFailed": "保存未生效,请重试。",
|
|
7631
|
+
"accel.savedOn": "已启用国内加速:{url}",
|
|
7632
|
+
"accel.savedOff": "已关闭国内加速",
|
|
7633
|
+
"accel.mirrorDownHint": "若加速地址不可用,可停用国内加速或更换地址"
|
|
6524
7634
|
};
|
|
6525
7635
|
//#endregion
|
|
6526
7636
|
//#region src/client/index.ts
|
|
@@ -6771,6 +7881,11 @@ window.__ModuleLoader__.load({
|
|
|
6771
7881
|
});
|
|
6772
7882
|
const form = new CardForm(settingsForm);
|
|
6773
7883
|
const store = form.bind();
|
|
7884
|
+
const accelForm = new AccelForm(settingsForm);
|
|
7885
|
+
const accelFace = {
|
|
7886
|
+
store: accelForm.bind(),
|
|
7887
|
+
...accelForm.actions()
|
|
7888
|
+
};
|
|
6774
7889
|
const describeFace = ctx.configForms.describe();
|
|
6775
7890
|
let configDisposer;
|
|
6776
7891
|
const syncCardSeat = () => {
|
|
@@ -6781,8 +7896,12 @@ window.__ModuleLoader__.load({
|
|
|
6781
7896
|
key: PLUGIN_PACKAGE,
|
|
6782
7897
|
locale: NS,
|
|
6783
7898
|
inject: () => ({
|
|
6784
|
-
hooks: {
|
|
7899
|
+
hooks: {
|
|
7900
|
+
gitWorktreeCard: store,
|
|
7901
|
+
gitWorktreeAccel: accelFace.store
|
|
7902
|
+
},
|
|
6785
7903
|
...form.actions(),
|
|
7904
|
+
...accelForm.actions(),
|
|
6786
7905
|
manager: managerFace()
|
|
6787
7906
|
})
|
|
6788
7907
|
}, GitWorktreeCard));
|
|
@@ -6814,7 +7933,8 @@ window.__ModuleLoader__.load({
|
|
|
6814
7933
|
component: (tabProps) => (0, react.createElement)(GitPanelTab, {
|
|
6815
7934
|
ctx: betterCtx,
|
|
6816
7935
|
scope: tabProps.scope,
|
|
6817
|
-
t: panelT
|
|
7936
|
+
t: panelT,
|
|
7937
|
+
accel: accelFace
|
|
6818
7938
|
})
|
|
6819
7939
|
}), "git-worktree: better-sidebar tab");
|
|
6820
7940
|
});
|