@young1lin/dsh-ui-gitworkbench 0.1.2 → 0.1.4
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/AGENTS.md +1 -1
- package/CHANGELOG.md +45 -0
- package/CHANGELOG_EN.md +45 -0
- package/README.md +39 -6
- package/README_EN.md +3 -2
- package/lib/client.js +2058 -305
- package/lib/discard-ops.js +158 -0
- package/lib/git-log.js +10 -6
- package/lib/git-ops.js +30 -9
- package/lib/index.js +215 -7
- package/lib/log-filter.js +71 -0
- package/lib/shortlog.js +40 -0
- package/package.json +6 -6
- package/src/client/GitWorkbenchPanel.module.css +523 -6
- package/src/client/GitWorkbenchPanel.tsx +1024 -26
- package/src/client/active-file.ts +83 -0
- package/src/client/calendar.ts +99 -0
- package/src/client/commit-filter.ts +49 -0
- package/src/client/dir-tree.ts +112 -0
- package/src/client/file-filter.ts +66 -0
- package/src/client/index.ts +44 -5
- package/src/client/locales.ts +94 -0
- package/src/client/log-filter-query.ts +189 -0
- package/src/client/path-select.ts +131 -0
- package/src/discard-ops.ts +197 -0
- package/src/git-log.ts +24 -6
- package/src/git-ops.ts +39 -7
- package/src/index.ts +215 -6
- package/src/log-filter.ts +87 -0
- package/src/shortlog.ts +46 -0
package/lib/client.js
CHANGED
|
@@ -487,6 +487,597 @@ window.__ModuleLoader__.load({
|
|
|
487
487
|
};
|
|
488
488
|
}
|
|
489
489
|
//#endregion
|
|
490
|
+
//#region src/client/commit-filter.ts
|
|
491
|
+
/**
|
|
492
|
+
* Render a commit's ISO 8601 date in full — "Aug 4, 2026, 5:30 PM", in the
|
|
493
|
+
* viewer's locale and timezone (or the overrides, which exist for tests).
|
|
494
|
+
*
|
|
495
|
+
* git's relative prose ("3 weeks ago") is right for the row and useless for
|
|
496
|
+
* the hover card, where the question is exactly WHEN. `%cI` is a strict ISO
|
|
497
|
+
* timestamp, so `new Date` parses it and the formatter renders local time —
|
|
498
|
+
* the same moment the viewer's own clock shows, which is the only timezone a
|
|
499
|
+
* hover card should speak. Unparsable input yields an empty string rather
|
|
500
|
+
* than a thrown RangeError: the card simply omits the line.
|
|
501
|
+
* @param iso - `%cI` string from the host log, possibly empty or absent.
|
|
502
|
+
* @param options - locale/timezone overrides; both optional.
|
|
503
|
+
*/
|
|
504
|
+
function formatCommitDate(iso, options = {}) {
|
|
505
|
+
if (iso.length === 0) return "";
|
|
506
|
+
const date = new Date(iso);
|
|
507
|
+
if (Number.isNaN(date.getTime())) return "";
|
|
508
|
+
return new Intl.DateTimeFormat(options.locale, {
|
|
509
|
+
year: "numeric",
|
|
510
|
+
month: "short",
|
|
511
|
+
day: "numeric",
|
|
512
|
+
hour: "2-digit",
|
|
513
|
+
minute: "2-digit",
|
|
514
|
+
...options.timeZone !== void 0 ? { timeZone: options.timeZone } : {}
|
|
515
|
+
}).format(date);
|
|
516
|
+
}
|
|
517
|
+
//#endregion
|
|
518
|
+
//#region src/client/log-filter-query.ts
|
|
519
|
+
/** The filter that filters nothing. */
|
|
520
|
+
function emptyQueryFilter() {
|
|
521
|
+
return {
|
|
522
|
+
users: [],
|
|
523
|
+
text: "",
|
|
524
|
+
textRegex: false,
|
|
525
|
+
paths: [],
|
|
526
|
+
after: "",
|
|
527
|
+
before: ""
|
|
528
|
+
};
|
|
529
|
+
}
|
|
530
|
+
const PREFIX_RE = /^(user|path|after|before):(.*)$/i;
|
|
531
|
+
function tokenize(query) {
|
|
532
|
+
const tokens = [];
|
|
533
|
+
let i = 0;
|
|
534
|
+
while (i < query.length) {
|
|
535
|
+
while (i < query.length && /\s/.test(query[i])) i += 1;
|
|
536
|
+
if (i >= query.length) break;
|
|
537
|
+
if (query[i] === "\"") {
|
|
538
|
+
const end = query.indexOf("\"", i + 1);
|
|
539
|
+
const value = end === -1 ? query.slice(i + 1) : query.slice(i + 1, end);
|
|
540
|
+
tokens.push({
|
|
541
|
+
value,
|
|
542
|
+
quoted: true
|
|
543
|
+
});
|
|
544
|
+
i = end === -1 ? query.length : end + 1;
|
|
545
|
+
continue;
|
|
546
|
+
}
|
|
547
|
+
const prefixQuote = /^(user|path|after|before):"/i.exec(query.slice(i));
|
|
548
|
+
if (prefixQuote !== null) {
|
|
549
|
+
const kind = prefixQuote[1].toLowerCase();
|
|
550
|
+
const open = i + prefixQuote[0].length;
|
|
551
|
+
const end = query.indexOf("\"", open);
|
|
552
|
+
const value = end === -1 ? query.slice(open) : query.slice(open, end);
|
|
553
|
+
tokens.push({
|
|
554
|
+
value,
|
|
555
|
+
quoted: true,
|
|
556
|
+
kind
|
|
557
|
+
});
|
|
558
|
+
i = end === -1 ? query.length : end + 1;
|
|
559
|
+
continue;
|
|
560
|
+
}
|
|
561
|
+
const start = i;
|
|
562
|
+
while (i < query.length && !/\s/.test(query[i])) i += 1;
|
|
563
|
+
const word = query.slice(start, i);
|
|
564
|
+
const match = PREFIX_RE.exec(word);
|
|
565
|
+
tokens.push(match === null ? {
|
|
566
|
+
value: word,
|
|
567
|
+
quoted: false
|
|
568
|
+
} : {
|
|
569
|
+
value: match[2],
|
|
570
|
+
quoted: false,
|
|
571
|
+
kind: match[1].toLowerCase()
|
|
572
|
+
});
|
|
573
|
+
}
|
|
574
|
+
return tokens;
|
|
575
|
+
}
|
|
576
|
+
/**
|
|
577
|
+
* Parse the box's text into a filter.
|
|
578
|
+
* @param query - raw box contents.
|
|
579
|
+
*/
|
|
580
|
+
function parseLogQuery(query) {
|
|
581
|
+
const tokens = tokenize(query);
|
|
582
|
+
const users = [];
|
|
583
|
+
const paths = [];
|
|
584
|
+
let text = "";
|
|
585
|
+
let after = "";
|
|
586
|
+
let before = "";
|
|
587
|
+
const textWords = [];
|
|
588
|
+
let i = 0;
|
|
589
|
+
while (i < tokens.length) {
|
|
590
|
+
const token = tokens[i];
|
|
591
|
+
if (token.kind === void 0) {
|
|
592
|
+
textWords.push(token.value);
|
|
593
|
+
i += 1;
|
|
594
|
+
continue;
|
|
595
|
+
}
|
|
596
|
+
if (token.kind === "user" || token.kind === "path") {
|
|
597
|
+
const list = token.kind === "user" ? users : paths;
|
|
598
|
+
if (token.value.length > 0 && !list.includes(token.value)) list.push(token.value);
|
|
599
|
+
i += 1;
|
|
600
|
+
continue;
|
|
601
|
+
}
|
|
602
|
+
const parts = [token.value];
|
|
603
|
+
let j = token.quoted ? i : i + 1;
|
|
604
|
+
while (j < tokens.length && tokens[j].kind === void 0 && !tokens[j].quoted && tokens[j].value.length > 0) {
|
|
605
|
+
parts.push(tokens[j].value);
|
|
606
|
+
j += 1;
|
|
607
|
+
}
|
|
608
|
+
const value = parts.join(" ").trim();
|
|
609
|
+
if (token.kind === "after") after = value;
|
|
610
|
+
else before = value;
|
|
611
|
+
i = token.quoted ? i + 1 : j;
|
|
612
|
+
}
|
|
613
|
+
text = textWords.join(" ").trim();
|
|
614
|
+
return {
|
|
615
|
+
users,
|
|
616
|
+
text,
|
|
617
|
+
textRegex: false,
|
|
618
|
+
paths,
|
|
619
|
+
after,
|
|
620
|
+
before
|
|
621
|
+
};
|
|
622
|
+
}
|
|
623
|
+
/** Quote a serialized value iff it would not reparse as itself. */
|
|
624
|
+
function quote(value) {
|
|
625
|
+
return /\s/.test(value) ? `"${value}"` : value;
|
|
626
|
+
}
|
|
627
|
+
/**
|
|
628
|
+
* Render a filter back into the box's grammar. The text criterion goes last
|
|
629
|
+
* and is quoted when any of its words would parse as a prefix token.
|
|
630
|
+
* @param filter - the filter to render.
|
|
631
|
+
*/
|
|
632
|
+
function serializeLogQuery(filter) {
|
|
633
|
+
const parts = [];
|
|
634
|
+
for (const user of filter.users) parts.push(`user:${quote(user)}`);
|
|
635
|
+
for (const path of filter.paths) parts.push(`path:${quote(path)}`);
|
|
636
|
+
if (filter.after.length > 0) parts.push(`after:${quote(filter.after)}`);
|
|
637
|
+
if (filter.before.length > 0) parts.push(`before:${quote(filter.before)}`);
|
|
638
|
+
if (filter.text.length > 0) {
|
|
639
|
+
const looksPrefixed = filter.text.split(/\s+/).some((word) => PREFIX_RE.test(word));
|
|
640
|
+
parts.push(looksPrefixed ? `"${filter.text}"` : filter.text);
|
|
641
|
+
}
|
|
642
|
+
return parts.join(" ");
|
|
643
|
+
}
|
|
644
|
+
/**
|
|
645
|
+
* One chip per criterion, in grammar order: users, paths, bounds, text.
|
|
646
|
+
* @param filter - the filter to decompose.
|
|
647
|
+
*/
|
|
648
|
+
function chipsFromFilter(filter) {
|
|
649
|
+
const chips = [];
|
|
650
|
+
for (const user of filter.users) chips.push({
|
|
651
|
+
kind: "user",
|
|
652
|
+
value: user
|
|
653
|
+
});
|
|
654
|
+
for (const path of filter.paths) chips.push({
|
|
655
|
+
kind: "path",
|
|
656
|
+
value: path
|
|
657
|
+
});
|
|
658
|
+
if (filter.after.length > 0) chips.push({
|
|
659
|
+
kind: "after",
|
|
660
|
+
value: filter.after
|
|
661
|
+
});
|
|
662
|
+
if (filter.before.length > 0) chips.push({
|
|
663
|
+
kind: "before",
|
|
664
|
+
value: filter.before
|
|
665
|
+
});
|
|
666
|
+
if (filter.text.length > 0) chips.push({
|
|
667
|
+
kind: "text",
|
|
668
|
+
value: filter.text
|
|
669
|
+
});
|
|
670
|
+
return chips;
|
|
671
|
+
}
|
|
672
|
+
/**
|
|
673
|
+
* The filter minus one chip. Immutable; dropping the last criterion yields
|
|
674
|
+
* the empty filter.
|
|
675
|
+
* @param filter - current filter.
|
|
676
|
+
* @param kind - the chip's criterion kind.
|
|
677
|
+
* @param value - the chip's value (which user, which path).
|
|
678
|
+
*/
|
|
679
|
+
function removeChip(filter, kind, value) {
|
|
680
|
+
switch (kind) {
|
|
681
|
+
case "user": return {
|
|
682
|
+
...filter,
|
|
683
|
+
users: filter.users.filter((user) => user !== value)
|
|
684
|
+
};
|
|
685
|
+
case "path": return {
|
|
686
|
+
...filter,
|
|
687
|
+
paths: filter.paths.filter((path) => path !== value)
|
|
688
|
+
};
|
|
689
|
+
case "after": return {
|
|
690
|
+
...filter,
|
|
691
|
+
after: ""
|
|
692
|
+
};
|
|
693
|
+
case "before": return {
|
|
694
|
+
...filter,
|
|
695
|
+
before: ""
|
|
696
|
+
};
|
|
697
|
+
case "text": return {
|
|
698
|
+
...filter,
|
|
699
|
+
text: "",
|
|
700
|
+
textRegex: false
|
|
701
|
+
};
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
//#endregion
|
|
705
|
+
//#region src/client/dir-tree.ts
|
|
706
|
+
/**
|
|
707
|
+
* Fold a flat path list into a sorted directory tree carrying its files.
|
|
708
|
+
* Root-level files live on no directory; the SEARCH ({@link searchPaths}) is
|
|
709
|
+
* where they surface.
|
|
710
|
+
* @param paths - repo-relative file paths, any order, no duplicates assumed.
|
|
711
|
+
* @returns the top-level directories, children and files sorted by name.
|
|
712
|
+
*/
|
|
713
|
+
function buildDirTree(paths) {
|
|
714
|
+
const rootNode = {
|
|
715
|
+
name: "",
|
|
716
|
+
path: "",
|
|
717
|
+
files: [],
|
|
718
|
+
children: /* @__PURE__ */ new Map()
|
|
719
|
+
};
|
|
720
|
+
for (const path of paths) {
|
|
721
|
+
if (path.length === 0) continue;
|
|
722
|
+
const parts = path.split("/");
|
|
723
|
+
let node = rootNode;
|
|
724
|
+
for (let i = 0; i < parts.length - 1; i += 1) {
|
|
725
|
+
const name = parts[i];
|
|
726
|
+
let child = node.children.get(name);
|
|
727
|
+
if (child === void 0) {
|
|
728
|
+
child = {
|
|
729
|
+
name,
|
|
730
|
+
path: parts.slice(0, i + 1).join("/"),
|
|
731
|
+
files: [],
|
|
732
|
+
children: /* @__PURE__ */ new Map()
|
|
733
|
+
};
|
|
734
|
+
node.children.set(name, child);
|
|
735
|
+
}
|
|
736
|
+
node = child;
|
|
737
|
+
}
|
|
738
|
+
node.files.push(parts[parts.length - 1]);
|
|
739
|
+
}
|
|
740
|
+
const freeze = (node) => {
|
|
741
|
+
const children = [...node.children.values()].sort((a, b) => a.name.localeCompare(b.name)).map(freeze);
|
|
742
|
+
const files = [...node.files].sort((a, b) => a.localeCompare(b));
|
|
743
|
+
const subtreeCount = files.length + children.reduce((sum, child) => sum + child.fileCount, 0);
|
|
744
|
+
return {
|
|
745
|
+
name: node.name,
|
|
746
|
+
path: node.path,
|
|
747
|
+
fileCount: subtreeCount,
|
|
748
|
+
files,
|
|
749
|
+
children
|
|
750
|
+
};
|
|
751
|
+
};
|
|
752
|
+
return freeze(rootNode).children;
|
|
753
|
+
}
|
|
754
|
+
/**
|
|
755
|
+
* Search the repository's paths for a fragment — case-insensitive, over the
|
|
756
|
+
* full path. Results are FLAT: a search list is not a tree (the same honesty
|
|
757
|
+
* as the filtered commit list), and each hit ticks as a pathspec directly.
|
|
758
|
+
*
|
|
759
|
+
* Directories match too: every directory is some file's prefix, and ticking a
|
|
760
|
+
* directory covers its subtree — the search takes the raw path list the host
|
|
761
|
+
* sent, so root-level files and unexpanded directories are all in scope.
|
|
762
|
+
* @param paths - repo-relative file paths, exactly as `repoTree` returned.
|
|
763
|
+
* @param needle - raw search text; blank matches nothing (caller shows the tree).
|
|
764
|
+
*/
|
|
765
|
+
function searchPaths(paths, needle) {
|
|
766
|
+
const n = needle.trim().toLowerCase();
|
|
767
|
+
if (n.length === 0) return [];
|
|
768
|
+
const hits = [];
|
|
769
|
+
const seen = /* @__PURE__ */ new Set();
|
|
770
|
+
for (const path of paths) {
|
|
771
|
+
if (path.toLowerCase().includes(n)) {
|
|
772
|
+
hits.push({
|
|
773
|
+
path,
|
|
774
|
+
isFile: true
|
|
775
|
+
});
|
|
776
|
+
seen.add(path);
|
|
777
|
+
}
|
|
778
|
+
const parts = path.split("/");
|
|
779
|
+
for (let i = 1; i < parts.length; i += 1) {
|
|
780
|
+
const dir = parts.slice(0, i).join("/");
|
|
781
|
+
if (!seen.has(dir) && dir.toLowerCase().includes(n)) {
|
|
782
|
+
seen.add(dir);
|
|
783
|
+
hits.push({
|
|
784
|
+
path: dir,
|
|
785
|
+
isFile: false
|
|
786
|
+
});
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
return [...hits.filter((hit) => hit.isFile), ...hits.filter((hit) => !hit.isFile)];
|
|
791
|
+
}
|
|
792
|
+
//#endregion
|
|
793
|
+
//#region src/client/file-filter.ts
|
|
794
|
+
/**
|
|
795
|
+
* Narrowing a file list by typing at it.
|
|
796
|
+
*
|
|
797
|
+
* A commit that touched 140 files is a scroll, not a list, and the drawer's
|
|
798
|
+
* tree is the same object in every tab — so the rule lives here once and both
|
|
799
|
+
* the working tree and a commit's contents get it.
|
|
800
|
+
*
|
|
801
|
+
* Two decisions worth stating, because both are the kind that get "simplified"
|
|
802
|
+
* later:
|
|
803
|
+
*
|
|
804
|
+
* - **Terms are ANDed, in any order.** `panel css` finds
|
|
805
|
+
* `src/client/GitWorkbenchPanel.module.css` — which is how anyone types
|
|
806
|
+
* when they half-remember a path, and is the behaviour a single-substring
|
|
807
|
+
* match gets wrong for exactly the paths that are long enough to need
|
|
808
|
+
* filtering.
|
|
809
|
+
* - **Smart case.** An all-lowercase query ignores case; the moment the
|
|
810
|
+
* reader types a capital they mean it. `README` should not match
|
|
811
|
+
* `readme-generator`, and `readme` should still find `README.md`.
|
|
812
|
+
*
|
|
813
|
+
* The result keeps the caller's order and its element type: the tree is built
|
|
814
|
+
* from whatever survives, so filtering never has to know what a file is beyond
|
|
815
|
+
* its path.
|
|
816
|
+
*
|
|
817
|
+
* @module @young1lin/dsh-ui-gitworkbench/client/file-filter
|
|
818
|
+
*/
|
|
819
|
+
/** Split a raw query into the terms every path must contain. */
|
|
820
|
+
function termsOf(query) {
|
|
821
|
+
return query.split(/\s+/).filter((term) => term.length > 0);
|
|
822
|
+
}
|
|
823
|
+
/**
|
|
824
|
+
* Whether one path satisfies a query.
|
|
825
|
+
*
|
|
826
|
+
* @param path - repo-relative path, as the tree lists it.
|
|
827
|
+
* @param query - raw text from the filter box; blank matches everything, so a
|
|
828
|
+
* caller that renders `filterFiles` unconditionally shows the
|
|
829
|
+
* whole list until something is typed.
|
|
830
|
+
*/
|
|
831
|
+
function matchesPath(path, query) {
|
|
832
|
+
const terms = termsOf(query);
|
|
833
|
+
if (terms.length === 0) return true;
|
|
834
|
+
return terms.every((term) => {
|
|
835
|
+
return term.toLowerCase() !== term ? path.includes(term) : path.toLowerCase().includes(term.toLowerCase());
|
|
836
|
+
});
|
|
837
|
+
}
|
|
838
|
+
/**
|
|
839
|
+
* Keep the files whose path satisfies the query, in the order given.
|
|
840
|
+
*
|
|
841
|
+
* @param files - anything carrying a `path`; the tree's own file objects.
|
|
842
|
+
* @param query - raw text from the filter box.
|
|
843
|
+
* @returns the same array instance when nothing is filtered out, so a blank
|
|
844
|
+
* query costs no re-render downstream.
|
|
845
|
+
*/
|
|
846
|
+
function filterFiles(files, query) {
|
|
847
|
+
if (termsOf(query).length === 0) return files;
|
|
848
|
+
return files.filter((file) => matchesPath(file.path, query));
|
|
849
|
+
}
|
|
850
|
+
//#endregion
|
|
851
|
+
//#region src/client/path-select.ts
|
|
852
|
+
/**
|
|
853
|
+
* Index a raw path list for child lookup.
|
|
854
|
+
* @param paths - repo-relative file paths, exactly as `repoTree` returned.
|
|
855
|
+
*/
|
|
856
|
+
function buildIndex(paths) {
|
|
857
|
+
const dirs = /* @__PURE__ */ new Map();
|
|
858
|
+
const files = /* @__PURE__ */ new Map();
|
|
859
|
+
const noteDir = (dir) => {
|
|
860
|
+
if (!dirs.has(dir)) dirs.set(dir, []);
|
|
861
|
+
};
|
|
862
|
+
noteDir("");
|
|
863
|
+
for (const path of paths) {
|
|
864
|
+
if (path.length === 0) continue;
|
|
865
|
+
const parts = path.split("/");
|
|
866
|
+
let dir = "";
|
|
867
|
+
for (let i = 0; i < parts.length - 1; i += 1) {
|
|
868
|
+
const childDir = dir === "" ? parts[i] : `${dir}/${parts[i]}`;
|
|
869
|
+
noteDir(childDir);
|
|
870
|
+
const list = dirs.get(dir);
|
|
871
|
+
if (!list.includes(childDir)) list.push(childDir);
|
|
872
|
+
dir = childDir;
|
|
873
|
+
}
|
|
874
|
+
const list = files.get(dir) ?? [];
|
|
875
|
+
list.push(path);
|
|
876
|
+
files.set(dir, list);
|
|
877
|
+
}
|
|
878
|
+
for (const list of dirs.values()) list.sort();
|
|
879
|
+
for (const list of files.values()) list.sort();
|
|
880
|
+
return {
|
|
881
|
+
dirs,
|
|
882
|
+
files
|
|
883
|
+
};
|
|
884
|
+
}
|
|
885
|
+
/** Children of a directory, alphabetical by full path — the tree's order. */
|
|
886
|
+
function childrenOf(index, dir) {
|
|
887
|
+
return [...index.dirs.get(dir) ?? [], ...index.files.get(dir) ?? []].sort();
|
|
888
|
+
}
|
|
889
|
+
/**
|
|
890
|
+
* Is `p` selected — itself ticked, or inside a ticked directory?
|
|
891
|
+
* (Segment-boundary prefix: `src` does not cover `src2`.)
|
|
892
|
+
*/
|
|
893
|
+
function isCovered(paths, p) {
|
|
894
|
+
return paths.some((tick) => tick === p || p.startsWith(`${tick}/`));
|
|
895
|
+
}
|
|
896
|
+
/**
|
|
897
|
+
* Tick a path. No-op when an ancestor already covers it; absorbs every
|
|
898
|
+
* descendant it covers, keeping the set minimal — one folder chip, never the
|
|
899
|
+
* pile of files under it.
|
|
900
|
+
*/
|
|
901
|
+
function addPath(paths, p) {
|
|
902
|
+
if (isCovered(paths, p)) return paths;
|
|
903
|
+
return [...paths.filter((tick) => !(tick === p || tick.startsWith(`${p}/`))), p];
|
|
904
|
+
}
|
|
905
|
+
/**
|
|
906
|
+
* Untick a path. Removing an exact tick drops it; removing a file COVERED by
|
|
907
|
+
* a ticked folder replaces that folder with its other children, level by
|
|
908
|
+
* level down to the file — the standard cascade-out.
|
|
909
|
+
*/
|
|
910
|
+
function removePath(paths, p, index) {
|
|
911
|
+
const out = [];
|
|
912
|
+
for (const tick of paths) {
|
|
913
|
+
if (tick !== p && !p.startsWith(`${tick}/`)) {
|
|
914
|
+
out.push(tick);
|
|
915
|
+
continue;
|
|
916
|
+
}
|
|
917
|
+
let dir = tick;
|
|
918
|
+
while (dir !== p) {
|
|
919
|
+
const rest = p.slice(dir.length + 1);
|
|
920
|
+
const nextName = dir === "" ? p.split("/")[0] : rest.split("/")[0];
|
|
921
|
+
const next = dir === "" ? nextName : `${dir}/${nextName}`;
|
|
922
|
+
for (const child of childrenOf(index, dir)) if (child !== next) out.push(child);
|
|
923
|
+
dir = next;
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
return out;
|
|
927
|
+
}
|
|
928
|
+
/** Every file under a directory (empty for a file path). */
|
|
929
|
+
function filesUnder$1(index, dir) {
|
|
930
|
+
const out = [];
|
|
931
|
+
const stack = [dir];
|
|
932
|
+
while (stack.length > 0) {
|
|
933
|
+
const current = stack.pop();
|
|
934
|
+
out.push(...index.files.get(current) ?? []);
|
|
935
|
+
stack.push(...index.dirs.get(current) ?? []);
|
|
936
|
+
}
|
|
937
|
+
return out;
|
|
938
|
+
}
|
|
939
|
+
/**
|
|
940
|
+
* A row's checkbox state, derived: `on` when covered — by an ancestor tick OR
|
|
941
|
+
* by every file under it being covered individually; `partial` when a
|
|
942
|
+
* directory holds some but not all of its files; else `off`.
|
|
943
|
+
*/
|
|
944
|
+
function checkedState(paths, p, index) {
|
|
945
|
+
if (isCovered(paths, p)) return "on";
|
|
946
|
+
const files = filesUnder$1(index, p);
|
|
947
|
+
if (files.length === 0) return "off";
|
|
948
|
+
const covered = files.filter((file) => isCovered(paths, file)).length;
|
|
949
|
+
return covered === files.length ? "on" : covered > 0 ? "partial" : "off";
|
|
950
|
+
}
|
|
951
|
+
//#endregion
|
|
952
|
+
//#region src/client/calendar.ts
|
|
953
|
+
const DAY_MS = 864e5;
|
|
954
|
+
function toIso(date) {
|
|
955
|
+
const m = String(date.getUTCMonth() + 1).padStart(2, "0");
|
|
956
|
+
const d = String(date.getUTCDate()).padStart(2, "0");
|
|
957
|
+
return `${date.getUTCFullYear()}-${m}-${d}`;
|
|
958
|
+
}
|
|
959
|
+
/**
|
|
960
|
+
* The 6×7 Monday-first grid for one month.
|
|
961
|
+
* @param year - displayed year.
|
|
962
|
+
* @param month - displayed month, 0-based like `Date`.
|
|
963
|
+
* @param todayIso - what counts as today, for the accent; `''` accents nothing.
|
|
964
|
+
*/
|
|
965
|
+
function monthGrid(year, month, todayIso) {
|
|
966
|
+
const first = new Date(Date.UTC(year, month, 1));
|
|
967
|
+
const lead = (first.getUTCDay() + 6) % 7;
|
|
968
|
+
const start = /* @__PURE__ */ new Date(first.getTime() - lead * DAY_MS);
|
|
969
|
+
const weeks = [];
|
|
970
|
+
for (let w = 0; w < 6; w += 1) {
|
|
971
|
+
const row = [];
|
|
972
|
+
for (let d = 0; d < 7; d += 1) {
|
|
973
|
+
const date = new Date(start.getTime() + (w * 7 + d) * DAY_MS);
|
|
974
|
+
const iso = toIso(date);
|
|
975
|
+
row.push({
|
|
976
|
+
iso,
|
|
977
|
+
day: date.getUTCDate(),
|
|
978
|
+
inMonth: date.getUTCMonth() === month,
|
|
979
|
+
isToday: iso === todayIso
|
|
980
|
+
});
|
|
981
|
+
}
|
|
982
|
+
weeks.push(row);
|
|
983
|
+
}
|
|
984
|
+
return weeks;
|
|
985
|
+
}
|
|
986
|
+
/**
|
|
987
|
+
* Single-letter weekday header row, Monday-first, via the viewer's own locale
|
|
988
|
+
* (or an explicit one, which is what the test does).
|
|
989
|
+
* @param locale - BCP 47 tag; undefined means the runtime default.
|
|
990
|
+
*/
|
|
991
|
+
function weekdayLabels(locale) {
|
|
992
|
+
const labels = [];
|
|
993
|
+
for (let d = 2; d <= 8; d += 1) labels.push(new Intl.DateTimeFormat(locale, { weekday: "narrow" }).format(new Date(Date.UTC(2023, 0, d))));
|
|
994
|
+
return labels;
|
|
995
|
+
}
|
|
996
|
+
/**
|
|
997
|
+
* Whether a day falls strictly BETWEEN the two bounds, so the grid can tint
|
|
998
|
+
* the span the filter admits rather than only its two endpoints.
|
|
999
|
+
*
|
|
1000
|
+
* Both bounds have to be `yyyy-mm-dd` for a range to exist: the bounds also
|
|
1001
|
+
* accept approxidate text (`1 week ago`), which names no grid day at all, and
|
|
1002
|
+
* lexical comparison on iso dates is the same as chronological. An inverted
|
|
1003
|
+
* pair (after > before) is a range git returns nothing for, and it tints
|
|
1004
|
+
* nothing here for the same reason.
|
|
1005
|
+
*
|
|
1006
|
+
* @param iso - the cell's day.
|
|
1007
|
+
* @param after - lower bound, exclusive here (it renders as an endpoint).
|
|
1008
|
+
* @param before - upper bound, exclusive here.
|
|
1009
|
+
*/
|
|
1010
|
+
function inCalRange(iso, after, before) {
|
|
1011
|
+
if (!ISO_DAY.test(after) || !ISO_DAY.test(before)) return false;
|
|
1012
|
+
return iso > after && iso < before;
|
|
1013
|
+
}
|
|
1014
|
+
const ISO_DAY = /^\d{4}-\d{2}-\d{2}$/;
|
|
1015
|
+
/** Today as `yyyy-mm-dd` in the viewer's local timezone (for `todayIso`). */
|
|
1016
|
+
function localTodayIso() {
|
|
1017
|
+
const now = /* @__PURE__ */ new Date();
|
|
1018
|
+
const m = String(now.getMonth() + 1).padStart(2, "0");
|
|
1019
|
+
const d = String(now.getDate()).padStart(2, "0");
|
|
1020
|
+
return `${now.getFullYear()}-${m}-${d}`;
|
|
1021
|
+
}
|
|
1022
|
+
//#endregion
|
|
1023
|
+
//#region src/client/active-file.ts
|
|
1024
|
+
/** No filter — a shared empty so callers keep a stable reference. */
|
|
1025
|
+
const NO_PATHS = [];
|
|
1026
|
+
/**
|
|
1027
|
+
* Whether a file is what a pathspec selected: the file itself, or anything in
|
|
1028
|
+
* its subtree.
|
|
1029
|
+
*
|
|
1030
|
+
* A pathspec from the picker is either a file path or a directory path with no
|
|
1031
|
+
* trailing slash (`dir.path` / the file's full path — `path-select.ts`), and
|
|
1032
|
+
* the two cases are told apart by the file rather than by the spec: `===` is
|
|
1033
|
+
* the file, `spec + '/'` prefix is the subtree. Guessing which KIND a spec is
|
|
1034
|
+
* from its string alone is what a trailing-slash convention would force, and
|
|
1035
|
+
* it would be wrong for any file without an extension.
|
|
1036
|
+
*/
|
|
1037
|
+
function covers(spec, path) {
|
|
1038
|
+
return path === spec || path.startsWith(`${spec}/`);
|
|
1039
|
+
}
|
|
1040
|
+
/**
|
|
1041
|
+
* The file a view should highlight.
|
|
1042
|
+
*
|
|
1043
|
+
* The order of preference, and why it is this order:
|
|
1044
|
+
*
|
|
1045
|
+
* 1. **The selection, if this view has it.** Stepping down a filtered list is
|
|
1046
|
+
* the whole point of filtering; changing the file under the reader every
|
|
1047
|
+
* time they move a row would undo it. This also means an explicit click
|
|
1048
|
+
* outranks the filter — the reader looked somewhere on purpose.
|
|
1049
|
+
* 2. **A file the filter names EXACTLY.** Ticking `xx/aa/dd.ts` is a statement
|
|
1050
|
+
* about that file; ticking `xx` is a statement about a region. When a
|
|
1051
|
+
* commit touches both kinds, the named file is the more specific intent, so
|
|
1052
|
+
* it wins. (The two can only coexist across disjoint trees: the picker's
|
|
1053
|
+
* invariant is that no ticked path covers another.)
|
|
1054
|
+
* 3. **A file under a filtered directory.**
|
|
1055
|
+
* 4. **The first file.** No filter, or nothing in this commit matched it —
|
|
1056
|
+
* the behaviour before any of this existed.
|
|
1057
|
+
*
|
|
1058
|
+
* Ties inside 2 and 3 go to the commit's own file order, which is the order
|
|
1059
|
+
* the tree renders: the highlight lands on the topmost matching row, so it is
|
|
1060
|
+
* where the reader is already looking and never needs a scroll to find. The
|
|
1061
|
+
* alternative — first match in FILTER order — would be arbitrary, since that
|
|
1062
|
+
* order is an artifact of the sequence the boxes were ticked in and is never
|
|
1063
|
+
* shown anywhere.
|
|
1064
|
+
*
|
|
1065
|
+
* @param files - the view's files, in the order the tree shows them.
|
|
1066
|
+
* @param filterPaths - active path filter; empty on views that have none.
|
|
1067
|
+
* @param previous - the currently selected path, or null.
|
|
1068
|
+
* @returns the path to highlight, or null when there are no files at all.
|
|
1069
|
+
*/
|
|
1070
|
+
function preferredFile(files, filterPaths, previous) {
|
|
1071
|
+
if (previous !== null && files.some((file) => file.path === previous)) return previous;
|
|
1072
|
+
if (filterPaths.length > 0) {
|
|
1073
|
+
const exact = files.find((file) => filterPaths.some((spec) => file.path === spec));
|
|
1074
|
+
if (exact !== void 0) return exact.path;
|
|
1075
|
+
const under = files.find((file) => filterPaths.some((spec) => covers(spec, file.path)));
|
|
1076
|
+
if (under !== void 0) return under.path;
|
|
1077
|
+
}
|
|
1078
|
+
return files[0]?.path ?? null;
|
|
1079
|
+
}
|
|
1080
|
+
//#endregion
|
|
490
1081
|
//#region src/client/stage-tree.ts
|
|
491
1082
|
/**
|
|
492
1083
|
* One file's tick.
|
|
@@ -11613,7 +12204,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
11613
12204
|
}
|
|
11614
12205
|
//#endregion
|
|
11615
12206
|
//#region \0dsh-css:src/client/GitWorkbenchPanel.module.css.mjs
|
|
11616
|
-
const css = ".SD8qLW_card{border:1px solid var(--dsw-alias-border-l2);min-height:28px;color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;cursor:pointer;background:0 0;border-radius:8px;align-items:center;gap:7px;padding:3px 10px;font-size:12px;line-height:18px;transition:background .12s,border-color .12s;display:inline-flex}.SD8qLW_card:hover,.SD8qLW_card:focus-visible{background:var(--dsw-alias-interactive-bg-hover);border-color:var(--dsw-alias-border-l3)}.SD8qLW_cardBranch{max-width:220px;color:var(--dsw-alias-label-primary);align-items:center;gap:4px;font-weight:550;display:inline-flex;overflow:hidden}.SD8qLW_cardBranchName{min-width:0;overflow:hidden}.SD8qLW_cardGlyph{opacity:.7;flex:none}.SD8qLW_cardDetached{background:var(--dsw-alias-state-warning-bg,#bb800926);color:var(--dsw-alias-state-warning-primary,#d29922);letter-spacing:.02em;border-radius:999px;padding:0 6px;font-size:10px;line-height:16px}.SD8qLW_cardWt{background:var(--dsw-alias-state-business-bg,#388bfd29);max-width:150px;color:var(--dsw-alias-state-business-primary,#58a6ff);letter-spacing:.02em;white-space:nowrap;text-overflow:ellipsis;border-radius:999px;align-items:center;gap:3px;padding:0 6px;font-size:10px;line-height:16px;display:inline-flex;overflow:hidden}.SD8qLW_cardAhead,.SD8qLW_cardBehind{color:var(--dsw-alias-label-tertiary);font-size:11px}.SD8qLW_cardAhead{color:var(--dsw-alias-state-success-primary)}.SD8qLW_cardBehind{color:var(--dsw-alias-state-attention-primary,#d29922)}.SD8qLW_cardSep{background:var(--dsw-alias-border-l2);width:1px;height:14px}.SD8qLW_cardAdded{color:var(--dsw-alias-state-success-primary)}.SD8qLW_cardDeleted{color:var(--dsw-alias-state-error-primary)}.SD8qLW_cardFiles{color:var(--dsw-alias-label-tertiary)}.SD8qLW_overlay[data-gs-theme=github-dark]{--gs-bg:#0d1117;--gs-panel:#161b22;--gs-raise:#21262d;--gs-border:#30363d;--gs-border-soft:#21262d;--gs-fg:#e6edf3;--gs-fg-muted:#c9d1d9;--gs-fg-dim:#8b949e;--gs-fg-faint:#6e7681;--gs-fg-fainter:#484f58;--gs-accent:#58a6ff;--gs-accent-bg:#388bfd24;--gs-accent-border:#388bfd8c;--gs-add:#3fb950;--gs-del:#f85149;--gs-warn:#d29922;--gs-info:#79c0ff;--gs-add-bg:#2ea04329;--gs-del-bg:#f8514929;--gs-warn-bg:#bb800929;--gs-info-bg:#388bfd29;--gs-add-line:#12261e;--gs-del-line:#25181c;--gs-add-word:#1a4a29;--gs-del-word:#6b2b2b;--gs-add-num:#1c4428;--gs-del-num:#542426;--gs-hunk:#111d2e;--gs-hunk-num:#0c2d6b;--gs-neutral-bg:#6e76812e;--gs-backdrop:#01040999;--gs-shadow:#0104098c}.SD8qLW_overlay[data-gs-theme=github-light]{--gs-bg:#fff;--gs-panel:#f6f8fa;--gs-raise:#eaeef2;--gs-border:#d0d7de;--gs-border-soft:#eaeef2;--gs-fg:#1f2328;--gs-fg-muted:#32383f;--gs-fg-dim:#656d76;--gs-fg-faint:#8c959f;--gs-fg-fainter:#afb8c1;--gs-accent:#0969da;--gs-accent-bg:#0969da1a;--gs-accent-border:#0969da73;--gs-add:#1a7f37;--gs-del:#cf222e;--gs-warn:#9a6700;--gs-info:#0550ae;--gs-add-bg:#1a7f371f;--gs-del-bg:#cf222e1f;--gs-warn-bg:#9a67001f;--gs-info-bg:#0969da1f;--gs-add-line:#dafbe1;--gs-del-line:#ffebe9;--gs-add-word:#aceebb;--gs-del-word:#ffcecb;--gs-add-num:#aceebb;--gs-del-num:#ffcecb;--gs-hunk:#ddf4ff;--gs-hunk-num:#b6e3ff;--gs-neutral-bg:#6e76811f;--gs-backdrop:#1f232847;--gs-shadow:#1f232838}.SD8qLW_overlay[data-gs-theme=idea-dark]{--gs-bg:#1e1f22;--gs-panel:#2b2d30;--gs-raise:#393b40;--gs-border:#393b40;--gs-border-soft:#2b2d30;--gs-fg:#dfe1e5;--gs-fg-muted:#ced0d6;--gs-fg-dim:#9da0a8;--gs-fg-faint:#6f737a;--gs-fg-fainter:#4e5157;--gs-accent:#548af7;--gs-accent-bg:#548af729;--gs-accent-border:#548af78c;--gs-add:#5fad65;--gs-del:#e26e6e;--gs-warn:#f0a732;--gs-info:#3592c4;--gs-add-bg:#5fad6529;--gs-del-bg:#e26e6e29;--gs-warn-bg:#f0a73229;--gs-info-bg:#3592c429;--gs-add-line:#293c2e;--gs-del-line:#3d2b2b;--gs-add-word:#3d6640;--gs-del-word:#6b3838;--gs-hunk:#548af71a;--gs-neutral-bg:#9da0a82e;--gs-backdrop:#10111399;--gs-shadow:#00000080}.SD8qLW_overlay[data-gs-theme=idea-light]{--gs-bg:#fff;--gs-panel:#f7f8fa;--gs-raise:#ebecf0;--gs-border:#d3d5db;--gs-border-soft:#ebecf0;--gs-fg:#1e1f22;--gs-fg-muted:#3c3f44;--gs-fg-dim:#6c707e;--gs-fg-faint:#818594;--gs-fg-fainter:#a8adbd;--gs-accent:#3574f0;--gs-accent-bg:#3574f01a;--gs-accent-border:#3574f073;--gs-add:#398a4a;--gs-del:#cc4a4a;--gs-warn:#a8760b;--gs-info:#2b7fb8;--gs-add-bg:#398a4a1f;--gs-del-bg:#cc4a4a1f;--gs-warn-bg:#a8760b1f;--gs-info-bg:#2b7fb81f;--gs-add-line:#e6f5e9;--gs-del-line:#fbe9ea;--gs-add-word:#c2e5c9;--gs-del-word:#f5c9cc;--gs-hunk:#3574f012;--gs-neutral-bg:#6c707e1f;--gs-backdrop:#1e1f2242;--gs-shadow:#1e1f222e}.SD8qLW_overlay[data-gs-theme=vscode-dark]{--gs-bg:#1e1e1e;--gs-panel:#252526;--gs-raise:#2d2d30;--gs-border:#3e3e42;--gs-border-soft:#2d2d30;--gs-fg:#d4d4d4;--gs-fg-muted:#ccc;--gs-fg-dim:#9d9d9d;--gs-fg-faint:gray;--gs-fg-fainter:#5a5a5a;--gs-accent:#0098ff;--gs-accent-bg:#007acc3d;--gs-accent-border:#0098ff99;--gs-add:#89d185;--gs-del:#f14c4c;--gs-warn:#cca700;--gs-info:#75beff;--gs-add-bg:#89d1852e;--gs-del-bg:#f14c4c2e;--gs-warn-bg:#cca7002e;--gs-info-bg:#75beff2e;--gs-add-line:#9bb9552e;--gs-del-line:#ff000029;--gs-add-word:#9bb95566;--gs-del-word:#ff00005c;--gs-hunk:#007acc24;--gs-neutral-bg:#bebebe24;--gs-backdrop:#0000008c;--gs-shadow:#0009}.SD8qLW_overlay[data-gs-theme=vscode-light]{--gs-bg:#fff;--gs-panel:#f3f3f3;--gs-raise:#e8e8e8;--gs-border:#cecece;--gs-border-soft:#e8e8e8;--gs-fg:#1e1e1e;--gs-fg-muted:#333;--gs-fg-dim:#616161;--gs-fg-faint:#767676;--gs-fg-fainter:#a0a0a0;--gs-accent:#005fb8;--gs-accent-bg:#005fb81a;--gs-accent-border:#005fb873;--gs-add:#10793f;--gs-del:#b5200d;--gs-warn:#855b00;--gs-info:#005fb8;--gs-add-bg:#10793f1f;--gs-del-bg:#b5200d1f;--gs-warn-bg:#855b001f;--gs-info-bg:#005fb81f;--gs-add-line:#9bb95538;--gs-del-line:#ff000021;--gs-add-word:#9bb95573;--gs-del-word:#ff00004d;--gs-hunk:#005fb812;--gs-neutral-bg:#6161611f;--gs-backdrop:#00000040;--gs-shadow:#0000002e}.SD8qLW_overlay[data-gs-theme=cyberpunk-dark]{--gs-bg:#0b0417;--gs-panel:#14082a;--gs-raise:#1f0d3d;--gs-border:#3a1f6b;--gs-border-soft:#26124a;--gs-fg:#f0e6ff;--gs-fg-muted:#d5c2f5;--gs-fg-dim:#a98fd6;--gs-fg-faint:#7d64a8;--gs-fg-fainter:#56427a;--gs-accent:#00f0ff;--gs-on-accent:#0b0417;--gs-accent-bg:#00f0ff24;--gs-accent-border:#00f0ff80;--gs-add:#39ff88;--gs-del:#ff2e88;--gs-warn:#fc0;--gs-info:#00f0ff;--gs-add-bg:#39ff8824;--gs-del-bg:#ff2e8824;--gs-warn-bg:#ffcc0024;--gs-info-bg:#00f0ff24;--gs-add-line:#39ff881f;--gs-del-line:#ff2e881f;--gs-add-word:#39ff8857;--gs-del-word:#ff2e8857;--gs-hunk:#00f0ff1a;--gs-neutral-bg:#a98fd629;--gs-backdrop:#06020ead;--gs-shadow:#00f0ff24}.SD8qLW_overlay[data-gs-theme=cyberpunk-light]{--gs-bg:#fdfbff;--gs-panel:#f4eeff;--gs-raise:#e9dfff;--gs-border:#c9b3f0;--gs-border-soft:#e0d2f7;--gs-fg:#1a0b2e;--gs-fg-muted:#33195c;--gs-fg-dim:#6b4ba0;--gs-fg-faint:#8f74bd;--gs-fg-fainter:#b9a5d6;--gs-accent:#00a6b8;--gs-accent-bg:#00a6b81f;--gs-accent-border:#00a6b880;--gs-add:#00875a;--gs-del:#d6006e;--gs-warn:#b37a00;--gs-info:#00a6b8;--gs-add-bg:#00875a1f;--gs-del-bg:#d6006e1f;--gs-warn-bg:#b37a001f;--gs-info-bg:#00a6b81f;--gs-add-line:#00c88224;--gs-del-line:#ff2e881f;--gs-add-word:#00c88257;--gs-del-word:#ff2e884d;--gs-hunk:#00a6b814;--gs-neutral-bg:#6b4ba01f;--gs-backdrop:#1a0b2e47;--gs-shadow:#1a0b2e29}.SD8qLW_overlay[data-gs-theme=one-dark]{--gs-bg:#282c34;--gs-panel:#21252b;--gs-raise:#2c313a;--gs-border:#3e4451;--gs-border-soft:#2c313a;--gs-fg:#abb2bf;--gs-fg-muted:#b6bdca;--gs-fg-dim:#7f848e;--gs-fg-faint:#636d83;--gs-fg-fainter:#4b5263;--gs-accent:#61afef;--gs-accent-bg:#61afef24;--gs-accent-border:#61afef8c;--gs-add:#98c379;--gs-del:#e06c75;--gs-warn:#e5c07b;--gs-info:#56b6c2;--gs-add-bg:#98c37929;--gs-del-bg:#e06c7529;--gs-warn-bg:#e5c07b29;--gs-info-bg:#56b6c229;--gs-add-line:#98c37924;--gs-del-line:#e06c7524;--gs-add-word:#98c37961;--gs-del-word:#e06c7561;--gs-hunk:#61afef1a;--gs-neutral-bg:#7f848e2e;--gs-backdrop:#171a2199;--gs-shadow:#0f11158c}.SD8qLW_overlay[data-gs-theme=one-light]{--gs-bg:#fafafa;--gs-panel:#f0f0f0;--gs-raise:#e5e5e6;--gs-border:#d4d4d4;--gs-border-soft:#e5e5e6;--gs-fg:#383a42;--gs-fg-muted:#4a4c53;--gs-fg-dim:#696c77;--gs-fg-faint:#909196;--gs-fg-fainter:#b8b9bd;--gs-accent:#4078f2;--gs-accent-bg:#4078f21a;--gs-accent-border:#4078f273;--gs-add:#50a14f;--gs-del:#e45649;--gs-warn:#c18401;--gs-info:#0184bc;--gs-add-bg:#50a14f1f;--gs-del-bg:#e456491f;--gs-warn-bg:#c184011f;--gs-info-bg:#0184bc1f;--gs-add-line:#50a14f24;--gs-del-line:#e4564921;--gs-add-word:#50a14f57;--gs-del-word:#e4564952;--gs-hunk:#4078f212;--gs-neutral-bg:#696c771f;--gs-backdrop:#383a4242;--gs-shadow:#383a422e}.SD8qLW_overlay[data-gs-theme=solarized-dark]{--gs-bg:#002b36;--gs-panel:#073642;--gs-raise:#0a4553;--gs-border:#0f5666;--gs-border-soft:#073642;--gs-fg:#93a1a1;--gs-fg-muted:#839496;--gs-fg-dim:#657b83;--gs-fg-faint:#586e75;--gs-fg-fainter:#45636b;--gs-accent:#268bd2;--gs-accent-bg:#268bd229;--gs-accent-border:#268bd28c;--gs-add:#859900;--gs-del:#dc322f;--gs-warn:#b58900;--gs-info:#2aa198;--gs-add-bg:#85990033;--gs-del-bg:#dc322f2e;--gs-warn-bg:#b589002e;--gs-info-bg:#2aa1982e;--gs-add-line:#85990029;--gs-del-line:#dc322f26;--gs-add-word:#85990066;--gs-del-word:#dc322f61;--gs-hunk:#268bd21f;--gs-neutral-bg:#657b8333;--gs-backdrop:#00141a9e;--gs-shadow:#00141a8c}.SD8qLW_overlay[data-gs-theme=solarized-light]{--gs-bg:#fdf6e3;--gs-panel:#eee8d5;--gs-raise:#e3ddca;--gs-border:#d5cfbb;--gs-border-soft:#eee8d5;--gs-fg:#586e75;--gs-fg-muted:#657b83;--gs-fg-dim:#839496;--gs-fg-faint:#93a1a1;--gs-fg-fainter:#b5b0a0;--gs-accent:#268bd2;--gs-accent-bg:#268bd21f;--gs-accent-border:#268bd273;--gs-add:#6c7c00;--gs-del:#cb2825;--gs-warn:#9a7400;--gs-info:#21867f;--gs-add-bg:#8599002e;--gs-del-bg:#dc322f24;--gs-warn-bg:#b5890029;--gs-info-bg:#2aa19824;--gs-add-line:#85990029;--gs-del-line:#dc322f1f;--gs-add-word:#85990061;--gs-del-word:#dc322f4d;--gs-hunk:#268bd214;--gs-neutral-bg:#586e7524;--gs-backdrop:#586e7542;--gs-shadow:#586e7533}.SD8qLW_overlay[data-gs-theme=nord-dark]{--gs-bg:#2e3440;--gs-panel:#3b4252;--gs-raise:#434c5e;--gs-border:#4c566a;--gs-border-soft:#3b4252;--gs-fg:#eceff4;--gs-fg-muted:#d8dee9;--gs-fg-dim:#9aa5b8;--gs-fg-faint:#7b88a1;--gs-fg-fainter:#5d6a82;--gs-accent:#88c0d0;--gs-on-accent:#2e3440;--gs-accent-bg:#88c0d024;--gs-accent-border:#88c0d08c;--gs-add:#a3be8c;--gs-del:#bf616a;--gs-warn:#ebcb8b;--gs-info:#81a1c1;--gs-add-bg:#a3be8c29;--gs-del-bg:#bf616a2e;--gs-warn-bg:#ebcb8b29;--gs-info-bg:#81a1c129;--gs-add-line:#a3be8c24;--gs-del-line:#bf616a26;--gs-add-word:#a3be8c61;--gs-del-word:#bf616a66;--gs-hunk:#88c0d01a;--gs-neutral-bg:#9aa5b829;--gs-backdrop:#1d232d9e;--gs-shadow:#1419218c}.SD8qLW_overlay[data-gs-theme=nord-light]{--gs-bg:#eceff4;--gs-panel:#e5e9f0;--gs-raise:#d8dee9;--gs-border:#c3cbd8;--gs-border-soft:#dfe4ec;--gs-fg:#2e3440;--gs-fg-muted:#3b4252;--gs-fg-dim:#4c566a;--gs-fg-faint:#6b7689;--gs-fg-fainter:#9aa4b5;--gs-accent:#5e81ac;--gs-accent-bg:#5e81ac1f;--gs-accent-border:#5e81ac80;--gs-add:#5e7d47;--gs-del:#a5424c;--gs-warn:#97701f;--gs-info:#4c7a8c;--gs-add-bg:#5e7d4724;--gs-del-bg:#a5424c21;--gs-warn-bg:#97701f24;--gs-info-bg:#4c7a8c21;--gs-add-line:#5e7d4724;--gs-del-line:#a5424c1f;--gs-add-word:#5e7d4757;--gs-del-word:#a5424c52;--gs-hunk:#5e81ac17;--gs-neutral-bg:#4c566a21;--gs-backdrop:#2e344042;--gs-shadow:#2e34402e}.SD8qLW_overlay{--gs-inset:14px;--gs-h-control:28px;--gs-h-compact:24px;--gs-pad-control:0 12px;--gs-pad-compact:0 10px;--gs-on-accent:#fff;--gs-graph-0:#58a6ff;--gs-graph-1:#3fb950;--gs-graph-2:#d29922;--gs-graph-3:#bc8cff;--gs-graph-4:#f778ba;--gs-graph-5:#39c5cf;--gs-r-pill:999px;--gs-r-control:8px;--gs-r-surface:12px;--gs-r-drawer:14px;--gs-t-meta:11px;--gs-t-dense:12px;--gs-t-ui:13px;--gs-gutter:16px;--gs-gutter-pane:12px;z-index:1000;padding:var(--gs-inset);box-sizing:border-box;background:var(--gs-backdrop);justify-content:flex-end;align-items:stretch;gap:12px;animation:.16s ease-out SD8qLW_gsFade;display:flex;position:fixed;inset:0}.SD8qLW_drawer{box-sizing:border-box;width:min(1600px,94vw);height:100%;color:var(--gs-fg);background:var(--gs-bg);border:1px solid var(--gs-border);border-radius:var(--gs-r-drawer);box-shadow:0 24px 64px var(--gs-shadow);font-family:var(--dsw-font-ui,-apple-system, BlinkMacSystemFont, \"Segoe UI\", Helvetica, Arial, sans-serif);--gs-surface:var(--gs-bg);--gs-surface-2:var(--gs-panel);flex-direction:column;animation:.18s cubic-bezier(.22,1,.36,1) SD8qLW_gsSlide;display:flex;position:relative;overflow:hidden}.SD8qLW_drawer[data-gs-bg]{--gs-surface:color-mix(in srgb, var(--gs-bg) var(--gs-veil), transparent);--gs-surface-2:color-mix(in srgb, var(--gs-panel) var(--gs-veil), transparent)}.SD8qLW_drawer[data-gs-bg]:before{content:\"\";z-index:0;background-image:var(--gs-bg-image);filter:blur(var(--gs-bg-blur));pointer-events:none;background-position:50%;background-size:cover;position:absolute;inset:0;transform:scale(1.12)}.SD8qLW_drawer>:not(.SD8qLW_resizer){z-index:1;position:relative}.SD8qLW_drawer>.SD8qLW_header{z-index:24}.SD8qLW_drawer>.SD8qLW_tabs{z-index:23}.SD8qLW_drawer>.SD8qLW_compareBar{z-index:22}.SD8qLW_drawer>.SD8qLW_syncBar{z-index:20}.SD8qLW_overlayMax{gap:0;padding:0}.SD8qLW_overlayMax .SD8qLW_drawer{border-width:0 0 0 1px;border-radius:0;flex:auto;width:auto;min-width:0}@keyframes SD8qLW_gsFade{0%{opacity:0}}@keyframes SD8qLW_gsSlide{0%{opacity:0;transform:translate(24px)}}.SD8qLW_resizer{z-index:30;cursor:col-resize;touch-action:none;justify-content:center;align-items:center;width:10px;display:flex;position:absolute;top:0;bottom:0;left:0}.SD8qLW_resizer:after{content:\"\";background:var(--gs-fg-fainter);opacity:0;border-radius:999px;width:3px;height:32px;transition:opacity .14s,height .14s}.SD8qLW_resizer:hover:after{opacity:.6}.SD8qLW_resizerActive:after{opacity:.9;height:56px}.SD8qLW_paneDivider{z-index:5;cursor:col-resize;touch-action:none;flex:none;justify-content:center;align-items:center;width:7px;display:flex;position:relative}.SD8qLW_paneDivider:before{content:\"\";background:var(--gs-border-soft);width:1px;position:absolute;top:0;bottom:0;left:3px}.SD8qLW_paneDivider:after{content:\"\";background:var(--gs-fg-fainter);opacity:0;border-radius:999px;width:3px;height:28px;transition:opacity .14s,height .14s;position:relative}.SD8qLW_paneDivider:hover:after{opacity:.6}.SD8qLW_paneDividerActive:after{opacity:.9;height:48px}.SD8qLW_header{padding:10px var(--gs-gutter);border-bottom:1px solid var(--gs-border);background:var(--gs-surface-2);font-family:var(--dsw-font-ui,system-ui, sans-serif);font-size:var(--gs-t-ui);flex:none;justify-content:space-between;align-items:center;gap:12px;display:flex}.SD8qLW_headerLeft{flex-wrap:wrap;align-items:center;gap:10px;min-width:0;display:flex}.SD8qLW_headerBranch{border:1px solid var(--gs-accent-border);background:var(--gs-accent-bg);color:var(--gs-accent);border-radius:999px;align-items:center;gap:6px;padding:2px 10px;font-weight:600;display:inline-flex}.SD8qLW_refButton.SD8qLW_headerPicker{border-color:var(--gs-accent-border);background:var(--gs-accent-bg);max-width:420px;color:var(--gs-accent);font-weight:600}.SD8qLW_refButton.SD8qLW_headerPicker:hover{background:var(--gs-accent-bg);color:var(--gs-accent);border-color:var(--gs-accent)}.SD8qLW_elide{flex:0 auto;align-items:baseline;min-width:0;display:inline-flex}.SD8qLW_elideHead{white-space:nowrap;text-overflow:ellipsis;flex:0 999 auto;min-width:0;overflow:hidden}.SD8qLW_elideTail{white-space:nowrap;text-overflow:ellipsis;flex:0 auto;min-width:0;overflow:hidden}.SD8qLW_headerPathMain{color:var(--gs-fg-faint);font-size:var(--gs-t-meta)}.SD8qLW_headerPathMain .SD8qLW_elideTail{color:var(--gs-fg-dim)}.SD8qLW_headerViewRef{max-width:160px}.SD8qLW_headerView{background:var(--gs-neutral-bg);color:var(--gs-fg-muted);font-size:var(--gs-t-meta);font-variant-numeric:tabular-nums;border-radius:999px;flex:none;padding:1px 8px}.SD8qLW_headerDetached{background:var(--gs-warn-bg);color:var(--gs-warn);font-size:var(--gs-t-meta);letter-spacing:.02em;border-radius:999px;padding:1px 8px}.SD8qLW_headerTotals{font-variant-numeric:tabular-nums}.SD8qLW_headerTotalsAdd{color:var(--gs-add);font-weight:600}.SD8qLW_headerTotalsDel{color:var(--gs-del);font-weight:600}.SD8qLW_headerTotalsDim{color:var(--gs-fg-dim);font-size:var(--gs-t-dense)}.SD8qLW_headerRight{flex:none;gap:8px;display:flex;position:relative}.SD8qLW_theme{display:inline-flex;position:relative}.SD8qLW_refPop.SD8qLW_settingsPop{width:320px;max-width:calc(100vw - 32px);max-height:min(560px,100vh - 120px);top:calc(100% + 6px);left:auto;right:0}.SD8qLW_themeRail{min-height:0;font-family:var(--dsw-font-ui,system-ui, sans-serif);flex-direction:column;flex:1;gap:14px;padding:12px 14px 16px;display:flex;overflow-y:auto}.SD8qLW_themeGroup{flex-direction:column;gap:6px;display:flex}.SD8qLW_themeLabel{color:var(--gs-fg-faint);font-size:var(--gs-t-meta);letter-spacing:.04em;text-transform:uppercase;font-weight:600}.SD8qLW_themeRowSplit{justify-content:space-between;align-items:center;gap:8px;display:flex}.SD8qLW_segmented{gap:6px;display:flex}.SD8qLW_segment{box-sizing:border-box;border:1px solid var(--gs-border);border-radius:var(--gs-r-control);color:var(--gs-fg-dim);font-family:inherit;font-size:var(--gs-t-meta);cursor:pointer;background:0 0;flex-direction:column;flex:1 1 0;align-items:stretch;gap:4px;padding:4px;transition:background .12s,color .12s,border-color .12s;display:flex}.SD8qLW_segment:hover{background:var(--gs-raise);border-color:var(--gs-fg-fainter);color:var(--gs-fg)}.SD8qLW_segment:focus-visible{outline:2px solid var(--gs-accent);outline-offset:1px}.SD8qLW_segmentChip{border-radius:calc(var(--gs-r-control) - 4px);border:1px solid #80808073;height:20px}.SD8qLW_chipLight{background:#fff}.SD8qLW_chipDark{background:#0b0b0d}.SD8qLW_chipSystem{background:linear-gradient(105deg,#fff 0 50%,#0b0b0d 50% 100%)}.SD8qLW_paletteRow{box-sizing:border-box;border-radius:var(--gs-r-control);width:100%;color:var(--gs-fg-muted);font-family:inherit;font-size:var(--gs-t-dense);text-align:left;cursor:pointer;background:0 0;border:0;align-items:center;gap:8px;padding:5px 8px;display:flex}.SD8qLW_paletteRow:hover{background:var(--gs-raise);color:var(--gs-fg)}.SD8qLW_swatch{border:1px solid var(--gs-border);border-radius:3px;flex:none;width:26px;height:12px;display:inline-flex;overflow:hidden}.SD8qLW_swatch span{flex:1}.SD8qLW_scopeRow{gap:6px;display:flex}.SD8qLW_scopeHint{color:var(--gs-fg-faint);font-size:var(--gs-t-meta);overflow-wrap:anywhere}.SD8qLW_bgPreview{border:1px solid var(--gs-border);border-radius:var(--gs-r-control);background-color:var(--gs-bg);background-position:50%;background-size:cover;height:64px}.SD8qLW_bgEmpty{color:var(--gs-fg-faint);font-size:var(--gs-t-meta);justify-content:center;align-items:center;display:flex}.SD8qLW_sliderRow{font-size:var(--gs-t-meta);color:var(--gs-fg-dim);align-items:center;gap:8px;display:flex}.SD8qLW_sliderRow input{min-width:0;accent-color:var(--gs-accent);flex:1}.SD8qLW_sliderValue{text-align:right;font-variant-numeric:tabular-nums;flex:none;width:38px}.SD8qLW_cssArea{box-sizing:border-box;border:1px solid var(--gs-border);border-radius:var(--gs-r-control);background:var(--gs-bg);width:100%;min-height:96px;max-height:220px;color:var(--gs-fg);font-family:var(--dsw-font-mono,ui-monospace, Consolas, monospace);font-size:var(--gs-t-meta);resize:vertical;outline:none;padding:7px 8px}.SD8qLW_cssArea:focus{border-color:var(--gs-accent-border)}.SD8qLW_themeNote{color:var(--gs-warn);font-size:var(--gs-t-meta)}.SD8qLW_themeDirty{color:var(--gs-accent);font-size:var(--gs-t-meta)}.SD8qLW_wtCurrent{color:var(--gs-add);font-size:8px;line-height:1}.SD8qLW_commitCopy{margin-left:auto}.SD8qLW_commitPop{z-index:40;box-sizing:border-box;border:1px solid var(--gs-border);border-radius:var(--gs-r-surface);background:var(--gs-panel);width:min(380px,100vw - 24px);color:var(--gs-fg);box-shadow:0 16px 40px var(--gs-shadow);font-family:var(--dsw-font-ui,-apple-system, BlinkMacSystemFont, \"Segoe UI\", Helvetica, Arial, sans-serif);font-size:var(--gs-t-dense);flex-direction:column;gap:6px;padding:10px 12px 12px;display:flex;position:fixed;overflow:auto}.SD8qLW_commitPopTop{align-items:center;gap:8px;display:flex}.SD8qLW_commitPopTop .SD8qLW_commitWhen{margin-right:auto}.SD8qLW_commitPopSubject{color:var(--gs-fg);font-size:var(--gs-t-ui);white-space:pre-wrap;overflow-wrap:anywhere;user-select:text;font-weight:600;line-height:18px}.SD8qLW_commitPopBody{color:var(--gs-fg-muted);font-family:inherit;font-size:var(--gs-t-dense);white-space:pre-wrap;overflow-wrap:anywhere;user-select:text;margin:0;line-height:18px}.SD8qLW_tabs{padding:0 var(--gs-gutter);border-bottom:1px solid var(--gs-border-soft);background:var(--gs-surface-2);flex:none;gap:24px;font-family:inherit;display:flex}.SD8qLW_tab{font-family:inherit;font-size:var(--gs-t-ui);color:var(--gs-fg-dim);cursor:pointer;background:0 0;border:none;padding:8px 0 9px;font-weight:500;line-height:16px;position:relative}.SD8qLW_tab:after{content:\"\";background:0 0;border-radius:2px;height:2px;position:absolute;bottom:1px;left:0;right:0}.SD8qLW_tabActive{color:var(--gs-accent)}.SD8qLW_tabActive:after{background:var(--gs-accent)}.SD8qLW_compareBar{padding:8px var(--gs-gutter);border-bottom:1px solid var(--gs-border-soft);background:var(--gs-surface-2);color:var(--gs-fg-dim);font-family:var(--dsw-font-ui,system-ui, sans-serif);font-size:var(--gs-t-dense);flex-wrap:wrap;flex:none;align-items:center;gap:10px;display:flex}.SD8qLW_compareArrow{color:var(--gs-fg-faint)}.SD8qLW_refPicker{align-items:center;gap:6px;display:inline-flex;position:relative}.SD8qLW_refLabel{color:var(--gs-fg-faint);flex:none}.SD8qLW_refButton{justify-content:space-between;max-width:260px}.SD8qLW_refValue{min-width:0;overflow:hidden}.SD8qLW_refCaret{color:var(--gs-fg-faint);flex:none;font-size:9px}.SD8qLW_refPop{z-index:10;border:1px solid var(--gs-border);border-radius:var(--gs-r-control);background:var(--gs-panel);width:300px;max-width:80vw;box-shadow:0 12px 32px var(--gs-shadow);flex-direction:column;display:flex;position:absolute;top:calc(100% + 4px);left:0;overflow:hidden}.SD8qLW_menuPop{width:auto;min-width:100%;padding:4px;left:auto;right:0}.SD8qLW_refSearch{border:0;border-bottom:1px solid var(--gs-border-soft);background:var(--gs-bg);color:var(--gs-fg);font-family:inherit;font-size:var(--gs-t-dense);outline:none;flex:none;padding:7px 10px}.SD8qLW_refSearch::placeholder{color:var(--gs-fg-faint)}.SD8qLW_refList{flex:1;max-height:280px;padding:4px;overflow-y:auto}.SD8qLW_refGroup{color:var(--gs-fg-faint);font-size:var(--gs-t-meta);letter-spacing:.04em;text-transform:uppercase;padding:6px 8px 3px;font-weight:600}.SD8qLW_refRow{box-sizing:border-box;border-radius:var(--gs-r-control);width:100%;color:var(--gs-fg-muted);font-family:inherit;font-size:var(--gs-t-dense);text-align:left;cursor:pointer;background:0 0;border:0;align-items:center;gap:6px;padding:5px 8px;display:flex}.SD8qLW_refRow:hover{background:var(--gs-raise);color:var(--gs-fg)}.SD8qLW_refRowSpacer{flex:none;width:12px}.SD8qLW_refRowName{flex:auto;min-width:0;overflow:hidden}.SD8qLW_refEmpty{color:var(--gs-fg-faint);font-size:var(--gs-t-dense);padding:14px 10px}.SD8qLW_refFoot{border-top:1px solid var(--gs-border-soft);color:var(--gs-fg-faint);font-size:var(--gs-t-meta);flex:none;padding:5px 10px}.SD8qLW_commitsPane{box-sizing:border-box;width:26%;min-height:0;min-width:var(--gs-min-commits);background:var(--gs-surface);flex-direction:column;flex:0 auto;max-width:340px;display:flex}.SD8qLW_paneHead{box-sizing:border-box;min-height:37px;padding:6px var(--gs-gutter-pane);border-bottom:1px solid var(--gs-border-soft);color:var(--gs-fg-dim);font-family:var(--dsw-font-ui,system-ui, sans-serif);font-size:var(--gs-t-dense);flex:none;align-items:center;gap:8px;display:flex}.SD8qLW_paneTitle{font-weight:600}.SD8qLW_commitsSentinel{flex:none;height:1px}.SD8qLW_commitsFoot{color:var(--gs-fg-faint);font-family:var(--dsw-font-ui,system-ui, sans-serif);font-size:var(--gs-t-meta);text-align:center;flex:none;min-height:16px;padding:8px}.SD8qLW_commits{flex-direction:column;flex:1;min-height:0;padding:4px 6px 4px 0;display:flex;overflow-y:auto}.SD8qLW_commitLine{flex:none;align-items:stretch;height:48px;display:flex}.SD8qLW_graphCell{flex:none;display:block}.SD8qLW_commit{box-sizing:border-box;border-radius:var(--gs-r-surface);text-align:left;cursor:pointer;min-width:0;height:100%;font-family:inherit;font-size:var(--gs-t-dense);background:0 0;border:1px solid #0000;flex-direction:column;flex:auto;justify-content:center;gap:2px;margin:1px 0;padding:4px 8px;transition:background .12s,border-color .12s;display:flex;overflow:hidden}.SD8qLW_commit:hover{background:var(--gs-raise)}.SD8qLW_commitActive{border-color:var(--gs-accent-border);background:var(--gs-accent-bg)}.SD8qLW_commitTop{justify-content:space-between;align-items:center;gap:8px;display:flex}.SD8qLW_commitHash{color:var(--gs-info)}.SD8qLW_commitSubjectRow{align-items:baseline;gap:4px;min-width:0;display:flex}.SD8qLW_commitSubject{white-space:nowrap;text-overflow:ellipsis;min-width:0;color:var(--gs-fg-muted);flex:1;overflow:hidden}.SD8qLW_commitHasBody{color:var(--gs-fg-faint);letter-spacing:.04em;flex:none}.SD8qLW_commitRef{white-space:nowrap;text-overflow:ellipsis;border:1px solid var(--gs-accent-border);border-radius:var(--gs-r-pill);background:var(--gs-accent-bg);max-width:96px;color:var(--gs-accent);font-size:var(--gs-t-meta);flex:none;padding:0 5px;line-height:15px;overflow:hidden}.SD8qLW_commitWhen{color:var(--gs-fg-faint);flex:none}.SD8qLW_body{z-index:1;flex:1;min-height:0;display:flex}.SD8qLW_treeCol{box-sizing:border-box;width:28%;min-height:0;min-width:var(--gs-min-tree);background:var(--gs-surface);flex-direction:column;flex:0 auto;max-width:400px;display:flex}.SD8qLW_treeWrap{box-sizing:border-box;flex-direction:column;flex:1;min-width:0;min-height:0;display:flex}.SD8qLW_treeTools{box-sizing:border-box;min-height:37px;padding:6px var(--gs-gutter-pane);border-bottom:1px solid var(--gs-border-soft);flex-wrap:wrap;flex:none;justify-content:space-between;align-items:center;gap:6px;display:flex}.SD8qLW_treeActions{flex-wrap:wrap;align-items:center;gap:6px;display:flex}.SD8qLW_treeLead{align-items:center;gap:4px;min-width:0;display:flex}.SD8qLW_treeLabel{white-space:nowrap;color:var(--gs-fg-dim);font-size:var(--gs-t-dense);flex:none}.SD8qLW_tree{flex:1;min-height:0;margin:0;padding:6px 0 16px;list-style:none;overflow:auto}.SD8qLW_treeEmpty{min-height:0;color:var(--gs-fg-faint);font-size:var(--gs-t-ui);flex:1;justify-content:center;align-items:center;display:flex}.SD8qLW_treeSub{margin:0;padding:0;list-style:none;position:relative}.SD8qLW_treeSub:before{content:\"\";top:0;bottom:0;left:var(--gs-rail,0);background:var(--gs-border-soft);width:1px;position:absolute}.SD8qLW_treeDirLi{position:relative}.SD8qLW_treeDir{box-sizing:border-box;width:100%;color:var(--gs-fg-dim);font-family:inherit;font-size:var(--gs-t-dense);text-align:left;cursor:pointer;background:0 0;border:0;align-items:center;gap:6px;padding:4px 12px 4px 8px;font-weight:600;display:flex}.SD8qLW_treeDir:hover{background:var(--gs-panel);color:var(--gs-fg-muted)}.SD8qLW_treeDirActive{color:var(--gs-fg-muted)}.SD8qLW_chevron{width:10px;color:var(--gs-fg-fainter);font-size:var(--gs-t-meta);flex:none;transition:transform .12s;display:inline-block}.SD8qLW_chevronOpen{transform:rotate(90deg)}.SD8qLW_treeDirName{white-space:nowrap;text-overflow:ellipsis;flex:1;min-width:0;overflow:hidden}.SD8qLW_treeDirCount{background:var(--gs-neutral-bg);color:var(--gs-fg-dim);font-size:var(--gs-t-meta);border-radius:999px;flex:none;padding:0 6px;font-weight:600;line-height:16px}.SD8qLW_treeDirCounts{font-size:var(--gs-t-meta);font-variant-numeric:tabular-nums;flex:none}.SD8qLW_file{box-sizing:border-box;width:100%;color:var(--gs-fg-muted);font-family:inherit;font-size:var(--gs-t-dense);text-align:left;cursor:pointer;background:0 0;border:0;border-left:2px solid #0000;align-items:center;gap:8px;padding:5px 14px;display:flex}.SD8qLW_file:hover{background:var(--gs-panel)}.SD8qLW_fileActive{background:var(--gs-panel);border-left-color:var(--gs-accent);color:var(--gs-fg)}.SD8qLW_fileStatus{width:18px;height:18px;font-weight:700;font-size:var(--gs-t-meta);border-radius:5px;flex:none;justify-content:center;align-items:center;line-height:1;display:inline-flex}.SD8qLW_stAdded,.SD8qLW_stUntracked{color:var(--gs-add);background:var(--gs-add-bg)}.SD8qLW_stDeleted{color:var(--gs-del);background:var(--gs-del-bg)}.SD8qLW_stModified{color:var(--gs-warn);background:var(--gs-warn-bg)}.SD8qLW_stRenamed{color:var(--gs-info);background:var(--gs-info-bg)}.SD8qLW_filePath{white-space:nowrap;text-overflow:ellipsis;flex:1;min-width:0;overflow:hidden}.SD8qLW_fileBinary{background:var(--gs-neutral-bg);color:var(--gs-fg-dim);letter-spacing:.06em;border-radius:4px;flex:none;padding:0 5px;font-size:9px;font-weight:700}.SD8qLW_fileCounts{font-size:var(--gs-t-meta);font-variant-numeric:tabular-nums;flex:none}.SD8qLW_fileCountAdd{color:var(--gs-add)}.SD8qLW_fileCountDel{color:var(--gs-del)}.SD8qLW_diffPane{min-width:var(--gs-min-diff);background:var(--gs-surface);flex:1 1 0;overflow:auto}.SD8qLW_diffPre{width:max-content;min-width:100%;font-family:ui-monospace,SFMono-Regular,SF Mono,Menlo,Consolas,Liberation Mono,monospace;font-size:var(--gs-t-dense);font-variant-ligatures:none;font-feature-settings:\"liga\" 0, \"calt\" 0;tab-size:4;flex-direction:column;margin:0;padding:8px 0 16px;font-weight:400;line-height:20px;display:flex}.SD8qLW_renameLine{border-bottom:1px solid var(--gs-border-soft);color:var(--gs-info);font-size:var(--gs-t-dense);padding:8px 16px}.SD8qLW_renameLine code{color:var(--gs-fg)}.SD8qLW_line{white-space:nowrap;align-items:stretch;width:max-content;min-width:100%;min-height:20px;line-height:20px;display:flex}.SD8qLW_lnOld,.SD8qLW_lnNew{box-sizing:border-box;text-align:right;width:3.2em;color:var(--gs-fg-fainter);user-select:none;font-variant-numeric:tabular-nums;flex:none;padding:0 8px}.SD8qLW_line:hover .SD8qLW_lnOld,.SD8qLW_line:hover .SD8qLW_lnNew{color:var(--gs-fg-dim)}.SD8qLW_gutter{text-align:center;user-select:none;flex:none;width:1.4em}.SD8qLW_code{white-space:pre;flex:1 0 auto;padding:0 16px 0 10px}.SD8qLW_lineAdd{background:var(--gs-add-line)}.SD8qLW_lineDel{background:var(--gs-del-line)}.SD8qLW_lineAdd .SD8qLW_lnOld,.SD8qLW_lineAdd .SD8qLW_lnNew,.SD8qLW_lineAdd .SD8qLW_gutter{background:var(--gs-add-num,var(--gs-add-line))}.SD8qLW_lineDel .SD8qLW_lnOld,.SD8qLW_lineDel .SD8qLW_lnNew,.SD8qLW_lineDel .SD8qLW_gutter{background:var(--gs-del-num,var(--gs-del-line))}.SD8qLW_lineContext{color:var(--gs-fg-muted)}.SD8qLW_lineHunk{background:var(--gs-hunk)}.SD8qLW_lineHunk .SD8qLW_code{color:var(--gs-fg-dim)}.SD8qLW_lineHunk .SD8qLW_lnOld,.SD8qLW_lineHunk .SD8qLW_lnNew,.SD8qLW_lineHunk .SD8qLW_gutter{background:var(--gs-hunk-num,var(--gs-hunk))}.SD8qLW_signAdd{color:var(--gs-add)}.SD8qLW_signDel{color:var(--gs-del)}.SD8qLW_wordAdd{background:var(--gs-add-word);border-radius:2px}.SD8qLW_wordDel{background:var(--gs-del-word);border-radius:2px}.SD8qLW_empty{color:var(--gs-fg-faint);font-size:var(--gs-t-ui);padding:32px 24px}.SD8qLW_syncBar{padding:8px var(--gs-gutter);border-bottom:1px solid var(--gs-border-soft);background:var(--gs-surface-2);font-size:var(--gs-t-dense);color:var(--gs-fg-dim);flex-wrap:wrap;flex:none;align-items:center;gap:8px;display:flex}.SD8qLW_syncUpstream{white-space:nowrap;text-overflow:ellipsis;max-width:260px;color:var(--gs-fg-muted);font-variant-numeric:tabular-nums;overflow:hidden}.SD8qLW_syncLevel{color:var(--gs-fg-faint)}.SD8qLW_syncSpacer{flex:auto}.SD8qLW_btn:disabled,.SD8qLW_treeIcon:disabled,.SD8qLW_miniBtn:disabled,.SD8qLW_refButton:disabled{opacity:.45;cursor:default}.SD8qLW_btn[data-quiet]:disabled,.SD8qLW_refButton[data-quiet]:disabled{opacity:1}.SD8qLW_opBanner{padding:8px var(--gs-gutter);border-bottom:1px solid var(--gs-border-soft);font-size:var(--gs-t-dense);white-space:pre-wrap;overflow-wrap:anywhere;user-select:text;flex:none;line-height:18px}.SD8qLW_opBannerOk{background:var(--gs-add-bg);color:var(--gs-add)}.SD8qLW_opBannerBad{background:var(--gs-del-bg);color:var(--gs-del)}.SD8qLW_fileLi{align-items:stretch;display:flex}.SD8qLW_fileLi .SD8qLW_file{flex:auto;min-width:0}.SD8qLW_treeRow{align-items:stretch;display:flex}.SD8qLW_treeRow .SD8qLW_treeDir{flex:auto;min-width:0}.SD8qLW_checkBox{cursor:pointer;background:0 0;border:0;flex:none;justify-content:center;align-items:center;width:22px;padding:0;display:flex}.SD8qLW_checkMark{box-sizing:border-box;border:1px solid var(--gs-fg-faint);background:var(--gs-bg);color:#0000;border-radius:3px;justify-content:center;align-items:center;width:14px;height:14px;font-size:10px;font-weight:700;line-height:1;transition:background .12s,border-color .12s;display:flex}.SD8qLW_checkBox:hover .SD8qLW_checkMark{border-color:var(--gs-accent)}.SD8qLW_checkBox:focus-visible{outline:none}.SD8qLW_checkBox:focus-visible .SD8qLW_checkMark{outline:2px solid var(--gs-accent);outline-offset:1px}.SD8qLW_checkMarkOn,.SD8qLW_checkMarkPartial{border-color:var(--gs-accent);background:var(--gs-accent);color:var(--gs-on-accent)}.SD8qLW_commitBox{padding:8px var(--gs-gutter-pane) 10px;border-top:1px solid var(--gs-border);background:var(--gs-surface-2);flex-direction:column;flex:none;gap:6px;display:flex}.SD8qLW_commitMessage{box-sizing:border-box;border:1px solid var(--gs-border);border-radius:var(--gs-r-control);background:var(--gs-bg);width:100%;min-height:46px;max-height:160px;color:var(--gs-fg);font-family:inherit;font-size:var(--gs-t-dense);resize:vertical;outline:none;padding:6px 8px;line-height:18px}.SD8qLW_commitMessage:focus{border-color:var(--gs-accent-border)}.SD8qLW_commitMessage::placeholder{color:var(--gs-fg-faint)}.SD8qLW_commitRow{align-items:center;gap:8px;display:flex}.SD8qLW_commitAmend{color:var(--gs-fg-dim);font-size:var(--gs-t-meta);cursor:pointer;align-items:center;gap:4px;display:inline-flex}.SD8qLW_commitAmend input{accent-color:var(--gs-accent);cursor:pointer}.SD8qLW_commitStaged{color:var(--gs-fg-faint);font-size:var(--gs-t-meta);margin-left:auto}.SD8qLW_commitLead{color:var(--gs-fg-faint);font-size:var(--gs-t-meta);margin:0;line-height:15px}.SD8qLW_commitBtn{box-sizing:border-box;min-width:92px;height:var(--gs-h-control);border-radius:var(--gs-r-control);background:var(--gs-accent);color:var(--gs-on-accent);font-family:inherit;font-size:var(--gs-t-ui);white-space:nowrap;cursor:pointer;border:1px solid #0000;flex:none;justify-content:center;align-items:center;padding:0 16px;font-weight:600;line-height:1;transition:filter .12s,opacity .12s;display:inline-flex}.SD8qLW_commitBtn:hover:not(:disabled){filter:brightness(1.1)}.SD8qLW_commitBtn:focus-visible{outline:2px solid var(--gs-accent);outline-offset:2px}.SD8qLW_commitBtn:disabled{background:var(--gs-raise);color:var(--gs-fg-faint);border-color:var(--gs-border);cursor:default}.SD8qLW_btn,.SD8qLW_miniBtn,.SD8qLW_treeIcon,.SD8qLW_commitCopy,.SD8qLW_scopeBtn,.SD8qLW_refButton{box-sizing:border-box;height:var(--gs-h-control);padding:var(--gs-pad-control);border:1px solid var(--gs-border);border-radius:var(--gs-r-control);color:var(--gs-fg-dim);font-family:inherit;font-size:var(--gs-t-ui);white-space:nowrap;cursor:pointer;background:0 0;flex:none;justify-content:center;align-items:center;gap:6px;line-height:1;transition:background .12s,color .12s,border-color .12s;display:inline-flex}.SD8qLW_btn:hover,.SD8qLW_miniBtn:hover,.SD8qLW_treeIcon:hover,.SD8qLW_commitCopy:hover,.SD8qLW_scopeBtn:hover,.SD8qLW_refButton:hover{background:var(--gs-raise);color:var(--gs-fg);border-color:var(--gs-fg-fainter)}.SD8qLW_btn:focus-visible,.SD8qLW_miniBtn:focus-visible,.SD8qLW_treeIcon:focus-visible,.SD8qLW_commitCopy:focus-visible,.SD8qLW_scopeBtn:focus-visible,.SD8qLW_refButton:focus-visible{outline:2px solid var(--gs-accent);outline-offset:1px}.SD8qLW_btn,.SD8qLW_refButton{border-radius:var(--gs-r-pill)}.SD8qLW_treeIcon,.SD8qLW_commitCopy{height:var(--gs-h-compact);padding:var(--gs-pad-compact);font-size:var(--gs-t-dense)}.SD8qLW_treeIcon{width:var(--gs-h-compact);padding:0}.SD8qLW_treeIconGlyph{font-size:var(--gs-t-meta);line-height:1;display:block}.SD8qLW_treeIconDown{transform:rotate(90deg)}.SD8qLW_scopeBtn{flex:1 1 0}.SD8qLW_scopeBtnActive,.SD8qLW_miniBtnPrimary,.SD8qLW_segmentActive,.SD8qLW_refRowActive,.SD8qLW_paletteRowActive{border-color:var(--gs-accent-border);background:var(--gs-accent-bg);color:var(--gs-accent);font-weight:600}.SD8qLW_btnIcon{width:var(--gs-h-control);padding:0}.SD8qLW_btnIcon svg{display:block}.SD8qLW_btnClose:hover:not(:disabled){border-color:var(--gs-del);background:var(--gs-del-bg);color:var(--gs-del)}.SD8qLW_btnBehind{border-color:var(--gs-warn);background:var(--gs-warn-bg);color:var(--gs-warn);font-weight:600}.SD8qLW_btnBehind:hover:not(:disabled){background:var(--gs-warn-bg);color:var(--gs-warn);border-color:var(--gs-warn)}.SD8qLW_btnAhead{border-color:var(--gs-add);background:var(--gs-add-bg);color:var(--gs-add);font-weight:600}.SD8qLW_btnAhead:hover:not(:disabled){background:var(--gs-add-bg);color:var(--gs-add);border-color:var(--gs-add)}.SD8qLW_btnPrimary{border-color:var(--gs-accent);background:var(--gs-accent);color:var(--gs-on-accent,#fff);font-weight:600}.SD8qLW_btnPrimary:hover:not(:disabled){background:var(--gs-accent);color:var(--gs-on-accent,#fff);border-color:var(--gs-accent)}.SD8qLW_btnCount{font-variant-numeric:tabular-nums;opacity:.85;border-left:1px solid;margin-left:1px;padding-left:6px}.SD8qLW_pullGroup{flex:none;align-items:center;display:inline-flex}.SD8qLW_pullGroup>.SD8qLW_refPicker>.SD8qLW_refButton{border-right-color:#0000;border-top-right-radius:0;border-bottom-right-radius:0}.SD8qLW_pullGroup>.SD8qLW_btn{border-top-left-radius:0;border-bottom-left-radius:0;margin-left:-1px}.SD8qLW_drawer{scrollbar-width:thin;scrollbar-color:var(--gs-border) transparent}.SD8qLW_drawer ::-webkit-scrollbar{width:10px;height:10px}.SD8qLW_drawer ::-webkit-scrollbar-track,.SD8qLW_drawer ::-webkit-scrollbar-corner{background:0 0}.SD8qLW_drawer ::-webkit-scrollbar-thumb{background:var(--gs-border);background-clip:padding-box;border:3px solid #0000;border-radius:999px}.SD8qLW_drawer ::-webkit-scrollbar-thumb:hover{background:var(--gs-fg-faint);background-clip:padding-box}.SD8qLW_commitRefMore{border-radius:var(--gs-r-pill);background:var(--gs-neutral-bg);color:var(--gs-fg-dim);font-size:var(--gs-t-meta);cursor:default;flex:none;padding:0 5px;line-height:15px}";
|
|
12207
|
+
const css = ".SD8qLW_card{border:1px solid var(--dsw-alias-border-l2);min-height:28px;color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;cursor:pointer;background:0 0;border-radius:8px;align-items:center;gap:7px;padding:3px 10px;font-size:12px;line-height:18px;transition:background .12s,border-color .12s;display:inline-flex}.SD8qLW_card:hover,.SD8qLW_card:focus-visible{background:var(--dsw-alias-interactive-bg-hover);border-color:var(--dsw-alias-border-l3)}.SD8qLW_cardBranch{max-width:220px;color:var(--dsw-alias-label-primary);align-items:center;gap:4px;font-weight:550;display:inline-flex;overflow:hidden}.SD8qLW_cardBranchName{min-width:0;overflow:hidden}.SD8qLW_cardGlyph{opacity:.7;flex:none}.SD8qLW_cardDetached{background:var(--dsw-alias-state-warning-bg,#bb800926);color:var(--dsw-alias-state-warning-primary,#d29922);letter-spacing:.02em;border-radius:999px;padding:0 6px;font-size:10px;line-height:16px}.SD8qLW_cardWt{background:var(--dsw-alias-state-business-bg,#388bfd29);max-width:150px;color:var(--dsw-alias-state-business-primary,#58a6ff);letter-spacing:.02em;white-space:nowrap;text-overflow:ellipsis;border-radius:999px;align-items:center;gap:3px;padding:0 6px;font-size:10px;line-height:16px;display:inline-flex;overflow:hidden}.SD8qLW_cardAhead,.SD8qLW_cardBehind{color:var(--dsw-alias-label-tertiary);font-size:11px}.SD8qLW_cardAhead{color:var(--dsw-alias-state-success-primary)}.SD8qLW_cardBehind{color:var(--dsw-alias-state-attention-primary,#d29922)}.SD8qLW_cardSep{background:var(--dsw-alias-border-l2);width:1px;height:14px}.SD8qLW_cardAdded{color:var(--dsw-alias-state-success-primary)}.SD8qLW_cardDeleted{color:var(--dsw-alias-state-error-primary)}.SD8qLW_cardFiles{color:var(--dsw-alias-label-tertiary)}.SD8qLW_overlay[data-gs-theme=github-dark]{--gs-bg:#0d1117;--gs-panel:#161b22;--gs-raise:#21262d;--gs-border:#30363d;--gs-border-soft:#21262d;--gs-fg:#e6edf3;--gs-fg-muted:#c9d1d9;--gs-fg-dim:#8b949e;--gs-fg-faint:#6e7681;--gs-fg-fainter:#484f58;--gs-accent:#58a6ff;--gs-accent-bg:#388bfd24;--gs-accent-border:#388bfd8c;--gs-add:#3fb950;--gs-del:#f85149;--gs-warn:#d29922;--gs-info:#79c0ff;--gs-add-bg:#2ea04329;--gs-del-bg:#f8514929;--gs-warn-bg:#bb800929;--gs-info-bg:#388bfd29;--gs-add-line:#12261e;--gs-del-line:#25181c;--gs-add-word:#1a4a29;--gs-del-word:#6b2b2b;--gs-add-num:#1c4428;--gs-del-num:#542426;--gs-hunk:#111d2e;--gs-hunk-num:#0c2d6b;--gs-neutral-bg:#6e76812e;--gs-backdrop:#01040999;--gs-shadow:#0104098c}.SD8qLW_overlay[data-gs-theme=github-light]{--gs-bg:#fff;--gs-panel:#f6f8fa;--gs-raise:#eaeef2;--gs-border:#d0d7de;--gs-border-soft:#eaeef2;--gs-fg:#1f2328;--gs-fg-muted:#32383f;--gs-fg-dim:#656d76;--gs-fg-faint:#8c959f;--gs-fg-fainter:#afb8c1;--gs-accent:#0969da;--gs-accent-bg:#0969da1a;--gs-accent-border:#0969da73;--gs-add:#1a7f37;--gs-del:#cf222e;--gs-warn:#9a6700;--gs-info:#0550ae;--gs-add-bg:#1a7f371f;--gs-del-bg:#cf222e1f;--gs-warn-bg:#9a67001f;--gs-info-bg:#0969da1f;--gs-add-line:#dafbe1;--gs-del-line:#ffebe9;--gs-add-word:#aceebb;--gs-del-word:#ffcecb;--gs-add-num:#aceebb;--gs-del-num:#ffcecb;--gs-hunk:#ddf4ff;--gs-hunk-num:#b6e3ff;--gs-neutral-bg:#6e76811f;--gs-backdrop:#1f232847;--gs-shadow:#1f232838}.SD8qLW_overlay[data-gs-theme=idea-dark]{--gs-bg:#1e1f22;--gs-panel:#2b2d30;--gs-raise:#393b40;--gs-border:#393b40;--gs-border-soft:#2b2d30;--gs-fg:#dfe1e5;--gs-fg-muted:#ced0d6;--gs-fg-dim:#9da0a8;--gs-fg-faint:#6f737a;--gs-fg-fainter:#4e5157;--gs-accent:#548af7;--gs-accent-bg:#548af729;--gs-accent-border:#548af78c;--gs-add:#5fad65;--gs-del:#e26e6e;--gs-warn:#f0a732;--gs-info:#3592c4;--gs-add-bg:#5fad6529;--gs-del-bg:#e26e6e29;--gs-warn-bg:#f0a73229;--gs-info-bg:#3592c429;--gs-add-line:#293c2e;--gs-del-line:#3d2b2b;--gs-add-word:#3d6640;--gs-del-word:#6b3838;--gs-hunk:#548af71a;--gs-neutral-bg:#9da0a82e;--gs-backdrop:#10111399;--gs-shadow:#00000080}.SD8qLW_overlay[data-gs-theme=idea-light]{--gs-bg:#fff;--gs-panel:#f7f8fa;--gs-raise:#ebecf0;--gs-border:#d3d5db;--gs-border-soft:#ebecf0;--gs-fg:#1e1f22;--gs-fg-muted:#3c3f44;--gs-fg-dim:#6c707e;--gs-fg-faint:#818594;--gs-fg-fainter:#a8adbd;--gs-accent:#3574f0;--gs-accent-bg:#3574f01a;--gs-accent-border:#3574f073;--gs-add:#398a4a;--gs-del:#cc4a4a;--gs-warn:#a8760b;--gs-info:#2b7fb8;--gs-add-bg:#398a4a1f;--gs-del-bg:#cc4a4a1f;--gs-warn-bg:#a8760b1f;--gs-info-bg:#2b7fb81f;--gs-add-line:#e6f5e9;--gs-del-line:#fbe9ea;--gs-add-word:#c2e5c9;--gs-del-word:#f5c9cc;--gs-hunk:#3574f012;--gs-neutral-bg:#6c707e1f;--gs-backdrop:#1e1f2242;--gs-shadow:#1e1f222e}.SD8qLW_overlay[data-gs-theme=vscode-dark]{--gs-bg:#1e1e1e;--gs-panel:#252526;--gs-raise:#2d2d30;--gs-border:#3e3e42;--gs-border-soft:#2d2d30;--gs-fg:#d4d4d4;--gs-fg-muted:#ccc;--gs-fg-dim:#9d9d9d;--gs-fg-faint:gray;--gs-fg-fainter:#5a5a5a;--gs-accent:#0098ff;--gs-accent-bg:#007acc3d;--gs-accent-border:#0098ff99;--gs-add:#89d185;--gs-del:#f14c4c;--gs-warn:#cca700;--gs-info:#75beff;--gs-add-bg:#89d1852e;--gs-del-bg:#f14c4c2e;--gs-warn-bg:#cca7002e;--gs-info-bg:#75beff2e;--gs-add-line:#9bb9552e;--gs-del-line:#ff000029;--gs-add-word:#9bb95566;--gs-del-word:#ff00005c;--gs-hunk:#007acc24;--gs-neutral-bg:#bebebe24;--gs-backdrop:#0000008c;--gs-shadow:#0009}.SD8qLW_overlay[data-gs-theme=vscode-light]{--gs-bg:#fff;--gs-panel:#f3f3f3;--gs-raise:#e8e8e8;--gs-border:#cecece;--gs-border-soft:#e8e8e8;--gs-fg:#1e1e1e;--gs-fg-muted:#333;--gs-fg-dim:#616161;--gs-fg-faint:#767676;--gs-fg-fainter:#a0a0a0;--gs-accent:#005fb8;--gs-accent-bg:#005fb81a;--gs-accent-border:#005fb873;--gs-add:#10793f;--gs-del:#b5200d;--gs-warn:#855b00;--gs-info:#005fb8;--gs-add-bg:#10793f1f;--gs-del-bg:#b5200d1f;--gs-warn-bg:#855b001f;--gs-info-bg:#005fb81f;--gs-add-line:#9bb95538;--gs-del-line:#ff000021;--gs-add-word:#9bb95573;--gs-del-word:#ff00004d;--gs-hunk:#005fb812;--gs-neutral-bg:#6161611f;--gs-backdrop:#00000040;--gs-shadow:#0000002e}.SD8qLW_overlay[data-gs-theme=cyberpunk-dark]{--gs-bg:#0b0417;--gs-panel:#14082a;--gs-raise:#1f0d3d;--gs-border:#3a1f6b;--gs-border-soft:#26124a;--gs-fg:#f0e6ff;--gs-fg-muted:#d5c2f5;--gs-fg-dim:#a98fd6;--gs-fg-faint:#7d64a8;--gs-fg-fainter:#56427a;--gs-accent:#00f0ff;--gs-on-accent:#0b0417;--gs-accent-bg:#00f0ff24;--gs-accent-border:#00f0ff80;--gs-add:#39ff88;--gs-del:#ff2e88;--gs-warn:#fc0;--gs-info:#00f0ff;--gs-add-bg:#39ff8824;--gs-del-bg:#ff2e8824;--gs-warn-bg:#ffcc0024;--gs-info-bg:#00f0ff24;--gs-add-line:#39ff881f;--gs-del-line:#ff2e881f;--gs-add-word:#39ff8857;--gs-del-word:#ff2e8857;--gs-hunk:#00f0ff1a;--gs-neutral-bg:#a98fd629;--gs-backdrop:#06020ead;--gs-shadow:#00f0ff24}.SD8qLW_overlay[data-gs-theme=cyberpunk-light]{--gs-bg:#fdfbff;--gs-panel:#f4eeff;--gs-raise:#e9dfff;--gs-border:#c9b3f0;--gs-border-soft:#e0d2f7;--gs-fg:#1a0b2e;--gs-fg-muted:#33195c;--gs-fg-dim:#6b4ba0;--gs-fg-faint:#8f74bd;--gs-fg-fainter:#b9a5d6;--gs-accent:#00a6b8;--gs-accent-bg:#00a6b81f;--gs-accent-border:#00a6b880;--gs-add:#00875a;--gs-del:#d6006e;--gs-warn:#b37a00;--gs-info:#00a6b8;--gs-add-bg:#00875a1f;--gs-del-bg:#d6006e1f;--gs-warn-bg:#b37a001f;--gs-info-bg:#00a6b81f;--gs-add-line:#00c88224;--gs-del-line:#ff2e881f;--gs-add-word:#00c88257;--gs-del-word:#ff2e884d;--gs-hunk:#00a6b814;--gs-neutral-bg:#6b4ba01f;--gs-backdrop:#1a0b2e47;--gs-shadow:#1a0b2e29}.SD8qLW_overlay[data-gs-theme=one-dark]{--gs-bg:#282c34;--gs-panel:#21252b;--gs-raise:#2c313a;--gs-border:#3e4451;--gs-border-soft:#2c313a;--gs-fg:#abb2bf;--gs-fg-muted:#b6bdca;--gs-fg-dim:#7f848e;--gs-fg-faint:#636d83;--gs-fg-fainter:#4b5263;--gs-accent:#61afef;--gs-accent-bg:#61afef24;--gs-accent-border:#61afef8c;--gs-add:#98c379;--gs-del:#e06c75;--gs-warn:#e5c07b;--gs-info:#56b6c2;--gs-add-bg:#98c37929;--gs-del-bg:#e06c7529;--gs-warn-bg:#e5c07b29;--gs-info-bg:#56b6c229;--gs-add-line:#98c37924;--gs-del-line:#e06c7524;--gs-add-word:#98c37961;--gs-del-word:#e06c7561;--gs-hunk:#61afef1a;--gs-neutral-bg:#7f848e2e;--gs-backdrop:#171a2199;--gs-shadow:#0f11158c}.SD8qLW_overlay[data-gs-theme=one-light]{--gs-bg:#fafafa;--gs-panel:#f0f0f0;--gs-raise:#e5e5e6;--gs-border:#d4d4d4;--gs-border-soft:#e5e5e6;--gs-fg:#383a42;--gs-fg-muted:#4a4c53;--gs-fg-dim:#696c77;--gs-fg-faint:#909196;--gs-fg-fainter:#b8b9bd;--gs-accent:#4078f2;--gs-accent-bg:#4078f21a;--gs-accent-border:#4078f273;--gs-add:#50a14f;--gs-del:#e45649;--gs-warn:#c18401;--gs-info:#0184bc;--gs-add-bg:#50a14f1f;--gs-del-bg:#e456491f;--gs-warn-bg:#c184011f;--gs-info-bg:#0184bc1f;--gs-add-line:#50a14f24;--gs-del-line:#e4564921;--gs-add-word:#50a14f57;--gs-del-word:#e4564952;--gs-hunk:#4078f212;--gs-neutral-bg:#696c771f;--gs-backdrop:#383a4242;--gs-shadow:#383a422e}.SD8qLW_overlay[data-gs-theme=solarized-dark]{--gs-bg:#002b36;--gs-panel:#073642;--gs-raise:#0a4553;--gs-border:#0f5666;--gs-border-soft:#073642;--gs-fg:#93a1a1;--gs-fg-muted:#839496;--gs-fg-dim:#657b83;--gs-fg-faint:#586e75;--gs-fg-fainter:#45636b;--gs-accent:#268bd2;--gs-accent-bg:#268bd229;--gs-accent-border:#268bd28c;--gs-add:#859900;--gs-del:#dc322f;--gs-warn:#b58900;--gs-info:#2aa198;--gs-add-bg:#85990033;--gs-del-bg:#dc322f2e;--gs-warn-bg:#b589002e;--gs-info-bg:#2aa1982e;--gs-add-line:#85990029;--gs-del-line:#dc322f26;--gs-add-word:#85990066;--gs-del-word:#dc322f61;--gs-hunk:#268bd21f;--gs-neutral-bg:#657b8333;--gs-backdrop:#00141a9e;--gs-shadow:#00141a8c}.SD8qLW_overlay[data-gs-theme=solarized-light]{--gs-bg:#fdf6e3;--gs-panel:#eee8d5;--gs-raise:#e3ddca;--gs-border:#d5cfbb;--gs-border-soft:#eee8d5;--gs-fg:#586e75;--gs-fg-muted:#657b83;--gs-fg-dim:#839496;--gs-fg-faint:#93a1a1;--gs-fg-fainter:#b5b0a0;--gs-accent:#268bd2;--gs-accent-bg:#268bd21f;--gs-accent-border:#268bd273;--gs-add:#6c7c00;--gs-del:#cb2825;--gs-warn:#9a7400;--gs-info:#21867f;--gs-add-bg:#8599002e;--gs-del-bg:#dc322f24;--gs-warn-bg:#b5890029;--gs-info-bg:#2aa19824;--gs-add-line:#85990029;--gs-del-line:#dc322f1f;--gs-add-word:#85990061;--gs-del-word:#dc322f4d;--gs-hunk:#268bd214;--gs-neutral-bg:#586e7524;--gs-backdrop:#586e7542;--gs-shadow:#586e7533}.SD8qLW_overlay[data-gs-theme=nord-dark]{--gs-bg:#2e3440;--gs-panel:#3b4252;--gs-raise:#434c5e;--gs-border:#4c566a;--gs-border-soft:#3b4252;--gs-fg:#eceff4;--gs-fg-muted:#d8dee9;--gs-fg-dim:#9aa5b8;--gs-fg-faint:#7b88a1;--gs-fg-fainter:#5d6a82;--gs-accent:#88c0d0;--gs-on-accent:#2e3440;--gs-accent-bg:#88c0d024;--gs-accent-border:#88c0d08c;--gs-add:#a3be8c;--gs-del:#bf616a;--gs-warn:#ebcb8b;--gs-info:#81a1c1;--gs-add-bg:#a3be8c29;--gs-del-bg:#bf616a2e;--gs-warn-bg:#ebcb8b29;--gs-info-bg:#81a1c129;--gs-add-line:#a3be8c24;--gs-del-line:#bf616a26;--gs-add-word:#a3be8c61;--gs-del-word:#bf616a66;--gs-hunk:#88c0d01a;--gs-neutral-bg:#9aa5b829;--gs-backdrop:#1d232d9e;--gs-shadow:#1419218c}.SD8qLW_overlay[data-gs-theme=nord-light]{--gs-bg:#eceff4;--gs-panel:#e5e9f0;--gs-raise:#d8dee9;--gs-border:#c3cbd8;--gs-border-soft:#dfe4ec;--gs-fg:#2e3440;--gs-fg-muted:#3b4252;--gs-fg-dim:#4c566a;--gs-fg-faint:#6b7689;--gs-fg-fainter:#9aa4b5;--gs-accent:#5e81ac;--gs-accent-bg:#5e81ac1f;--gs-accent-border:#5e81ac80;--gs-add:#5e7d47;--gs-del:#a5424c;--gs-warn:#97701f;--gs-info:#4c7a8c;--gs-add-bg:#5e7d4724;--gs-del-bg:#a5424c21;--gs-warn-bg:#97701f24;--gs-info-bg:#4c7a8c21;--gs-add-line:#5e7d4724;--gs-del-line:#a5424c1f;--gs-add-word:#5e7d4757;--gs-del-word:#a5424c52;--gs-hunk:#5e81ac17;--gs-neutral-bg:#4c566a21;--gs-backdrop:#2e344042;--gs-shadow:#2e34402e}.SD8qLW_overlay{--gs-inset:14px;--gs-h-control:28px;--gs-h-compact:24px;--gs-pad-control:0 12px;--gs-pad-compact:0 10px;--gs-on-accent:#fff;--gs-graph-0:#58a6ff;--gs-graph-1:#3fb950;--gs-graph-2:#d29922;--gs-graph-3:#bc8cff;--gs-graph-4:#f778ba;--gs-graph-5:#39c5cf;--gs-r-pill:999px;--gs-r-control:8px;--gs-r-surface:12px;--gs-r-drawer:14px;--gs-t-meta:11px;--gs-t-dense:12px;--gs-t-ui:13px;--gs-gutter:16px;--gs-gutter-pane:12px;z-index:1000;padding:var(--gs-inset);box-sizing:border-box;background:var(--gs-backdrop);justify-content:flex-end;align-items:stretch;gap:12px;animation:.16s ease-out SD8qLW_gsFade;display:flex;position:fixed;inset:0}.SD8qLW_drawer{box-sizing:border-box;width:min(1600px,94vw);height:100%;color:var(--gs-fg);background:var(--gs-bg);border:1px solid var(--gs-border);border-radius:var(--gs-r-drawer);box-shadow:0 24px 64px var(--gs-shadow);font-family:var(--dsw-font-ui,-apple-system, BlinkMacSystemFont, \"Segoe UI\", Helvetica, Arial, sans-serif);--gs-surface:var(--gs-bg);--gs-surface-2:var(--gs-panel);flex-direction:column;animation:.18s cubic-bezier(.22,1,.36,1) SD8qLW_gsSlide;display:flex;position:relative;overflow:hidden}.SD8qLW_drawer[data-gs-bg]{--gs-surface:color-mix(in srgb, var(--gs-bg) var(--gs-veil), transparent);--gs-surface-2:color-mix(in srgb, var(--gs-panel) var(--gs-veil), transparent)}.SD8qLW_drawer[data-gs-bg]:before{content:\"\";z-index:0;background-image:var(--gs-bg-image);filter:blur(var(--gs-bg-blur));pointer-events:none;background-position:50%;background-size:cover;position:absolute;inset:0;transform:scale(1.12)}.SD8qLW_drawer>:not(.SD8qLW_resizer){z-index:1;position:relative}.SD8qLW_drawer>.SD8qLW_header{z-index:24}.SD8qLW_drawer>.SD8qLW_tabs{z-index:23}.SD8qLW_drawer>.SD8qLW_compareBar{z-index:22}.SD8qLW_drawer>.SD8qLW_syncBar{z-index:20}.SD8qLW_drawer>.SD8qLW_confirmScrim{z-index:40;position:absolute;inset:0}.SD8qLW_overlayMax{gap:0;padding:0}.SD8qLW_overlayMax .SD8qLW_drawer{border-width:0 0 0 1px;border-radius:0;flex:auto;width:auto;min-width:0}@keyframes SD8qLW_gsFade{0%{opacity:0}}@keyframes SD8qLW_gsSlide{0%{opacity:0;transform:translate(24px)}}.SD8qLW_resizer{z-index:30;cursor:col-resize;touch-action:none;justify-content:center;align-items:center;width:10px;display:flex;position:absolute;top:0;bottom:0;left:0}.SD8qLW_resizer:after{content:\"\";background:var(--gs-fg-fainter);opacity:0;border-radius:999px;width:3px;height:32px;transition:opacity .14s,height .14s}.SD8qLW_resizer:hover:after{opacity:.6}.SD8qLW_resizerActive:after{opacity:.9;height:56px}.SD8qLW_paneDivider{z-index:5;cursor:col-resize;touch-action:none;flex:none;justify-content:center;align-items:center;width:7px;display:flex;position:relative}.SD8qLW_paneDivider:before{content:\"\";background:var(--gs-border-soft);width:1px;position:absolute;top:0;bottom:0;left:3px}.SD8qLW_paneDivider:after{content:\"\";background:var(--gs-fg-fainter);opacity:0;border-radius:999px;width:3px;height:28px;transition:opacity .14s,height .14s;position:relative}.SD8qLW_paneDivider:hover:after{opacity:.6}.SD8qLW_paneDividerActive:after{opacity:.9;height:48px}.SD8qLW_header{padding:10px var(--gs-gutter);border-bottom:1px solid var(--gs-border);background:var(--gs-surface-2);font-family:var(--dsw-font-ui,system-ui, sans-serif);font-size:var(--gs-t-ui);flex:none;justify-content:space-between;align-items:center;gap:12px;display:flex}.SD8qLW_headerLeft{flex-wrap:wrap;align-items:center;gap:10px;min-width:0;display:flex}.SD8qLW_headerBranch{border:1px solid var(--gs-accent-border);background:var(--gs-accent-bg);color:var(--gs-accent);border-radius:999px;align-items:center;gap:6px;padding:2px 10px;font-weight:600;display:inline-flex}.SD8qLW_refButton.SD8qLW_headerPicker{border-color:var(--gs-accent-border);background:var(--gs-accent-bg);max-width:420px;color:var(--gs-accent);font-weight:600}.SD8qLW_refButton.SD8qLW_headerPicker:hover{background:var(--gs-accent-bg);color:var(--gs-accent);border-color:var(--gs-accent)}.SD8qLW_elide{flex:0 auto;align-items:baseline;min-width:0;display:inline-flex}.SD8qLW_elideHead{white-space:nowrap;text-overflow:ellipsis;flex:0 999 auto;min-width:0;overflow:hidden}.SD8qLW_elideTail{white-space:nowrap;text-overflow:ellipsis;flex:0 auto;min-width:0;overflow:hidden}.SD8qLW_headerPathMain{color:var(--gs-fg-faint);font-size:var(--gs-t-meta)}.SD8qLW_headerPathMain .SD8qLW_elideTail{color:var(--gs-fg-dim)}.SD8qLW_headerViewRef{max-width:160px}.SD8qLW_headerView{background:var(--gs-neutral-bg);color:var(--gs-fg-muted);font-size:var(--gs-t-meta);font-variant-numeric:tabular-nums;border-radius:999px;flex:none;padding:1px 8px}.SD8qLW_headerDetached{background:var(--gs-warn-bg);color:var(--gs-warn);font-size:var(--gs-t-meta);letter-spacing:.02em;border-radius:999px;padding:1px 8px}.SD8qLW_headerTotals{font-variant-numeric:tabular-nums}.SD8qLW_headerTotalsAdd{color:var(--gs-add);font-weight:600}.SD8qLW_headerTotalsDel{color:var(--gs-del);font-weight:600}.SD8qLW_headerTotalsDim{color:var(--gs-fg-dim);font-size:var(--gs-t-dense)}.SD8qLW_headerRight{flex:none;gap:8px;display:flex;position:relative}.SD8qLW_theme{display:inline-flex;position:relative}.SD8qLW_refPop.SD8qLW_settingsPop{width:320px;max-width:calc(100vw - 32px);max-height:min(560px,100vh - 120px);top:calc(100% + 6px);left:auto;right:0}.SD8qLW_themeRail{min-height:0;font-family:var(--dsw-font-ui,system-ui, sans-serif);flex-direction:column;flex:1;gap:14px;padding:12px 14px 16px;display:flex;overflow-y:auto}.SD8qLW_themeGroup{flex-direction:column;gap:6px;display:flex}.SD8qLW_themeLabel{color:var(--gs-fg-faint);font-size:var(--gs-t-meta);letter-spacing:.04em;text-transform:uppercase;font-weight:600}.SD8qLW_themeRowSplit{justify-content:space-between;align-items:center;gap:8px;display:flex}.SD8qLW_segmented{gap:6px;display:flex}.SD8qLW_segment{box-sizing:border-box;border:1px solid var(--gs-border);border-radius:var(--gs-r-control);color:var(--gs-fg-dim);font-family:inherit;font-size:var(--gs-t-meta);cursor:pointer;background:0 0;flex-direction:column;flex:1 1 0;align-items:stretch;gap:4px;padding:4px;transition:background .12s,color .12s,border-color .12s;display:flex}.SD8qLW_segment:hover{background:var(--gs-raise);border-color:var(--gs-fg-fainter);color:var(--gs-fg)}.SD8qLW_segment:focus-visible{outline:2px solid var(--gs-accent);outline-offset:1px}.SD8qLW_segmentChip{border-radius:calc(var(--gs-r-control) - 4px);border:1px solid #80808073;height:20px}.SD8qLW_chipLight{background:#fff}.SD8qLW_chipDark{background:#0b0b0d}.SD8qLW_chipSystem{background:linear-gradient(105deg,#fff 0 50%,#0b0b0d 50% 100%)}.SD8qLW_paletteRow{box-sizing:border-box;border-radius:var(--gs-r-control);width:100%;color:var(--gs-fg-muted);font-family:inherit;font-size:var(--gs-t-dense);text-align:left;cursor:pointer;background:0 0;border:0;align-items:center;gap:8px;padding:5px 8px;display:flex}.SD8qLW_paletteRow:hover{background:var(--gs-raise);color:var(--gs-fg)}.SD8qLW_swatch{border:1px solid var(--gs-border);border-radius:3px;flex:none;width:26px;height:12px;display:inline-flex;overflow:hidden}.SD8qLW_swatch span{flex:1}.SD8qLW_scopeRow{gap:6px;display:flex}.SD8qLW_scopeHint{color:var(--gs-fg-faint);font-size:var(--gs-t-meta);overflow-wrap:anywhere}.SD8qLW_bgPreview{border:1px solid var(--gs-border);border-radius:var(--gs-r-control);background-color:var(--gs-bg);background-position:50%;background-size:cover;height:64px}.SD8qLW_bgEmpty{color:var(--gs-fg-faint);font-size:var(--gs-t-meta);justify-content:center;align-items:center;display:flex}.SD8qLW_sliderRow{font-size:var(--gs-t-meta);color:var(--gs-fg-dim);align-items:center;gap:8px;display:flex}.SD8qLW_sliderRow input{min-width:0;accent-color:var(--gs-accent);flex:1}.SD8qLW_sliderValue{text-align:right;font-variant-numeric:tabular-nums;flex:none;width:38px}.SD8qLW_cssArea{box-sizing:border-box;border:1px solid var(--gs-border);border-radius:var(--gs-r-control);background:var(--gs-bg);width:100%;min-height:96px;max-height:220px;color:var(--gs-fg);font-family:var(--dsw-font-mono,ui-monospace, Consolas, monospace);font-size:var(--gs-t-meta);resize:vertical;outline:none;padding:7px 8px}.SD8qLW_cssArea:focus{border-color:var(--gs-accent-border)}.SD8qLW_themeNote{color:var(--gs-warn);font-size:var(--gs-t-meta)}.SD8qLW_themeDirty{color:var(--gs-accent);font-size:var(--gs-t-meta)}.SD8qLW_wtCurrent{color:var(--gs-add);font-size:8px;line-height:1}.SD8qLW_commitCopy{margin-left:auto}.SD8qLW_commitPop{z-index:40;box-sizing:border-box;border:1px solid var(--gs-border);border-radius:var(--gs-r-surface);background:var(--gs-panel);width:min(380px,100vw - 24px);color:var(--gs-fg);box-shadow:0 16px 40px var(--gs-shadow);font-family:var(--dsw-font-ui,-apple-system, BlinkMacSystemFont, \"Segoe UI\", Helvetica, Arial, sans-serif);font-size:var(--gs-t-dense);flex-direction:column;gap:6px;padding:10px 12px 12px;display:flex;position:fixed;overflow:auto}.SD8qLW_commitPopTop{align-items:center;gap:8px;display:flex}.SD8qLW_commitPopTop .SD8qLW_commitWhen{margin-right:auto}.SD8qLW_commitPopMeta{color:var(--gs-fg-muted);font-size:var(--gs-t-dense);flex-direction:column;gap:2px;line-height:16px;display:flex}.SD8qLW_commitFilter{min-width:120px;height:var(--gs-h-control);box-sizing:border-box;border:1px solid var(--gs-border-soft);border-radius:var(--gs-r-control);background:var(--gs-bg);color:var(--gs-fg);font-family:inherit;font-size:var(--gs-t-dense);outline:none;flex:auto;padding:0 10px}.SD8qLW_commitFilter:focus{border-color:var(--gs-accent)}.SD8qLW_commitFilter::placeholder{color:var(--gs-fg-faint)}.SD8qLW_filterChips{border-bottom:1px solid var(--gs-border-soft);flex-wrap:wrap;flex:none;align-items:center;gap:6px;padding:6px 10px;display:flex}.SD8qLW_filterChip{border:1px solid var(--gs-border-soft);border-radius:var(--gs-r-control);background:var(--gs-surface-2);max-width:220px;font-size:var(--gs-t-dense);align-items:center;gap:2px;line-height:18px;display:inline-flex}.SD8qLW_filterChipLabel{text-overflow:ellipsis;white-space:nowrap;color:var(--gs-fg-muted);padding:2px 4px 2px 8px;overflow:hidden}.SD8qLW_filterChipRemove{border:none;border-left:1px solid var(--gs-border-soft);color:var(--gs-fg-dim);font-family:inherit;font-size:var(--gs-t-dense);cursor:pointer;background:0 0;flex:none;padding:0 6px;line-height:20px}.SD8qLW_filterChipRemove:hover{color:var(--gs-danger,var(--gs-fg))}.SD8qLW_filterClear{color:var(--gs-fg-faint);font-family:inherit;font-size:var(--gs-t-dense);cursor:pointer;background:0 0;border:none;padding:2px 4px;line-height:18px}.SD8qLW_filterClear:hover{color:var(--gs-fg)}.SD8qLW_funnel{display:inline-flex;position:relative}.SD8qLW_funnelButton.SD8qLW_funnelButtonActive{color:var(--gs-accent);border-color:var(--gs-accent)}.SD8qLW_funnelPop{z-index:40;box-sizing:border-box;border:1px solid var(--gs-border);border-radius:var(--gs-r-surface);background:var(--gs-panel);width:320px;max-width:calc(100vw - 24px);box-shadow:0 16px 40px var(--gs-shadow);font-size:var(--gs-t-dense);flex-direction:column;display:flex;position:fixed;overflow:hidden}.SD8qLW_funnelTabs{border:1px solid var(--gs-border-soft);border-radius:var(--gs-r-pill);background:var(--gs-bg);flex:none;gap:2px;margin:8px 8px 0;padding:2px;display:flex}.SD8qLW_funnelTab{border-radius:var(--gs-r-pill);min-width:0;height:22px;color:var(--gs-fg-dim);font-family:inherit;font-size:var(--gs-t-dense);white-space:nowrap;cursor:pointer;background:0 0;border:none;flex:1;padding:0 8px;line-height:1;transition:background .12s,color .12s}.SD8qLW_funnelTab:hover{color:var(--gs-fg)}.SD8qLW_funnelTab.SD8qLW_funnelTabActive{color:var(--gs-fg);background:var(--gs-raise);box-shadow:inset 0 0 0 1px var(--gs-border)}.SD8qLW_funnelTab:focus-visible{outline:2px solid var(--gs-accent);outline-offset:1px}.SD8qLW_funnelTabCount{color:var(--gs-accent);font-variant-numeric:tabular-nums;margin-left:4px}.SD8qLW_funnelPane{flex-direction:column;flex:auto;gap:6px;min-height:0;padding:8px;display:flex}.SD8qLW_funnelCaption{color:var(--gs-fg-faint);letter-spacing:.04em;text-transform:uppercase;flex:none;font-size:11px;line-height:14px}.SD8qLW_funnelBounds{border:1px solid var(--gs-border-soft);border-radius:var(--gs-r-control);background:var(--gs-bg);flex:none;gap:2px;padding:2px;display:flex}.SD8qLW_funnelBoundBtn{border-radius:calc(var(--gs-r-control) - 3px);min-width:0;height:20px;color:var(--gs-fg-dim);font-family:inherit;font-size:var(--gs-t-dense);cursor:pointer;background:0 0;border:none;flex:1;padding:0 6px;line-height:1;transition:background .12s,color .12s}.SD8qLW_funnelBoundBtn:hover{color:var(--gs-fg)}.SD8qLW_funnelBoundBtn.SD8qLW_funnelBoundBtnActive{color:var(--gs-accent);background:var(--gs-accent-bg);box-shadow:inset 0 0 0 1px var(--gs-accent-border)}.SD8qLW_cal{flex-direction:column;flex:none;gap:4px;display:flex}.SD8qLW_calHead{align-items:center;gap:4px;display:flex}.SD8qLW_calTitle{text-align:center;color:var(--gs-fg);font-size:var(--gs-t-dense);flex:1;line-height:20px}.SD8qLW_calNav{border-radius:var(--gs-r-control);width:22px;height:20px;color:var(--gs-fg-dim);font-family:inherit;font-size:var(--gs-t-ui);cursor:pointer;background:0 0;border:none;flex:none;padding:0}.SD8qLW_calNav:hover{color:var(--gs-fg);background:var(--gs-raise)}.SD8qLW_calWeek,.SD8qLW_calGrid{grid-template-columns:repeat(7,1fr);gap:2px;display:grid}.SD8qLW_calWeek span{text-align:center;color:var(--gs-fg-faint);font-size:11px;line-height:14px}.SD8qLW_calGrid button{border-radius:var(--gs-r-control);height:26px;color:var(--gs-fg-muted);font-family:inherit;font-size:var(--gs-t-dense);font-variant-numeric:tabular-nums;cursor:pointer;background:0 0;border:none}.SD8qLW_calGrid button:hover{background:var(--gs-raise);color:var(--gs-fg)}.SD8qLW_calGrid button.SD8qLW_calOut{color:var(--gs-fg-fainter)}.SD8qLW_calGrid button.SD8qLW_calToday{color:var(--gs-accent);font-weight:600}.SD8qLW_calGrid button.SD8qLW_calIn{background:var(--gs-accent-bg);color:var(--gs-fg)}.SD8qLW_calGrid button.SD8qLW_calMark{background:var(--gs-accent);color:var(--gs-on-accent);font-weight:600}.SD8qLW_funnelBoundRows{flex:none;grid-template-columns:1fr 1fr;gap:6px;display:grid}.SD8qLW_funnelBoundRow{box-sizing:border-box;border:1px solid var(--gs-border-soft);border-radius:var(--gs-r-control);background:var(--gs-bg);align-items:center;gap:4px;min-width:0;padding:3px 6px;font-size:11px;line-height:16px;display:flex}.SD8qLW_funnelBoundKey{color:var(--gs-fg-faint);flex:none}.SD8qLW_funnelBoundVal{text-overflow:ellipsis;white-space:nowrap;min-width:0;color:var(--gs-fg-faint);font-variant-numeric:tabular-nums;flex:1;overflow:hidden}.SD8qLW_funnelBoundValSet{color:var(--gs-fg)}.SD8qLW_funnelBoundClear{color:var(--gs-fg-dim);font-family:inherit;font-size:var(--gs-t-dense);cursor:pointer;background:0 0;border:none;flex:none;padding:0 2px;line-height:1}.SD8qLW_funnelBoundClear:hover{color:var(--gs-danger,var(--gs-fg))}.SD8qLW_funnelSearch{box-sizing:border-box;border:1px solid var(--gs-border-soft);border-radius:var(--gs-r-control);background:var(--gs-bg);height:26px;color:var(--gs-fg);font-family:inherit;font-size:var(--gs-t-dense);outline:none;flex:none;padding:0 8px}.SD8qLW_funnelSearch::placeholder{color:var(--gs-fg-faint)}.SD8qLW_funnelSearch:focus{border-color:var(--gs-accent);box-shadow:0 0 0 2px var(--gs-accent-bg)}.SD8qLW_funnelList{overscroll-behavior:contain;scrollbar-width:thin;scrollbar-color:var(--gs-fg-fainter) transparent;flex-direction:column;flex:auto;gap:1px;min-height:0;margin:0 -4px;padding:0 4px;display:flex;overflow-y:auto}.SD8qLW_funnelList::-webkit-scrollbar{width:10px}.SD8qLW_funnelList::-webkit-scrollbar-track{background:0 0}.SD8qLW_funnelList::-webkit-scrollbar-thumb{background:var(--gs-fg-fainter);background-clip:content-box;border:3px solid #0000;border-radius:5px}.SD8qLW_funnelRow{box-sizing:border-box;border-radius:var(--gs-r-control);cursor:pointer;align-items:center;gap:6px;min-height:24px;padding:0 4px;display:flex}.SD8qLW_funnelRow:hover{background:var(--gs-raise)}.SD8qLW_funnelRow:has(input[type=checkbox]:checked) .SD8qLW_funnelName,.SD8qLW_funnelRow:has(input[type=checkbox]:indeterminate) .SD8qLW_funnelName{color:var(--gs-fg)}.SD8qLW_funnelRow input[type=checkbox]{-webkit-appearance:none;appearance:none;box-sizing:border-box;border:1px solid var(--gs-fg-fainter);cursor:pointer;background:0 0;border-radius:3px;flex:none;width:14px;height:14px;margin:0;transition:background .12s,border-color .12s;position:relative}.SD8qLW_funnelRow input[type=checkbox]:hover{border-color:var(--gs-fg-dim)}.SD8qLW_funnelRow input[type=checkbox]:checked,.SD8qLW_funnelRow input[type=checkbox]:indeterminate{background:var(--gs-accent);border-color:var(--gs-accent)}.SD8qLW_funnelRow input[type=checkbox]:focus-visible{outline:2px solid var(--gs-accent);outline-offset:1px}.SD8qLW_funnelRow input[type=checkbox]:checked:after{content:\"\";border:solid var(--gs-on-accent);border-width:0 1.5px 1.5px 0;width:3px;height:7px;position:absolute;top:1px;left:4px;transform:rotate(42deg)}.SD8qLW_funnelRow input[type=checkbox]:indeterminate:after{content:\"\";background:var(--gs-on-accent);border-radius:1px;width:8px;height:2px;position:absolute;top:5px;left:2px}.SD8qLW_pathNode,.SD8qLW_pathChildren{flex-direction:column;display:flex}.SD8qLW_pathFileGlyph,.SD8qLW_pathDirGlyph{width:16px;height:16px;color:var(--gs-fg-dim);flex:none}.SD8qLW_funnelRow:hover .SD8qLW_pathFileGlyph,.SD8qLW_funnelRow:hover .SD8qLW_pathDirGlyph{color:var(--gs-fg-muted)}.SD8qLW_funnelName{text-overflow:ellipsis;white-space:nowrap;min-width:0;color:var(--gs-fg-muted);flex:1;overflow:hidden}.SD8qLW_funnelCount{color:var(--gs-fg-faint);font-variant-numeric:tabular-nums;flex:none;font-size:11px}.SD8qLW_funnelMore{color:var(--gs-fg-faint);padding:4px 6px}.SD8qLW_funnelChevron{width:14px;color:var(--gs-fg-faint);cursor:pointer;background:0 0;border:none;flex:none;padding:0;font-family:inherit;font-size:9px;line-height:16px}.SD8qLW_funnelChevron:hover:not(:disabled){color:var(--gs-fg)}.SD8qLW_funnelChevron:disabled{cursor:default}.SD8qLW_funnelPresets{flex:none;gap:4px;display:flex}.SD8qLW_funnelPreset{flex:1 1 0;min-width:0;padding:0 6px}.SD8qLW_funnelPreset.SD8qLW_funnelPresetActive{color:var(--gs-accent);border-color:var(--gs-accent);background:var(--gs-accent-bg)}.SD8qLW_funnelFoot{border-top:1px solid var(--gs-border-soft);background:var(--gs-bg);flex:none;justify-content:space-between;align-items:center;gap:8px;padding:6px 10px;font-size:11px;line-height:16px;display:flex}.SD8qLW_funnelFootCount{color:var(--gs-fg-faint);font-variant-numeric:tabular-nums}.SD8qLW_funnelFootCountOn{color:var(--gs-fg-muted)}.SD8qLW_funnelFootClear{border-radius:var(--gs-r-control);color:var(--gs-fg-dim);cursor:pointer;background:0 0;border:none;flex:none;padding:2px 6px;font-family:inherit;font-size:11px;line-height:16px}.SD8qLW_funnelFootClear:hover:not(:disabled){color:var(--gs-fg);background:var(--gs-raise)}.SD8qLW_funnelFootClear:disabled{color:var(--gs-fg-fainter);cursor:default}.SD8qLW_commitPopSubject{color:var(--gs-fg);font-size:var(--gs-t-ui);white-space:pre-wrap;overflow-wrap:anywhere;user-select:text;font-weight:600;line-height:18px}.SD8qLW_commitPopBody{color:var(--gs-fg-muted);font-family:inherit;font-size:var(--gs-t-dense);white-space:pre-wrap;overflow-wrap:anywhere;user-select:text;margin:0;line-height:18px}.SD8qLW_tabs{padding:0 var(--gs-gutter);border-bottom:1px solid var(--gs-border-soft);background:var(--gs-surface-2);flex:none;gap:24px;font-family:inherit;display:flex}.SD8qLW_tab{font-family:inherit;font-size:var(--gs-t-ui);color:var(--gs-fg-dim);cursor:pointer;background:0 0;border:none;padding:8px 0 9px;font-weight:500;line-height:16px;position:relative}.SD8qLW_tab:after{content:\"\";background:0 0;border-radius:2px;height:2px;position:absolute;bottom:1px;left:0;right:0}.SD8qLW_tabActive{color:var(--gs-accent)}.SD8qLW_tabActive:after{background:var(--gs-accent)}.SD8qLW_compareBar{padding:8px var(--gs-gutter);border-bottom:1px solid var(--gs-border-soft);background:var(--gs-surface-2);color:var(--gs-fg-dim);font-family:var(--dsw-font-ui,system-ui, sans-serif);font-size:var(--gs-t-dense);flex-wrap:wrap;flex:none;align-items:center;gap:10px;display:flex}.SD8qLW_compareArrow{color:var(--gs-fg-faint)}.SD8qLW_refPicker{align-items:center;gap:6px;display:inline-flex;position:relative}.SD8qLW_refLabel{color:var(--gs-fg-faint);flex:none}.SD8qLW_refButton{justify-content:space-between;max-width:260px}.SD8qLW_refValue{min-width:0;overflow:hidden}.SD8qLW_refCaret{color:var(--gs-fg-faint);flex:none;font-size:9px}.SD8qLW_refPop{z-index:10;border:1px solid var(--gs-border);border-radius:var(--gs-r-control);background:var(--gs-panel);width:300px;max-width:80vw;box-shadow:0 12px 32px var(--gs-shadow);flex-direction:column;display:flex;position:absolute;top:calc(100% + 4px);left:0;overflow:hidden}.SD8qLW_menuPop{width:auto;min-width:100%;padding:4px;left:auto;right:0}.SD8qLW_refSearch{border:0;border-bottom:1px solid var(--gs-border-soft);background:var(--gs-bg);color:var(--gs-fg);font-family:inherit;font-size:var(--gs-t-dense);outline:none;flex:none;padding:7px 10px}.SD8qLW_refSearch::placeholder{color:var(--gs-fg-faint)}.SD8qLW_refList{flex:1;max-height:280px;padding:4px;overflow-y:auto}.SD8qLW_refGroup{color:var(--gs-fg-faint);font-size:var(--gs-t-meta);letter-spacing:.04em;text-transform:uppercase;padding:6px 8px 3px;font-weight:600}.SD8qLW_refRow{box-sizing:border-box;border-radius:var(--gs-r-control);width:100%;color:var(--gs-fg-muted);font-family:inherit;font-size:var(--gs-t-dense);text-align:left;cursor:pointer;background:0 0;border:0;align-items:center;gap:6px;padding:5px 8px;display:flex}.SD8qLW_refRow:hover{background:var(--gs-raise);color:var(--gs-fg)}.SD8qLW_refRowSpacer{flex:none;width:12px}.SD8qLW_refRowName{flex:auto;min-width:0;overflow:hidden}.SD8qLW_refEmpty{color:var(--gs-fg-faint);font-size:var(--gs-t-dense);padding:14px 10px}.SD8qLW_refFoot{border-top:1px solid var(--gs-border-soft);color:var(--gs-fg-faint);font-size:var(--gs-t-meta);flex:none;padding:5px 10px}.SD8qLW_commitsPane{box-sizing:border-box;width:26%;min-height:0;min-width:var(--gs-min-commits);background:var(--gs-surface);flex-direction:column;flex:0 auto;max-width:340px;display:flex}.SD8qLW_paneHead{box-sizing:border-box;min-height:37px;padding:6px var(--gs-gutter-pane);border-bottom:1px solid var(--gs-border-soft);color:var(--gs-fg-dim);font-family:var(--dsw-font-ui,system-ui, sans-serif);font-size:var(--gs-t-dense);flex:none;align-items:center;gap:8px;display:flex}.SD8qLW_paneTitle{font-weight:600}.SD8qLW_commitsSentinel{flex:none;height:1px}.SD8qLW_commitsFoot{color:var(--gs-fg-faint);font-family:var(--dsw-font-ui,system-ui, sans-serif);font-size:var(--gs-t-meta);text-align:center;flex:none;min-height:16px;padding:8px}.SD8qLW_commits{flex-direction:column;flex:1;min-height:0;padding:4px 6px 4px 0;display:flex;overflow-y:auto}.SD8qLW_commitLine{flex:none;align-items:stretch;height:48px;display:flex}.SD8qLW_graphCell{flex:none;display:block}.SD8qLW_commit{box-sizing:border-box;border-radius:var(--gs-r-surface);text-align:left;cursor:pointer;min-width:0;height:100%;font-family:inherit;font-size:var(--gs-t-dense);background:0 0;border:1px solid #0000;flex-direction:column;flex:auto;justify-content:center;gap:2px;margin:1px 0;padding:4px 8px;transition:background .12s,border-color .12s;display:flex;overflow:hidden}.SD8qLW_commit:hover{background:var(--gs-raise)}.SD8qLW_commitActive{border-color:var(--gs-accent-border);background:var(--gs-accent-bg)}.SD8qLW_commitTop{justify-content:space-between;align-items:center;gap:8px;display:flex}.SD8qLW_commitHash{color:var(--gs-info)}.SD8qLW_commitSubjectRow{align-items:baseline;gap:4px;min-width:0;display:flex}.SD8qLW_commitSubject{white-space:nowrap;text-overflow:ellipsis;min-width:0;color:var(--gs-fg-muted);flex:1;overflow:hidden}.SD8qLW_commitHasBody{color:var(--gs-fg-faint);letter-spacing:.04em;flex:none}.SD8qLW_commitRef{white-space:nowrap;text-overflow:ellipsis;border:1px solid var(--gs-accent-border);border-radius:var(--gs-r-pill);background:var(--gs-accent-bg);max-width:96px;color:var(--gs-accent);font-size:var(--gs-t-meta);flex:none;padding:0 5px;line-height:15px;overflow:hidden}.SD8qLW_commitWhen{color:var(--gs-fg-faint);flex:none}.SD8qLW_commitAuthor{text-overflow:ellipsis;white-space:nowrap;max-width:120px;color:var(--gs-fg-dim);flex:none;overflow:hidden}.SD8qLW_body{z-index:1;flex:1;min-height:0;display:flex}.SD8qLW_treeCol{box-sizing:border-box;width:28%;min-height:0;min-width:var(--gs-min-tree);background:var(--gs-surface);flex-direction:column;flex:0 auto;max-width:400px;display:flex}.SD8qLW_treeWrap{box-sizing:border-box;flex-direction:column;flex:1;min-width:0;min-height:0;display:flex}.SD8qLW_treeTools{box-sizing:border-box;min-height:37px;padding:6px var(--gs-gutter-pane);border-bottom:1px solid var(--gs-border-soft);flex-wrap:wrap;flex:none;justify-content:space-between;align-items:center;gap:6px;display:flex}.SD8qLW_treeActions{flex-wrap:wrap;align-items:center;gap:6px;display:flex}.SD8qLW_treeLead{align-items:center;gap:4px;min-width:0;display:flex}.SD8qLW_treeLabel{white-space:nowrap;color:var(--gs-fg-dim);font-size:var(--gs-t-dense);flex:none}.SD8qLW_treeFilter{padding:6px var(--gs-gutter-pane);border-bottom:1px solid var(--gs-border-soft);flex:none;align-items:center;display:flex;position:relative}.SD8qLW_treeFilterInput{min-width:0;height:var(--gs-h-compact);box-sizing:border-box;border:1px solid var(--gs-border-soft);border-radius:var(--gs-r-control);background:var(--gs-bg);color:var(--gs-fg);font-family:inherit;font-size:var(--gs-t-dense);outline:none;flex:auto;padding:0 24px 0 8px}.SD8qLW_treeFilterInput:focus{border-color:var(--gs-accent)}.SD8qLW_treeFilterInput::placeholder{color:var(--gs-fg-faint)}.SD8qLW_treeFilterClear{right:calc(var(--gs-gutter-pane) + 6px);border-radius:var(--gs-r-pill);width:16px;height:16px;color:var(--gs-fg-faint);font-size:var(--gs-t-ui);cursor:pointer;background:0 0;border:0;justify-content:center;align-items:center;padding:0;line-height:1;display:flex;position:absolute}.SD8qLW_treeFilterClear:hover{color:var(--gs-fg)}.SD8qLW_tree{flex:1;min-height:0;margin:0;padding:6px 0 16px;list-style:none;overflow:auto}.SD8qLW_treeEmpty{min-height:0;color:var(--gs-fg-faint);font-size:var(--gs-t-ui);flex:1;justify-content:center;align-items:center;display:flex}.SD8qLW_treeSub{margin:0;padding:0;list-style:none;position:relative}.SD8qLW_treeSub:before{content:\"\";top:0;bottom:0;left:var(--gs-rail,0);background:var(--gs-border-soft);width:1px;position:absolute}.SD8qLW_treeDirLi{position:relative}.SD8qLW_treeDir{box-sizing:border-box;width:100%;color:var(--gs-fg-dim);font-family:inherit;font-size:var(--gs-t-dense);text-align:left;cursor:pointer;background:0 0;border:0;align-items:center;gap:6px;padding:4px 12px 4px 8px;font-weight:600;display:flex}.SD8qLW_treeDir:hover{background:var(--gs-panel);color:var(--gs-fg-muted)}.SD8qLW_treeDirActive{color:var(--gs-fg-muted)}.SD8qLW_chevron{width:10px;color:var(--gs-fg-fainter);font-size:var(--gs-t-meta);flex:none;transition:transform .12s;display:inline-block}.SD8qLW_chevronOpen{transform:rotate(90deg)}.SD8qLW_treeDirName{white-space:nowrap;text-overflow:ellipsis;flex:1;min-width:0;overflow:hidden}.SD8qLW_treeDirCount{background:var(--gs-neutral-bg);color:var(--gs-fg-dim);font-size:var(--gs-t-meta);border-radius:999px;flex:none;padding:0 6px;font-weight:600;line-height:16px}.SD8qLW_treeDirCounts{font-size:var(--gs-t-meta);font-variant-numeric:tabular-nums;flex:none}.SD8qLW_file{box-sizing:border-box;width:100%;color:var(--gs-fg-muted);font-family:inherit;font-size:var(--gs-t-dense);text-align:left;cursor:pointer;background:0 0;border:0;border-left:2px solid #0000;align-items:center;gap:8px;padding:5px 14px;display:flex}.SD8qLW_file:hover{background:var(--gs-panel)}.SD8qLW_fileActive{background:var(--gs-panel);border-left-color:var(--gs-accent);color:var(--gs-fg)}.SD8qLW_fileStatus{width:18px;height:18px;font-weight:700;font-size:var(--gs-t-meta);border-radius:5px;flex:none;justify-content:center;align-items:center;line-height:1;display:inline-flex}.SD8qLW_stAdded,.SD8qLW_stUntracked{color:var(--gs-add);background:var(--gs-add-bg)}.SD8qLW_stDeleted{color:var(--gs-del);background:var(--gs-del-bg)}.SD8qLW_stModified{color:var(--gs-warn);background:var(--gs-warn-bg)}.SD8qLW_stRenamed{color:var(--gs-info);background:var(--gs-info-bg)}.SD8qLW_filePath{white-space:nowrap;text-overflow:ellipsis;flex:1;min-width:0;overflow:hidden}.SD8qLW_fileBinary{background:var(--gs-neutral-bg);color:var(--gs-fg-dim);letter-spacing:.06em;border-radius:4px;flex:none;padding:0 5px;font-size:9px;font-weight:700}.SD8qLW_fileCounts{font-size:var(--gs-t-meta);font-variant-numeric:tabular-nums;flex:none}.SD8qLW_fileCountAdd{color:var(--gs-add)}.SD8qLW_fileCountDel{color:var(--gs-del)}.SD8qLW_diffPane{min-width:var(--gs-min-diff);background:var(--gs-surface);flex:1 1 0;overflow:auto}.SD8qLW_diffPre{width:max-content;min-width:100%;font-family:ui-monospace,SFMono-Regular,SF Mono,Menlo,Consolas,Liberation Mono,monospace;font-size:var(--gs-t-dense);font-variant-ligatures:none;font-feature-settings:\"liga\" 0, \"calt\" 0;tab-size:4;flex-direction:column;margin:0;padding:8px 0 16px;font-weight:400;line-height:20px;display:flex}.SD8qLW_renameLine{border-bottom:1px solid var(--gs-border-soft);color:var(--gs-info);font-size:var(--gs-t-dense);padding:8px 16px}.SD8qLW_renameLine code{color:var(--gs-fg)}.SD8qLW_line{white-space:nowrap;align-items:stretch;width:max-content;min-width:100%;min-height:20px;line-height:20px;display:flex}.SD8qLW_lnOld,.SD8qLW_lnNew{box-sizing:border-box;text-align:right;width:3.2em;color:var(--gs-fg-fainter);user-select:none;font-variant-numeric:tabular-nums;flex:none;padding:0 8px}.SD8qLW_line:hover .SD8qLW_lnOld,.SD8qLW_line:hover .SD8qLW_lnNew{color:var(--gs-fg-dim)}.SD8qLW_gutter{text-align:center;user-select:none;flex:none;width:1.4em}.SD8qLW_code{white-space:pre;flex:1 0 auto;padding:0 16px 0 10px}.SD8qLW_lineAdd{background:var(--gs-add-line)}.SD8qLW_lineDel{background:var(--gs-del-line)}.SD8qLW_lineAdd .SD8qLW_lnOld,.SD8qLW_lineAdd .SD8qLW_lnNew,.SD8qLW_lineAdd .SD8qLW_gutter{background:var(--gs-add-num,var(--gs-add-line))}.SD8qLW_lineDel .SD8qLW_lnOld,.SD8qLW_lineDel .SD8qLW_lnNew,.SD8qLW_lineDel .SD8qLW_gutter{background:var(--gs-del-num,var(--gs-del-line))}.SD8qLW_lineContext{color:var(--gs-fg-muted)}.SD8qLW_lineHunk{background:var(--gs-hunk)}.SD8qLW_lineHunk .SD8qLW_code{color:var(--gs-fg-dim)}.SD8qLW_lineHunk .SD8qLW_lnOld,.SD8qLW_lineHunk .SD8qLW_lnNew,.SD8qLW_lineHunk .SD8qLW_gutter{background:var(--gs-hunk-num,var(--gs-hunk))}.SD8qLW_signAdd{color:var(--gs-add)}.SD8qLW_signDel{color:var(--gs-del)}.SD8qLW_wordAdd{background:var(--gs-add-word);border-radius:2px}.SD8qLW_wordDel{background:var(--gs-del-word);border-radius:2px}.SD8qLW_empty{color:var(--gs-fg-faint);font-size:var(--gs-t-ui);padding:32px 24px}.SD8qLW_syncBar{padding:8px var(--gs-gutter);border-bottom:1px solid var(--gs-border-soft);background:var(--gs-surface-2);font-size:var(--gs-t-dense);color:var(--gs-fg-dim);flex-wrap:wrap;flex:none;align-items:center;gap:8px;display:flex}.SD8qLW_syncUpstream{white-space:nowrap;text-overflow:ellipsis;max-width:260px;color:var(--gs-fg-muted);font-variant-numeric:tabular-nums;overflow:hidden}.SD8qLW_syncLevel{color:var(--gs-fg-faint)}.SD8qLW_syncSpacer{flex:auto}.SD8qLW_btn:disabled,.SD8qLW_treeIcon:disabled,.SD8qLW_miniBtn:disabled,.SD8qLW_refButton:disabled,.SD8qLW_funnelButton:disabled{opacity:.45;cursor:default}.SD8qLW_btn[data-quiet]:disabled,.SD8qLW_refButton[data-quiet]:disabled{opacity:1}.SD8qLW_opBanner{padding:8px var(--gs-gutter);border-bottom:1px solid var(--gs-border-soft);font-size:var(--gs-t-dense);white-space:pre-wrap;overflow-wrap:anywhere;user-select:text;flex:none;line-height:18px}.SD8qLW_opBannerOk{background:var(--gs-add-bg);color:var(--gs-add)}.SD8qLW_opBannerBad{background:var(--gs-del-bg);color:var(--gs-del)}.SD8qLW_fileLi{align-items:stretch;display:flex}.SD8qLW_fileLi .SD8qLW_file{flex:auto;min-width:0}.SD8qLW_fileDiscard{width:22px;color:var(--gs-fg-faint);opacity:0;cursor:pointer;background:0 0;border:0;flex:none;justify-content:center;align-items:center;padding:0;transition:opacity .12s,color .12s;display:flex}.SD8qLW_fileLi:hover .SD8qLW_fileDiscard,.SD8qLW_fileDiscard:focus-visible{opacity:1}.SD8qLW_fileDiscard:hover{color:var(--gs-del)}.SD8qLW_fileDiscard:focus-visible{outline:2px solid var(--gs-accent);outline-offset:-2px;border-radius:var(--gs-r-control)}.SD8qLW_treeRow{align-items:stretch;display:flex}.SD8qLW_treeRow .SD8qLW_treeDir{flex:auto;min-width:0}.SD8qLW_checkBox{cursor:pointer;background:0 0;border:0;flex:none;justify-content:center;align-items:center;width:22px;padding:0;display:flex}.SD8qLW_checkMark{box-sizing:border-box;border:1px solid var(--gs-fg-faint);background:var(--gs-bg);color:#0000;border-radius:3px;justify-content:center;align-items:center;width:14px;height:14px;font-size:10px;font-weight:700;line-height:1;transition:background .12s,border-color .12s;display:flex}.SD8qLW_checkBox:hover .SD8qLW_checkMark{border-color:var(--gs-accent)}.SD8qLW_checkBox:focus-visible{outline:none}.SD8qLW_checkBox:focus-visible .SD8qLW_checkMark{outline:2px solid var(--gs-accent);outline-offset:1px}.SD8qLW_checkMarkOn,.SD8qLW_checkMarkPartial{border-color:var(--gs-accent);background:var(--gs-accent);color:var(--gs-on-accent)}.SD8qLW_commitBox{padding:8px var(--gs-gutter-pane) 10px;border-top:1px solid var(--gs-border);background:var(--gs-surface-2);flex-direction:column;flex:none;gap:6px;display:flex}.SD8qLW_commitMessage{box-sizing:border-box;border:1px solid var(--gs-border);border-radius:var(--gs-r-control);background:var(--gs-bg);width:100%;min-height:46px;max-height:160px;color:var(--gs-fg);font-family:inherit;font-size:var(--gs-t-dense);resize:vertical;outline:none;padding:6px 8px;line-height:18px}.SD8qLW_commitMessage:focus{border-color:var(--gs-accent-border)}.SD8qLW_commitMessage::placeholder{color:var(--gs-fg-faint)}.SD8qLW_commitRow{align-items:center;gap:8px;display:flex}.SD8qLW_commitAmend{color:var(--gs-fg-dim);font-size:var(--gs-t-meta);cursor:pointer;align-items:center;gap:4px;display:inline-flex}.SD8qLW_commitAmend input{accent-color:var(--gs-accent);cursor:pointer}.SD8qLW_commitStaged{color:var(--gs-fg-faint);font-size:var(--gs-t-meta);margin-left:auto}.SD8qLW_commitLead{color:var(--gs-fg-faint);font-size:var(--gs-t-meta);margin:0;line-height:15px}.SD8qLW_commitBtn{box-sizing:border-box;min-width:92px;height:var(--gs-h-control);border-radius:var(--gs-r-control);background:var(--gs-accent);color:var(--gs-on-accent);font-family:inherit;font-size:var(--gs-t-ui);white-space:nowrap;cursor:pointer;border:1px solid #0000;flex:none;justify-content:center;align-items:center;padding:0 16px;font-weight:600;line-height:1;transition:filter .12s,opacity .12s;display:inline-flex}.SD8qLW_commitBtn:hover:not(:disabled){filter:brightness(1.1)}.SD8qLW_commitBtn:focus-visible{outline:2px solid var(--gs-accent);outline-offset:2px}.SD8qLW_commitBtn:disabled{background:var(--gs-raise);color:var(--gs-fg-faint);border-color:var(--gs-border);cursor:default}.SD8qLW_btn,.SD8qLW_miniBtn,.SD8qLW_treeIcon,.SD8qLW_commitCopy,.SD8qLW_scopeBtn,.SD8qLW_refButton,.SD8qLW_funnelButton,.SD8qLW_funnelPreset{box-sizing:border-box;height:var(--gs-h-control);padding:var(--gs-pad-control);border:1px solid var(--gs-border);border-radius:var(--gs-r-control);color:var(--gs-fg-dim);font-family:inherit;font-size:var(--gs-t-ui);white-space:nowrap;cursor:pointer;background:0 0;flex:none;justify-content:center;align-items:center;gap:6px;line-height:1;transition:background .12s,color .12s,border-color .12s;display:inline-flex}.SD8qLW_btn:hover,.SD8qLW_miniBtn:hover,.SD8qLW_treeIcon:hover,.SD8qLW_commitCopy:hover,.SD8qLW_scopeBtn:hover,.SD8qLW_refButton:hover,.SD8qLW_funnelButton:hover,.SD8qLW_funnelPreset:hover{background:var(--gs-raise);color:var(--gs-fg);border-color:var(--gs-fg-fainter)}.SD8qLW_btn:focus-visible,.SD8qLW_miniBtn:focus-visible,.SD8qLW_treeIcon:focus-visible,.SD8qLW_commitCopy:focus-visible,.SD8qLW_scopeBtn:focus-visible,.SD8qLW_refButton:focus-visible,.SD8qLW_funnelPreset:focus-visible{outline:2px solid var(--gs-accent);outline-offset:1px}.SD8qLW_btn,.SD8qLW_refButton{border-radius:var(--gs-r-pill)}.SD8qLW_treeIcon,.SD8qLW_commitCopy,.SD8qLW_funnelPreset{height:var(--gs-h-compact);padding:var(--gs-pad-compact);font-size:var(--gs-t-dense)}.SD8qLW_treeIcon{width:var(--gs-h-compact);padding:0}.SD8qLW_treeIcon.SD8qLW_treeIconOn{color:var(--gs-accent);border-color:var(--gs-accent)}.SD8qLW_treeIconGlyph{font-size:var(--gs-t-meta);line-height:1;display:block}.SD8qLW_treeIconDown{transform:rotate(90deg)}.SD8qLW_scopeBtn{flex:1 1 0}.SD8qLW_scopeBtnActive,.SD8qLW_miniBtnPrimary,.SD8qLW_segmentActive,.SD8qLW_refRowActive,.SD8qLW_paletteRowActive{border-color:var(--gs-accent-border);background:var(--gs-accent-bg);color:var(--gs-accent);font-weight:600}.SD8qLW_btnIcon{width:var(--gs-h-control);padding:0}.SD8qLW_btnIcon svg{display:block}.SD8qLW_btnClose:hover:not(:disabled){border-color:var(--gs-del);background:var(--gs-del-bg);color:var(--gs-del)}.SD8qLW_btnBehind{border-color:var(--gs-warn);background:var(--gs-warn-bg);color:var(--gs-warn);font-weight:600}.SD8qLW_btnBehind:hover:not(:disabled){background:var(--gs-warn-bg);color:var(--gs-warn);border-color:var(--gs-warn)}.SD8qLW_btnAhead{border-color:var(--gs-add);background:var(--gs-add-bg);color:var(--gs-add);font-weight:600}.SD8qLW_btnAhead:hover:not(:disabled){background:var(--gs-add-bg);color:var(--gs-add);border-color:var(--gs-add)}.SD8qLW_btnPrimary{border-color:var(--gs-accent);background:var(--gs-accent);color:var(--gs-on-accent,#fff);font-weight:600}.SD8qLW_btnPrimary:hover:not(:disabled){background:var(--gs-accent);color:var(--gs-on-accent,#fff);border-color:var(--gs-accent)}.SD8qLW_btnCount{font-variant-numeric:tabular-nums;opacity:.85;border-left:1px solid;margin-left:1px;padding-left:6px}.SD8qLW_pullGroup{flex:none;align-items:center;display:inline-flex}.SD8qLW_pullGroup>.SD8qLW_refPicker>.SD8qLW_refButton{border-right-color:#0000;border-top-right-radius:0;border-bottom-right-radius:0}.SD8qLW_pullGroup>.SD8qLW_btn{border-top-left-radius:0;border-bottom-left-radius:0;margin-left:-1px}.SD8qLW_drawer{scrollbar-width:thin;scrollbar-color:var(--gs-border) transparent}.SD8qLW_drawer ::-webkit-scrollbar{width:10px;height:10px}.SD8qLW_drawer ::-webkit-scrollbar-track,.SD8qLW_drawer ::-webkit-scrollbar-corner{background:0 0}.SD8qLW_drawer ::-webkit-scrollbar-thumb{background:var(--gs-border);background-clip:padding-box;border:3px solid #0000;border-radius:999px}.SD8qLW_drawer ::-webkit-scrollbar-thumb:hover{background:var(--gs-fg-faint);background-clip:padding-box}.SD8qLW_commitRefMore{border-radius:var(--gs-r-pill);background:var(--gs-neutral-bg);color:var(--gs-fg-dim);font-size:var(--gs-t-meta);cursor:default;flex:none;padding:0 5px;line-height:15px}.SD8qLW_confirmScrim{background:var(--gs-backdrop);justify-content:center;align-items:center;padding:24px;animation:.12s ease-out SD8qLW_gsFade;display:flex}.SD8qLW_confirmBox{box-sizing:border-box;border:1px solid var(--gs-border);border-radius:var(--gs-r-drawer);background:var(--gs-bg);width:min(440px,100%);box-shadow:0 18px 48px var(--gs-shadow);flex-direction:column;gap:10px;padding:18px;display:flex}.SD8qLW_confirmTitle{color:var(--gs-fg);font-size:var(--gs-t-ui);font-weight:600}.SD8qLW_confirmBody{color:var(--gs-fg-dim);font-size:var(--gs-t-dense);overflow-wrap:anywhere;line-height:1.55}.SD8qLW_confirmActions{flex-direction:row-reverse;gap:8px;margin-top:2px;display:flex}.SD8qLW_btn.SD8qLW_btnDanger{border-color:var(--gs-del);background:var(--gs-del);color:var(--gs-on-accent,#fff);font-weight:600}.SD8qLW_btn.SD8qLW_btnDanger:hover:not(:disabled){border-color:var(--gs-del);background:var(--gs-del);color:var(--gs-on-accent,#fff)}";
|
|
11617
12208
|
const tagId = "@young1lin/dsh-ui-gitworkbench/GitWorkbenchPanel.module.css";
|
|
11618
12209
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
|
|
11619
12210
|
const tag = document.createElement("style");
|
|
@@ -11623,196 +12214,262 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
11623
12214
|
document.head.appendChild(tag);
|
|
11624
12215
|
}
|
|
11625
12216
|
var GitWorkbenchPanel_module_css_default = {
|
|
11626
|
-
"
|
|
11627
|
-
"
|
|
11628
|
-
"
|
|
11629
|
-
"
|
|
11630
|
-
"
|
|
11631
|
-
"
|
|
11632
|
-
"
|
|
11633
|
-
"
|
|
11634
|
-
"
|
|
11635
|
-
"
|
|
11636
|
-
"
|
|
11637
|
-
"
|
|
11638
|
-
"refList": "SD8qLW_refList",
|
|
11639
|
-
"gutter": "SD8qLW_gutter",
|
|
11640
|
-
"headerLeft": "SD8qLW_headerLeft",
|
|
11641
|
-
"lineDel": "SD8qLW_lineDel",
|
|
11642
|
-
"checkBox": "SD8qLW_checkBox",
|
|
11643
|
-
"btnPrimary": "SD8qLW_btnPrimary",
|
|
11644
|
-
"refCaret": "SD8qLW_refCaret",
|
|
11645
|
-
"lineAdd": "SD8qLW_lineAdd",
|
|
12217
|
+
"syncSpacer": "SD8qLW_syncSpacer",
|
|
12218
|
+
"treeDirActive": "SD8qLW_treeDirActive",
|
|
12219
|
+
"commits": "SD8qLW_commits",
|
|
12220
|
+
"btnAhead": "SD8qLW_btnAhead",
|
|
12221
|
+
"commitTop": "SD8qLW_commitTop",
|
|
12222
|
+
"commitPopSubject": "SD8qLW_commitPopSubject",
|
|
12223
|
+
"segment": "SD8qLW_segment",
|
|
12224
|
+
"funnelChevron": "SD8qLW_funnelChevron",
|
|
12225
|
+
"resizerActive": "SD8qLW_resizerActive",
|
|
12226
|
+
"segmented": "SD8qLW_segmented",
|
|
12227
|
+
"commitMessage": "SD8qLW_commitMessage",
|
|
12228
|
+
"signDel": "SD8qLW_signDel",
|
|
11646
12229
|
"themeRail": "SD8qLW_themeRail",
|
|
11647
|
-
"headerTotals": "SD8qLW_headerTotals",
|
|
11648
|
-
"btn": "SD8qLW_btn",
|
|
11649
|
-
"btnCount": "SD8qLW_btnCount",
|
|
11650
|
-
"compareArrow": "SD8qLW_compareArrow",
|
|
11651
|
-
"headerTotalsDel": "SD8qLW_headerTotalsDel",
|
|
11652
|
-
"overlay": "SD8qLW_overlay",
|
|
11653
|
-
"empty": "SD8qLW_empty",
|
|
11654
|
-
"commitRow": "SD8qLW_commitRow",
|
|
11655
12230
|
"cardAdded": "SD8qLW_cardAdded",
|
|
11656
|
-
"
|
|
11657
|
-
"
|
|
11658
|
-
"scopeBtnActive": "SD8qLW_scopeBtnActive",
|
|
11659
|
-
"treeDirLi": "SD8qLW_treeDirLi",
|
|
12231
|
+
"refEmpty": "SD8qLW_refEmpty",
|
|
12232
|
+
"file": "SD8qLW_file",
|
|
11660
12233
|
"elide": "SD8qLW_elide",
|
|
11661
|
-
"
|
|
11662
|
-
"
|
|
11663
|
-
"
|
|
11664
|
-
"
|
|
11665
|
-
"
|
|
11666
|
-
"
|
|
11667
|
-
"
|
|
11668
|
-
"
|
|
12234
|
+
"sliderValue": "SD8qLW_sliderValue",
|
|
12235
|
+
"fileBinary": "SD8qLW_fileBinary",
|
|
12236
|
+
"wtCurrent": "SD8qLW_wtCurrent",
|
|
12237
|
+
"funnelBoundBtnActive": "SD8qLW_funnelBoundBtnActive",
|
|
12238
|
+
"wordDel": "SD8qLW_wordDel",
|
|
12239
|
+
"themeRowSplit": "SD8qLW_themeRowSplit",
|
|
12240
|
+
"funnelBounds": "SD8qLW_funnelBounds",
|
|
12241
|
+
"graphCell": "SD8qLW_graphCell",
|
|
12242
|
+
"treeIcon": "SD8qLW_treeIcon",
|
|
12243
|
+
"scopeHint": "SD8qLW_scopeHint",
|
|
12244
|
+
"paletteRow": "SD8qLW_paletteRow",
|
|
11669
12245
|
"btnBehind": "SD8qLW_btnBehind",
|
|
11670
|
-
"
|
|
12246
|
+
"stAdded": "SD8qLW_stAdded",
|
|
12247
|
+
"line": "SD8qLW_line",
|
|
12248
|
+
"gutter": "SD8qLW_gutter",
|
|
12249
|
+
"funnelPreset": "SD8qLW_funnelPreset",
|
|
12250
|
+
"treeFilterClear": "SD8qLW_treeFilterClear",
|
|
12251
|
+
"funnelBoundKey": "SD8qLW_funnelBoundKey",
|
|
12252
|
+
"commitActive": "SD8qLW_commitActive",
|
|
12253
|
+
"code": "SD8qLW_code",
|
|
12254
|
+
"commitFilter": "SD8qLW_commitFilter",
|
|
12255
|
+
"funnelPop": "SD8qLW_funnelPop",
|
|
12256
|
+
"treeRow": "SD8qLW_treeRow",
|
|
12257
|
+
"funnelList": "SD8qLW_funnelList",
|
|
12258
|
+
"chevronOpen": "SD8qLW_chevronOpen",
|
|
12259
|
+
"segmentChip": "SD8qLW_segmentChip",
|
|
12260
|
+
"refLabel": "SD8qLW_refLabel",
|
|
12261
|
+
"calNav": "SD8qLW_calNav",
|
|
12262
|
+
"refFoot": "SD8qLW_refFoot",
|
|
11671
12263
|
"miniBtn": "SD8qLW_miniBtn",
|
|
11672
|
-
"
|
|
11673
|
-
"
|
|
11674
|
-
"
|
|
11675
|
-
"
|
|
11676
|
-
"
|
|
11677
|
-
"
|
|
11678
|
-
"
|
|
11679
|
-
"
|
|
11680
|
-
"
|
|
11681
|
-
"
|
|
11682
|
-
"
|
|
11683
|
-
"
|
|
11684
|
-
"
|
|
11685
|
-
"
|
|
11686
|
-
"
|
|
12264
|
+
"commitAmend": "SD8qLW_commitAmend",
|
|
12265
|
+
"treeDirCounts": "SD8qLW_treeDirCounts",
|
|
12266
|
+
"chevron": "SD8qLW_chevron",
|
|
12267
|
+
"headerPicker": "SD8qLW_headerPicker",
|
|
12268
|
+
"overlay": "SD8qLW_overlay",
|
|
12269
|
+
"syncBar": "SD8qLW_syncBar",
|
|
12270
|
+
"funnelPresetActive": "SD8qLW_funnelPresetActive",
|
|
12271
|
+
"refValue": "SD8qLW_refValue",
|
|
12272
|
+
"refList": "SD8qLW_refList",
|
|
12273
|
+
"funnelBoundRows": "SD8qLW_funnelBoundRows",
|
|
12274
|
+
"funnelPresets": "SD8qLW_funnelPresets",
|
|
12275
|
+
"funnelTabActive": "SD8qLW_funnelTabActive",
|
|
12276
|
+
"headerPathMain": "SD8qLW_headerPathMain",
|
|
12277
|
+
"treeLabel": "SD8qLW_treeLabel",
|
|
12278
|
+
"bgEmpty": "SD8qLW_bgEmpty",
|
|
12279
|
+
"themeNote": "SD8qLW_themeNote",
|
|
12280
|
+
"treeFilter": "SD8qLW_treeFilter",
|
|
12281
|
+
"treeDirCount": "SD8qLW_treeDirCount",
|
|
12282
|
+
"stModified": "SD8qLW_stModified",
|
|
12283
|
+
"cardFiles": "SD8qLW_cardFiles",
|
|
12284
|
+
"funnelButtonActive": "SD8qLW_funnelButtonActive",
|
|
12285
|
+
"calWeek": "SD8qLW_calWeek",
|
|
12286
|
+
"tabs": "SD8qLW_tabs",
|
|
12287
|
+
"funnelFootCount": "SD8qLW_funnelFootCount",
|
|
12288
|
+
"fileActive": "SD8qLW_fileActive",
|
|
12289
|
+
"gsFade": "SD8qLW_gsFade",
|
|
12290
|
+
"calIn": "SD8qLW_calIn",
|
|
12291
|
+
"filePath": "SD8qLW_filePath",
|
|
12292
|
+
"funnelBoundVal": "SD8qLW_funnelBoundVal",
|
|
12293
|
+
"refRow": "SD8qLW_refRow",
|
|
12294
|
+
"commitBox": "SD8qLW_commitBox",
|
|
12295
|
+
"calMark": "SD8qLW_calMark",
|
|
12296
|
+
"treeFilterInput": "SD8qLW_treeFilterInput",
|
|
12297
|
+
"commit": "SD8qLW_commit",
|
|
12298
|
+
"btnDanger": "SD8qLW_btnDanger",
|
|
12299
|
+
"commitHasBody": "SD8qLW_commitHasBody",
|
|
12300
|
+
"confirmBox": "SD8qLW_confirmBox",
|
|
11687
12301
|
"commitsFoot": "SD8qLW_commitsFoot",
|
|
12302
|
+
"settingsPop": "SD8qLW_settingsPop",
|
|
12303
|
+
"checkBox": "SD8qLW_checkBox",
|
|
12304
|
+
"diffPre": "SD8qLW_diffPre",
|
|
12305
|
+
"headerView": "SD8qLW_headerView",
|
|
12306
|
+
"commitAuthor": "SD8qLW_commitAuthor",
|
|
12307
|
+
"filterClear": "SD8qLW_filterClear",
|
|
12308
|
+
"treeTools": "SD8qLW_treeTools",
|
|
12309
|
+
"scopeBtnActive": "SD8qLW_scopeBtnActive",
|
|
12310
|
+
"chipLight": "SD8qLW_chipLight",
|
|
12311
|
+
"fileCounts": "SD8qLW_fileCounts",
|
|
12312
|
+
"refPop": "SD8qLW_refPop",
|
|
12313
|
+
"themeGroup": "SD8qLW_themeGroup",
|
|
12314
|
+
"funnelTabCount": "SD8qLW_funnelTabCount",
|
|
12315
|
+
"cardBranchName": "SD8qLW_cardBranchName",
|
|
12316
|
+
"card": "SD8qLW_card",
|
|
12317
|
+
"commitWhen": "SD8qLW_commitWhen",
|
|
12318
|
+
"cal": "SD8qLW_cal",
|
|
12319
|
+
"calOut": "SD8qLW_calOut",
|
|
12320
|
+
"refRowName": "SD8qLW_refRowName",
|
|
12321
|
+
"diffPane": "SD8qLW_diffPane",
|
|
12322
|
+
"headerDetached": "SD8qLW_headerDetached",
|
|
12323
|
+
"drawer": "SD8qLW_drawer",
|
|
12324
|
+
"refRowSpacer": "SD8qLW_refRowSpacer",
|
|
12325
|
+
"calTitle": "SD8qLW_calTitle",
|
|
11688
12326
|
"wordAdd": "SD8qLW_wordAdd",
|
|
11689
|
-
"
|
|
11690
|
-
"
|
|
12327
|
+
"cardDetached": "SD8qLW_cardDetached",
|
|
12328
|
+
"funnelTabs": "SD8qLW_funnelTabs",
|
|
12329
|
+
"funnelFoot": "SD8qLW_funnelFoot",
|
|
12330
|
+
"cssArea": "SD8qLW_cssArea",
|
|
12331
|
+
"gsSlide": "SD8qLW_gsSlide",
|
|
12332
|
+
"elideTail": "SD8qLW_elideTail",
|
|
12333
|
+
"treeCol": "SD8qLW_treeCol",
|
|
12334
|
+
"fileCountAdd": "SD8qLW_fileCountAdd",
|
|
12335
|
+
"stDeleted": "SD8qLW_stDeleted",
|
|
12336
|
+
"commitRow": "SD8qLW_commitRow",
|
|
12337
|
+
"stUntracked": "SD8qLW_stUntracked",
|
|
12338
|
+
"treeSub": "SD8qLW_treeSub",
|
|
12339
|
+
"stRenamed": "SD8qLW_stRenamed",
|
|
12340
|
+
"syncLevel": "SD8qLW_syncLevel",
|
|
11691
12341
|
"elideHead": "SD8qLW_elideHead",
|
|
11692
|
-
"
|
|
11693
|
-
"
|
|
11694
|
-
"
|
|
11695
|
-
"
|
|
11696
|
-
"
|
|
12342
|
+
"compareArrow": "SD8qLW_compareArrow",
|
|
12343
|
+
"chipDark": "SD8qLW_chipDark",
|
|
12344
|
+
"cardWt": "SD8qLW_cardWt",
|
|
12345
|
+
"funnelBoundClear": "SD8qLW_funnelBoundClear",
|
|
12346
|
+
"headerTotalsDim": "SD8qLW_headerTotalsDim",
|
|
12347
|
+
"confirmScrim": "SD8qLW_confirmScrim",
|
|
12348
|
+
"funnelPane": "SD8qLW_funnelPane",
|
|
12349
|
+
"tree": "SD8qLW_tree",
|
|
12350
|
+
"treeIconGlyph": "SD8qLW_treeIconGlyph",
|
|
12351
|
+
"btnCount": "SD8qLW_btnCount",
|
|
12352
|
+
"commitHash": "SD8qLW_commitHash",
|
|
12353
|
+
"headerViewRef": "SD8qLW_headerViewRef",
|
|
12354
|
+
"funnelBoundValSet": "SD8qLW_funnelBoundValSet",
|
|
12355
|
+
"confirmBody": "SD8qLW_confirmBody",
|
|
12356
|
+
"empty": "SD8qLW_empty",
|
|
12357
|
+
"btnPrimary": "SD8qLW_btnPrimary",
|
|
12358
|
+
"fileDiscard": "SD8qLW_fileDiscard",
|
|
12359
|
+
"pathChildren": "SD8qLW_pathChildren",
|
|
12360
|
+
"opBannerOk": "SD8qLW_opBannerOk",
|
|
12361
|
+
"confirmTitle": "SD8qLW_confirmTitle",
|
|
12362
|
+
"funnelCount": "SD8qLW_funnelCount",
|
|
12363
|
+
"cardBranch": "SD8qLW_cardBranch",
|
|
12364
|
+
"segmentActive": "SD8qLW_segmentActive",
|
|
12365
|
+
"lineContext": "SD8qLW_lineContext",
|
|
11697
12366
|
"treeDirName": "SD8qLW_treeDirName",
|
|
11698
|
-
"resizer": "SD8qLW_resizer",
|
|
11699
12367
|
"commitPop": "SD8qLW_commitPop",
|
|
11700
|
-
"
|
|
11701
|
-
"
|
|
11702
|
-
"
|
|
11703
|
-
"
|
|
11704
|
-
"
|
|
11705
|
-
"
|
|
11706
|
-
"
|
|
11707
|
-
"
|
|
11708
|
-
"
|
|
11709
|
-
"
|
|
11710
|
-
"
|
|
11711
|
-
"
|
|
11712
|
-
"
|
|
11713
|
-
"
|
|
11714
|
-
"themeDirty": "SD8qLW_themeDirty",
|
|
12368
|
+
"filterChipLabel": "SD8qLW_filterChipLabel",
|
|
12369
|
+
"commitPopBody": "SD8qLW_commitPopBody",
|
|
12370
|
+
"refRowActive": "SD8qLW_refRowActive",
|
|
12371
|
+
"headerLeft": "SD8qLW_headerLeft",
|
|
12372
|
+
"headerTotals": "SD8qLW_headerTotals",
|
|
12373
|
+
"resizer": "SD8qLW_resizer",
|
|
12374
|
+
"funnel": "SD8qLW_funnel",
|
|
12375
|
+
"funnelBoundRow": "SD8qLW_funnelBoundRow",
|
|
12376
|
+
"refGroup": "SD8qLW_refGroup",
|
|
12377
|
+
"opBanner": "SD8qLW_opBanner",
|
|
12378
|
+
"miniBtnPrimary": "SD8qLW_miniBtnPrimary",
|
|
12379
|
+
"btnIcon": "SD8qLW_btnIcon",
|
|
12380
|
+
"funnelButton": "SD8qLW_funnelButton",
|
|
12381
|
+
"funnelSearch": "SD8qLW_funnelSearch",
|
|
11715
12382
|
"refSearch": "SD8qLW_refSearch",
|
|
11716
|
-
"
|
|
12383
|
+
"commitsPane": "SD8qLW_commitsPane",
|
|
12384
|
+
"lnOld": "SD8qLW_lnOld",
|
|
12385
|
+
"treeIconOn": "SD8qLW_treeIconOn",
|
|
12386
|
+
"headerBranch": "SD8qLW_headerBranch",
|
|
12387
|
+
"swatch": "SD8qLW_swatch",
|
|
12388
|
+
"funnelTab": "SD8qLW_funnelTab",
|
|
12389
|
+
"syncUpstream": "SD8qLW_syncUpstream",
|
|
12390
|
+
"commitLead": "SD8qLW_commitLead",
|
|
12391
|
+
"pullGroup": "SD8qLW_pullGroup",
|
|
12392
|
+
"lineAdd": "SD8qLW_lineAdd",
|
|
12393
|
+
"treeIconDown": "SD8qLW_treeIconDown",
|
|
12394
|
+
"checkMark": "SD8qLW_checkMark",
|
|
11717
12395
|
"commitStaged": "SD8qLW_commitStaged",
|
|
11718
|
-
"
|
|
11719
|
-
"sliderRow": "SD8qLW_sliderRow",
|
|
11720
|
-
"code": "SD8qLW_code",
|
|
11721
|
-
"gsFade": "SD8qLW_gsFade",
|
|
11722
|
-
"tabs": "SD8qLW_tabs",
|
|
11723
|
-
"treeRow": "SD8qLW_treeRow",
|
|
11724
|
-
"refValue": "SD8qLW_refValue",
|
|
11725
|
-
"chipLight": "SD8qLW_chipLight",
|
|
11726
|
-
"commitActive": "SD8qLW_commitActive",
|
|
11727
|
-
"commitSubjectRow": "SD8qLW_commitSubjectRow",
|
|
12396
|
+
"lineHunk": "SD8qLW_lineHunk",
|
|
11728
12397
|
"treeWrap": "SD8qLW_treeWrap",
|
|
11729
|
-
"
|
|
11730
|
-
"
|
|
11731
|
-
"
|
|
11732
|
-
"
|
|
11733
|
-
"
|
|
11734
|
-
"
|
|
11735
|
-
"
|
|
11736
|
-
"
|
|
11737
|
-
"cardBehind": "SD8qLW_cardBehind",
|
|
11738
|
-
"paneDividerActive": "SD8qLW_paneDividerActive",
|
|
11739
|
-
"refRowName": "SD8qLW_refRowName",
|
|
11740
|
-
"commitTop": "SD8qLW_commitTop",
|
|
11741
|
-
"refLabel": "SD8qLW_refLabel",
|
|
11742
|
-
"treeDirCounts": "SD8qLW_treeDirCounts",
|
|
11743
|
-
"checkMarkPartial": "SD8qLW_checkMarkPartial",
|
|
11744
|
-
"pullGroup": "SD8qLW_pullGroup",
|
|
11745
|
-
"cssArea": "SD8qLW_cssArea",
|
|
11746
|
-
"treeLead": "SD8qLW_treeLead",
|
|
11747
|
-
"headerTotalsDim": "SD8qLW_headerTotalsDim",
|
|
11748
|
-
"commitCopy": "SD8qLW_commitCopy",
|
|
12398
|
+
"overlayMax": "SD8qLW_overlayMax",
|
|
12399
|
+
"menuPop": "SD8qLW_menuPop",
|
|
12400
|
+
"tabActive": "SD8qLW_tabActive",
|
|
12401
|
+
"btn": "SD8qLW_btn",
|
|
12402
|
+
"headerTotalsDel": "SD8qLW_headerTotalsDel",
|
|
12403
|
+
"lnNew": "SD8qLW_lnNew",
|
|
12404
|
+
"paneTitle": "SD8qLW_paneTitle",
|
|
12405
|
+
"refCaret": "SD8qLW_refCaret",
|
|
11749
12406
|
"renameLine": "SD8qLW_renameLine",
|
|
11750
|
-
"
|
|
12407
|
+
"scopeRow": "SD8qLW_scopeRow",
|
|
11751
12408
|
"paneDivider": "SD8qLW_paneDivider",
|
|
11752
|
-
"
|
|
11753
|
-
"
|
|
11754
|
-
"
|
|
11755
|
-
"cardFiles": "SD8qLW_cardFiles",
|
|
11756
|
-
"tree": "SD8qLW_tree",
|
|
11757
|
-
"syncSpacer": "SD8qLW_syncSpacer",
|
|
11758
|
-
"refPicker": "SD8qLW_refPicker",
|
|
12409
|
+
"refButton": "SD8qLW_refButton",
|
|
12410
|
+
"cardDeleted": "SD8qLW_cardDeleted",
|
|
12411
|
+
"headerRight": "SD8qLW_headerRight",
|
|
11759
12412
|
"paletteRowActive": "SD8qLW_paletteRowActive",
|
|
11760
|
-
"
|
|
11761
|
-
"refFoot": "SD8qLW_refFoot",
|
|
11762
|
-
"lineContext": "SD8qLW_lineContext",
|
|
11763
|
-
"headerDetached": "SD8qLW_headerDetached",
|
|
11764
|
-
"commitBtn": "SD8qLW_commitBtn",
|
|
11765
|
-
"stModified": "SD8qLW_stModified",
|
|
11766
|
-
"paneTitle": "SD8qLW_paneTitle",
|
|
12413
|
+
"checkMarkPartial": "SD8qLW_checkMarkPartial",
|
|
11767
12414
|
"scopeBtn": "SD8qLW_scopeBtn",
|
|
11768
|
-
"
|
|
11769
|
-
"
|
|
11770
|
-
"
|
|
11771
|
-
"
|
|
11772
|
-
"
|
|
11773
|
-
"
|
|
11774
|
-
"
|
|
11775
|
-
"
|
|
11776
|
-
"
|
|
11777
|
-
"
|
|
11778
|
-
"diffPane": "SD8qLW_diffPane",
|
|
11779
|
-
"syncLevel": "SD8qLW_syncLevel",
|
|
11780
|
-
"syncBar": "SD8qLW_syncBar",
|
|
11781
|
-
"refPop": "SD8qLW_refPop",
|
|
11782
|
-
"segment": "SD8qLW_segment",
|
|
11783
|
-
"theme": "SD8qLW_theme",
|
|
11784
|
-
"chipSystem": "SD8qLW_chipSystem",
|
|
11785
|
-
"wtCurrent": "SD8qLW_wtCurrent",
|
|
11786
|
-
"commitRef": "SD8qLW_commitRef",
|
|
11787
|
-
"scopeHint": "SD8qLW_scopeHint",
|
|
11788
|
-
"bgEmpty": "SD8qLW_bgEmpty",
|
|
11789
|
-
"cardDetached": "SD8qLW_cardDetached",
|
|
11790
|
-
"refEmpty": "SD8qLW_refEmpty",
|
|
11791
|
-
"treeEmpty": "SD8qLW_treeEmpty",
|
|
11792
|
-
"headerRight": "SD8qLW_headerRight",
|
|
11793
|
-
"cardBranchName": "SD8qLW_cardBranchName",
|
|
11794
|
-
"fileBinary": "SD8qLW_fileBinary",
|
|
11795
|
-
"fileCounts": "SD8qLW_fileCounts",
|
|
11796
|
-
"headerPicker": "SD8qLW_headerPicker",
|
|
11797
|
-
"signAdd": "SD8qLW_signAdd",
|
|
11798
|
-
"commitLead": "SD8qLW_commitLead",
|
|
12415
|
+
"lineDel": "SD8qLW_lineDel",
|
|
12416
|
+
"themeDirty": "SD8qLW_themeDirty",
|
|
12417
|
+
"confirmActions": "SD8qLW_confirmActions",
|
|
12418
|
+
"opBannerBad": "SD8qLW_opBannerBad",
|
|
12419
|
+
"commitCopy": "SD8qLW_commitCopy",
|
|
12420
|
+
"sliderRow": "SD8qLW_sliderRow",
|
|
12421
|
+
"fileLi": "SD8qLW_fileLi",
|
|
12422
|
+
"funnelCaption": "SD8qLW_funnelCaption",
|
|
12423
|
+
"body": "SD8qLW_body",
|
|
12424
|
+
"commitPopMeta": "SD8qLW_commitPopMeta",
|
|
11799
12425
|
"commitPopTop": "SD8qLW_commitPopTop",
|
|
11800
12426
|
"tab": "SD8qLW_tab",
|
|
11801
|
-
"
|
|
11802
|
-
"
|
|
11803
|
-
"
|
|
11804
|
-
"
|
|
11805
|
-
"
|
|
12427
|
+
"commitSubject": "SD8qLW_commitSubject",
|
|
12428
|
+
"commitsSentinel": "SD8qLW_commitsSentinel",
|
|
12429
|
+
"cardGlyph": "SD8qLW_cardGlyph",
|
|
12430
|
+
"paneDividerActive": "SD8qLW_paneDividerActive",
|
|
12431
|
+
"funnelRow": "SD8qLW_funnelRow",
|
|
11806
12432
|
"commitRefMore": "SD8qLW_commitRefMore",
|
|
11807
|
-
"
|
|
12433
|
+
"themeLabel": "SD8qLW_themeLabel",
|
|
12434
|
+
"commitBtn": "SD8qLW_commitBtn",
|
|
12435
|
+
"refPicker": "SD8qLW_refPicker",
|
|
12436
|
+
"treeLead": "SD8qLW_treeLead",
|
|
12437
|
+
"funnelBoundBtn": "SD8qLW_funnelBoundBtn",
|
|
12438
|
+
"treeDirLi": "SD8qLW_treeDirLi",
|
|
12439
|
+
"fileStatus": "SD8qLW_fileStatus",
|
|
12440
|
+
"btnClose": "SD8qLW_btnClose",
|
|
12441
|
+
"funnelFootCountOn": "SD8qLW_funnelFootCountOn",
|
|
12442
|
+
"paneHead": "SD8qLW_paneHead",
|
|
12443
|
+
"funnelMore": "SD8qLW_funnelMore",
|
|
12444
|
+
"headerTotalsAdd": "SD8qLW_headerTotalsAdd",
|
|
12445
|
+
"fileCountDel": "SD8qLW_fileCountDel",
|
|
12446
|
+
"bgPreview": "SD8qLW_bgPreview",
|
|
12447
|
+
"cardBehind": "SD8qLW_cardBehind",
|
|
12448
|
+
"header": "SD8qLW_header",
|
|
12449
|
+
"compareBar": "SD8qLW_compareBar",
|
|
12450
|
+
"theme": "SD8qLW_theme",
|
|
12451
|
+
"filterChips": "SD8qLW_filterChips",
|
|
12452
|
+
"cardAhead": "SD8qLW_cardAhead",
|
|
12453
|
+
"filterChip": "SD8qLW_filterChip",
|
|
12454
|
+
"commitSubjectRow": "SD8qLW_commitSubjectRow",
|
|
12455
|
+
"pathDirGlyph": "SD8qLW_pathDirGlyph",
|
|
11808
12456
|
"treeDir": "SD8qLW_treeDir",
|
|
12457
|
+
"commitRef": "SD8qLW_commitRef",
|
|
12458
|
+
"commitLine": "SD8qLW_commitLine",
|
|
12459
|
+
"treeActions": "SD8qLW_treeActions",
|
|
12460
|
+
"cardSep": "SD8qLW_cardSep",
|
|
12461
|
+
"funnelFootClear": "SD8qLW_funnelFootClear",
|
|
12462
|
+
"funnelName": "SD8qLW_funnelName",
|
|
12463
|
+
"chipSystem": "SD8qLW_chipSystem",
|
|
12464
|
+
"treeEmpty": "SD8qLW_treeEmpty",
|
|
12465
|
+
"pathNode": "SD8qLW_pathNode",
|
|
11809
12466
|
"checkMarkOn": "SD8qLW_checkMarkOn",
|
|
11810
|
-
"
|
|
11811
|
-
"
|
|
11812
|
-
"
|
|
11813
|
-
"
|
|
11814
|
-
"
|
|
11815
|
-
"
|
|
12467
|
+
"pathFileGlyph": "SD8qLW_pathFileGlyph",
|
|
12468
|
+
"calToday": "SD8qLW_calToday",
|
|
12469
|
+
"signAdd": "SD8qLW_signAdd",
|
|
12470
|
+
"filterChipRemove": "SD8qLW_filterChipRemove",
|
|
12471
|
+
"calHead": "SD8qLW_calHead",
|
|
12472
|
+
"calGrid": "SD8qLW_calGrid"
|
|
11816
12473
|
};
|
|
11817
12474
|
//#endregion
|
|
11818
12475
|
//#region src/client/GitWorkbenchPanel.tsx
|
|
@@ -12037,7 +12694,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
12037
12694
|
renamed: GitWorkbenchPanel_module_css_default.stRenamed,
|
|
12038
12695
|
deleted: GitWorkbenchPanel_module_css_default.stDeleted
|
|
12039
12696
|
};
|
|
12040
|
-
function GitWorkbenchPanel({ sessionId, useSessions, t, fetchStats, fetchFileDiff, fetchWorktreeStatus, fetchSessionBinding, fetchCommitStats, fetchCommits, fetchCompare, fetchStyle, saveStyle, fetchSync, runGitOp }) {
|
|
12697
|
+
function GitWorkbenchPanel({ sessionId, useSessions, t, fetchStats, fetchFileDiff, fetchWorktreeStatus, fetchSessionBinding, fetchCommitStats, fetchCommits, fetchAuthors, fetchRepoTree, fetchCompare, fetchStyle, saveStyle, fetchSync, runGitOp, fetchDiscardPlan }) {
|
|
12041
12698
|
const worktreePath = useSessions((state) => state?.byId?.[sessionId]?.cwd);
|
|
12042
12699
|
/** Whether the session's agent has a turn in flight — the store mirrors it
|
|
12043
12700
|
* live, so it is the signal for polling faster while there is something to
|
|
@@ -12082,6 +12739,22 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
12082
12739
|
/** First page of the history list in flight — the pane says "loading", not
|
|
12083
12740
|
* "no commit history", which is a claim about the repository. */
|
|
12084
12741
|
const [historyLoading, setHistoryLoading] = (0, react.useState)(false);
|
|
12742
|
+
/** Why the history list is empty when it is git's word, not the log's: a
|
|
12743
|
+
* bad filter pattern or date, with the stderr tail to say so. */
|
|
12744
|
+
const [historyError, setHistoryError] = (0, react.useState)(null);
|
|
12745
|
+
/** The history filter box's raw text. Parsed into the LogFilter the host
|
|
12746
|
+
* compiles into git log arguments — the funnel popup writes here too: one
|
|
12747
|
+
* grammar, one filter, however the criterion arrived. */
|
|
12748
|
+
const [historyQuery, setHistoryQuery] = (0, react.useState)("");
|
|
12749
|
+
const historyFilterKey = serializeLogQuery(parseLogQuery(historyQuery));
|
|
12750
|
+
/** Debounced by KEY, not by text: "liam " and "liam" are the same query and
|
|
12751
|
+
* must not refetch. 300ms is a keystroke's pause, not a page's wait. */
|
|
12752
|
+
const [liveFilterKey, setLiveFilterKey] = (0, react.useState)("");
|
|
12753
|
+
(0, react.useEffect)(() => {
|
|
12754
|
+
const id = window.setTimeout(() => setLiveFilterKey(historyFilterKey), 300);
|
|
12755
|
+
return () => window.clearTimeout(id);
|
|
12756
|
+
}, [historyFilterKey]);
|
|
12757
|
+
const liveFilter = (0, react.useMemo)(() => liveFilterKey.length === 0 ? emptyQueryFilter() : parseLogQuery(liveFilterKey), [liveFilterKey]);
|
|
12085
12758
|
const [loadingMore, setLoadingMore] = (0, react.useState)(false);
|
|
12086
12759
|
/** In-flight marker for paging, read synchronously — see {@link loadMoreCommits}. */
|
|
12087
12760
|
const loadingRef = (0, react.useRef)(false);
|
|
@@ -12409,12 +13082,14 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
12409
13082
|
setCommitHash(null);
|
|
12410
13083
|
setCommitStats(null);
|
|
12411
13084
|
setHistoryLoading(true);
|
|
12412
|
-
|
|
13085
|
+
setHistoryError(null);
|
|
13086
|
+
fetchCommits(statsPath, effectiveHistoryRef, 0, HISTORY_PAGE, liveFilter, ctrl.signal).then((page) => {
|
|
12413
13087
|
if (!alive) return;
|
|
12414
13088
|
setHistoryLoading(false);
|
|
12415
13089
|
if (page === null) return;
|
|
12416
13090
|
setHistoryCommits(page.commits);
|
|
12417
13091
|
setHistoryHasMore(page.hasMore);
|
|
13092
|
+
setHistoryError(page.error ?? null);
|
|
12418
13093
|
}).catch(() => {
|
|
12419
13094
|
if (alive) setHistoryLoading(false);
|
|
12420
13095
|
});
|
|
@@ -12427,7 +13102,8 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
12427
13102
|
statsPath,
|
|
12428
13103
|
effectiveHistoryRef,
|
|
12429
13104
|
fetchCommits,
|
|
12430
|
-
gen
|
|
13105
|
+
gen,
|
|
13106
|
+
liveFilter
|
|
12431
13107
|
]);
|
|
12432
13108
|
(0, react.useEffect)(() => {
|
|
12433
13109
|
if (tab !== "history" || commitHash !== null) return;
|
|
@@ -12718,7 +13394,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
12718
13394
|
loadingRef.current = true;
|
|
12719
13395
|
setLoadingMore(true);
|
|
12720
13396
|
const ctrl = new AbortController();
|
|
12721
|
-
fetchCommits(statsPath, effectiveHistoryRef, historyCommits.length, HISTORY_PAGE, ctrl.signal).then((page) => {
|
|
13397
|
+
fetchCommits(statsPath, effectiveHistoryRef, historyCommits.length, HISTORY_PAGE, liveFilter, ctrl.signal).then((page) => {
|
|
12722
13398
|
if (page === null) return;
|
|
12723
13399
|
setHistoryCommits((prev) => [...prev, ...page.commits]);
|
|
12724
13400
|
setHistoryHasMore(page.hasMore);
|
|
@@ -12760,6 +13436,11 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
12760
13436
|
onLoadMoreCommits: loadMoreCommits,
|
|
12761
13437
|
historyRef: effectiveHistoryRef,
|
|
12762
13438
|
onHistoryRef: setHistoryRef,
|
|
13439
|
+
historyQuery,
|
|
13440
|
+
onHistoryQuery: setHistoryQuery,
|
|
13441
|
+
historyError,
|
|
13442
|
+
fetchAuthors,
|
|
13443
|
+
fetchRepoTree,
|
|
12763
13444
|
branches,
|
|
12764
13445
|
worktreeBranches,
|
|
12765
13446
|
branchesTruncated,
|
|
@@ -12801,6 +13482,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
12801
13482
|
busy,
|
|
12802
13483
|
opResult,
|
|
12803
13484
|
runOp,
|
|
13485
|
+
fetchDiscardPlan,
|
|
12804
13486
|
pendingTicks,
|
|
12805
13487
|
onTick: queueTicks,
|
|
12806
13488
|
fetchFileDiff: fetchDiffForView,
|
|
@@ -12923,7 +13605,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
12923
13605
|
]
|
|
12924
13606
|
});
|
|
12925
13607
|
}
|
|
12926
|
-
function Drawer({ stats, shown, tab, onSwitchTab, commits, commitHash, onSelectCommit, hasMoreCommits, loadingMore, onLoadMoreCommits, historyRef, onHistoryRef, branches, worktreeBranches, branchesTruncated, baseRef, headRef, onBaseRef, onHeadRef, comparable, t, binding, worktrees, sessionPath, statsPath, onSwitchSource, segments, selected, onSelect, maximized, onToggleMaximized, theme, mode, family, onMode, onFamily, style, background, onStyle, width, onWidth, panes, onPane, onClose, onRefresh, commitDraft, onCommitDraft, commitAmend, onCommitAmend, sync, treeLoading, historyLoading, busy, opResult, runOp, pendingTicks, onTick, fetchFileDiff, viewKey, gen, collapsed, onCollapsedChange }) {
|
|
13608
|
+
function Drawer({ stats, shown, tab, onSwitchTab, commits, commitHash, onSelectCommit, hasMoreCommits, loadingMore, onLoadMoreCommits, historyRef, onHistoryRef, historyQuery, onHistoryQuery, historyError, fetchAuthors, fetchRepoTree, branches, worktreeBranches, branchesTruncated, baseRef, headRef, onBaseRef, onHeadRef, comparable, t, binding, worktrees, sessionPath, statsPath, onSwitchSource, segments, selected, onSelect, maximized, onToggleMaximized, theme, mode, family, onMode, onFamily, style, background, onStyle, width, onWidth, panes, onPane, onClose, onRefresh, commitDraft, onCommitDraft, commitAmend, onCommitAmend, sync, treeLoading, historyLoading, busy, opResult, runOp, fetchDiscardPlan, pendingTicks, onTick, fetchFileDiff, viewKey, gen, collapsed, onCollapsedChange }) {
|
|
12927
13609
|
const body = shown ?? EMPTY_STATS;
|
|
12928
13610
|
/** The file list with ticks still awaiting git laid over them. The tree and
|
|
12929
13611
|
* the commit box read this, so a click moves its box and the "N ticked"
|
|
@@ -12934,8 +13616,16 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
12934
13616
|
* while a refresh lands over it. Derived once and handed to both the header
|
|
12935
13617
|
* and the tree: spelling it twice is what let the header get it wrong. */
|
|
12936
13618
|
const pending = showsPending(treeLoading, body.files.length);
|
|
12937
|
-
|
|
13619
|
+
/** The history filter's paths, which decide what a commit OPENS on. Only the
|
|
13620
|
+
* history tab has one: the changes and compare trees are not filtered, and
|
|
13621
|
+
* steering their default selection by a query the reader cannot see from
|
|
13622
|
+
* there would be a spooky action. */
|
|
13623
|
+
const activeFilterPaths = (0, react.useMemo)(() => tab === "history" ? parseLogQuery(historyQuery).paths : NO_PATHS, [tab, historyQuery]);
|
|
13624
|
+
const active = preferredFile(body.files, activeFilterPaths, selected);
|
|
12938
13625
|
const activeFile = body.files.find((file) => file.path === active) ?? null;
|
|
13626
|
+
/** The file whose roll-back is being asked about; `plan` is null while the
|
|
13627
|
+
* host is still being asked what it would do. */
|
|
13628
|
+
const [discardPending, setDiscardPending] = (0, react.useState)(null);
|
|
12939
13629
|
const [fetched, setFetched] = (0, react.useState)(/* @__PURE__ */ new Map());
|
|
12940
13630
|
const [loading, setLoading] = (0, react.useState)(false);
|
|
12941
13631
|
const bundled = active === null ? "" : segments.get(active) ?? "";
|
|
@@ -12967,6 +13657,55 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
12967
13657
|
setFetched(/* @__PURE__ */ new Map());
|
|
12968
13658
|
}, [gen]);
|
|
12969
13659
|
const selectAndReveal = (path) => onSelect(path);
|
|
13660
|
+
/**
|
|
13661
|
+
* Roll-back, in two steps that are deliberately not one.
|
|
13662
|
+
*
|
|
13663
|
+
* The click asks the host what rolling this file back would DO, and only the
|
|
13664
|
+
* answer opens the dialog. Deriving the wording from the clicked row instead
|
|
13665
|
+
* would mean describing a file as the last poll saw it: the difference
|
|
13666
|
+
* between "goes back to its committed content" and "leaves the disk and
|
|
13667
|
+
* cannot come back" is the entire subject of the question being asked, and it
|
|
13668
|
+
* is exactly the thing a stale row gets wrong.
|
|
13669
|
+
*
|
|
13670
|
+
* `recover` — a deleted file coming back — shows no dialog at all. It loses
|
|
13671
|
+
* nothing, and a confirmation in front of a pure gain is how people learn to
|
|
13672
|
+
* dismiss confirmations without reading them.
|
|
13673
|
+
*/
|
|
13674
|
+
const askDiscard = (file) => {
|
|
13675
|
+
setDiscardPending({
|
|
13676
|
+
file,
|
|
13677
|
+
plan: null
|
|
13678
|
+
});
|
|
13679
|
+
(async () => {
|
|
13680
|
+
const preview = await fetchDiscardPlan(statsPath, file.path, new AbortController().signal);
|
|
13681
|
+
if (preview === null || preview.effect === void 0) {
|
|
13682
|
+
setDiscardPending(null);
|
|
13683
|
+
onRefresh();
|
|
13684
|
+
return;
|
|
13685
|
+
}
|
|
13686
|
+
if (preview.irreversible !== true) {
|
|
13687
|
+
setDiscardPending(null);
|
|
13688
|
+
runOp("discardFile", {
|
|
13689
|
+
path: file.path,
|
|
13690
|
+
expectedEffect: preview.effect
|
|
13691
|
+
});
|
|
13692
|
+
return;
|
|
13693
|
+
}
|
|
13694
|
+
setDiscardPending({
|
|
13695
|
+
file,
|
|
13696
|
+
plan: preview
|
|
13697
|
+
});
|
|
13698
|
+
})();
|
|
13699
|
+
};
|
|
13700
|
+
const confirmDiscard = () => {
|
|
13701
|
+
const pending = discardPending;
|
|
13702
|
+
if (pending === null || pending.plan === null) return;
|
|
13703
|
+
setDiscardPending(null);
|
|
13704
|
+
runOp("discardFile", {
|
|
13705
|
+
path: pending.file.path,
|
|
13706
|
+
expectedEffect: pending.plan.effect
|
|
13707
|
+
});
|
|
13708
|
+
};
|
|
12970
13709
|
const drawerRef = (0, react.useRef)(null);
|
|
12971
13710
|
const commitsRef = (0, react.useRef)(null);
|
|
12972
13711
|
const treeRef = (0, react.useRef)(null);
|
|
@@ -13202,7 +13941,8 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
13202
13941
|
branches,
|
|
13203
13942
|
worktreeBranches,
|
|
13204
13943
|
truncated: branchesTruncated,
|
|
13205
|
-
onPick: onHistoryRef
|
|
13944
|
+
onPick: onHistoryRef,
|
|
13945
|
+
allLabel: t("allBranches")
|
|
13206
13946
|
})
|
|
13207
13947
|
}) : null,
|
|
13208
13948
|
tab === "changes" && sync !== null && sync.hasRemote ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SyncBar, {
|
|
@@ -13231,7 +13971,14 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
13231
13971
|
onSelect: onSelectCommit,
|
|
13232
13972
|
hasMore: hasMoreCommits,
|
|
13233
13973
|
loadingMore,
|
|
13234
|
-
onLoadMore: onLoadMoreCommits
|
|
13974
|
+
onLoadMore: onLoadMoreCommits,
|
|
13975
|
+
query: historyQuery,
|
|
13976
|
+
onQueryChange: onHistoryQuery,
|
|
13977
|
+
error: historyError,
|
|
13978
|
+
statsPath,
|
|
13979
|
+
refName: historyRef,
|
|
13980
|
+
fetchAuthors,
|
|
13981
|
+
fetchRepoTree
|
|
13235
13982
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PaneDivider, {
|
|
13236
13983
|
label: t("resizeCommits"),
|
|
13237
13984
|
onDrag: paneDrag("commits", commitsRef)
|
|
@@ -13243,6 +13990,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
13243
13990
|
"data-gs-part": "tree",
|
|
13244
13991
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FileTree, {
|
|
13245
13992
|
t,
|
|
13993
|
+
scopeKey: viewKey,
|
|
13246
13994
|
loading: pending,
|
|
13247
13995
|
lead: tab === "changes" ? t("workingTree") : void 0,
|
|
13248
13996
|
files: tickedFiles,
|
|
@@ -13255,6 +14003,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
13255
14003
|
const paths = pathsFor(checked, action);
|
|
13256
14004
|
if (paths.length > 0) onTick(action, paths);
|
|
13257
14005
|
} : void 0,
|
|
14006
|
+
onDiscard: tab === "changes" ? askDiscard : void 0,
|
|
13258
14007
|
footer: tab === "changes" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CommitBox, {
|
|
13259
14008
|
t,
|
|
13260
14009
|
files: tickedFiles,
|
|
@@ -13303,6 +14052,86 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
13303
14052
|
})]
|
|
13304
14053
|
})
|
|
13305
14054
|
]
|
|
14055
|
+
}),
|
|
14056
|
+
discardPending?.plan != null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DiscardConfirm, {
|
|
14057
|
+
t,
|
|
14058
|
+
file: discardPending.file,
|
|
14059
|
+
plan: discardPending.plan,
|
|
14060
|
+
onCancel: () => setDiscardPending(null),
|
|
14061
|
+
onConfirm: confirmDiscard
|
|
14062
|
+
}) : null
|
|
14063
|
+
]
|
|
14064
|
+
})
|
|
14065
|
+
});
|
|
14066
|
+
}
|
|
14067
|
+
/**
|
|
14068
|
+
* The one dialog in this drawer, because this is the one act it cannot undo.
|
|
14069
|
+
*
|
|
14070
|
+
* It never asks a generic "are you sure": the body names the file and states
|
|
14071
|
+
* which of the three consequences is about to happen, in the host's own reading
|
|
14072
|
+
* of that file taken moments ago. Cancel holds the initial focus and Escape
|
|
14073
|
+
* closes, because the default answer to an irreversible question is no.
|
|
14074
|
+
*
|
|
14075
|
+
* There is deliberately no "don't ask again". This is the only path in the
|
|
14076
|
+
* drawer with nothing behind it, and a checkbox whose whole function is to
|
|
14077
|
+
* switch off the last guard is a feature that eventually gets clicked.
|
|
14078
|
+
*/
|
|
14079
|
+
function DiscardConfirm({ t, file, plan, onCancel, onConfirm }) {
|
|
14080
|
+
const cancelRef = (0, react.useRef)(null);
|
|
14081
|
+
(0, react.useEffect)(() => {
|
|
14082
|
+
cancelRef.current?.focus();
|
|
14083
|
+
}, []);
|
|
14084
|
+
(0, react.useEffect)(() => {
|
|
14085
|
+
const onKey = (event) => {
|
|
14086
|
+
if (event.key !== "Escape") return;
|
|
14087
|
+
event.stopPropagation();
|
|
14088
|
+
onCancel();
|
|
14089
|
+
};
|
|
14090
|
+
window.addEventListener("keydown", onKey, true);
|
|
14091
|
+
return () => {
|
|
14092
|
+
window.removeEventListener("keydown", onKey, true);
|
|
14093
|
+
};
|
|
14094
|
+
}, [onCancel]);
|
|
14095
|
+
const body = plan.effect === "delete" ? t("discardBodyDelete", { path: file.path }) : plan.effect === "unrename" ? t("discardBodyUnrename", {
|
|
14096
|
+
path: file.path,
|
|
14097
|
+
previousPath: plan.previousPath ?? ""
|
|
14098
|
+
}) : t("discardBodyRestore", {
|
|
14099
|
+
path: file.path,
|
|
14100
|
+
added: file.addedLines,
|
|
14101
|
+
deleted: file.deletedLines
|
|
14102
|
+
});
|
|
14103
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
14104
|
+
className: GitWorkbenchPanel_module_css_default.confirmScrim,
|
|
14105
|
+
onClick: onCancel,
|
|
14106
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
14107
|
+
className: GitWorkbenchPanel_module_css_default.confirmBox,
|
|
14108
|
+
role: "alertdialog",
|
|
14109
|
+
"aria-modal": "true",
|
|
14110
|
+
"aria-label": t("discardTitle"),
|
|
14111
|
+
onClick: (event) => event.stopPropagation(),
|
|
14112
|
+
children: [
|
|
14113
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
14114
|
+
className: GitWorkbenchPanel_module_css_default.confirmTitle,
|
|
14115
|
+
children: t("discardTitle")
|
|
14116
|
+
}),
|
|
14117
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
14118
|
+
className: GitWorkbenchPanel_module_css_default.confirmBody,
|
|
14119
|
+
children: body
|
|
14120
|
+
}),
|
|
14121
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
14122
|
+
className: GitWorkbenchPanel_module_css_default.confirmActions,
|
|
14123
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
14124
|
+
ref: cancelRef,
|
|
14125
|
+
type: "button",
|
|
14126
|
+
className: GitWorkbenchPanel_module_css_default.btn,
|
|
14127
|
+
onClick: onCancel,
|
|
14128
|
+
children: t("discardCancel")
|
|
14129
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
14130
|
+
type: "button",
|
|
14131
|
+
className: `${GitWorkbenchPanel_module_css_default.btn} ${GitWorkbenchPanel_module_css_default.btnDanger}`,
|
|
14132
|
+
onClick: onConfirm,
|
|
14133
|
+
children: t("discardConfirm")
|
|
14134
|
+
})]
|
|
13306
14135
|
})
|
|
13307
14136
|
]
|
|
13308
14137
|
})
|
|
@@ -13835,7 +14664,12 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
13835
14664
|
* checked-out branch is the likeliest thing to want. Enter takes the first
|
|
13836
14665
|
* match, so a distinctive substring plus Enter reaches any branch in the list.
|
|
13837
14666
|
*/
|
|
13838
|
-
|
|
14667
|
+
/** Sentinel ref meaning "walk every ref" — same string the host special-cases
|
|
14668
|
+
* into `--all`. A real ref cannot begin with a dash, so it collides with
|
|
14669
|
+
* nothing; defined separately on both halves (client bundles import no host
|
|
14670
|
+
* values), tied by this comment and the probe. */
|
|
14671
|
+
const ALL_REFS = "--all";
|
|
14672
|
+
function RefPicker({ t, label, value, branches, worktreeBranches, truncated, onPick, allLabel }) {
|
|
13839
14673
|
const [open, setOpen] = (0, react.useState)(false);
|
|
13840
14674
|
const [query, setQuery] = (0, react.useState)("");
|
|
13841
14675
|
const rootRef = useDismissable(open, setOpen);
|
|
@@ -13876,7 +14710,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
13876
14710
|
title: value.length > 0 ? value : void 0,
|
|
13877
14711
|
onClick: () => setOpen((isOpen) => !isOpen),
|
|
13878
14712
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Elided, {
|
|
13879
|
-
text: value.length > 0 ? value : "—",
|
|
14713
|
+
text: value === ALL_REFS && allLabel !== void 0 ? allLabel : value.length > 0 ? value : "—",
|
|
13880
14714
|
className: GitWorkbenchPanel_module_css_default.refValue
|
|
13881
14715
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
13882
14716
|
className: GitWorkbenchPanel_module_css_default.refCaret,
|
|
@@ -13901,6 +14735,18 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
13901
14735
|
role: "listbox",
|
|
13902
14736
|
"aria-label": label,
|
|
13903
14737
|
children: [
|
|
14738
|
+
allLabel !== void 0 && (needle.length === 0 || allLabel.toLowerCase().includes(needle)) ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
14739
|
+
type: "button",
|
|
14740
|
+
role: "option",
|
|
14741
|
+
"aria-selected": value === ALL_REFS,
|
|
14742
|
+
className: value === ALL_REFS ? `${GitWorkbenchPanel_module_css_default.refRow} ${GitWorkbenchPanel_module_css_default.refRowActive}` : GitWorkbenchPanel_module_css_default.refRow,
|
|
14743
|
+
title: allLabel,
|
|
14744
|
+
onClick: () => choose(ALL_REFS),
|
|
14745
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: GitWorkbenchPanel_module_css_default.refRowSpacer }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Elided, {
|
|
14746
|
+
text: allLabel,
|
|
14747
|
+
className: GitWorkbenchPanel_module_css_default.refRowName
|
|
14748
|
+
})]
|
|
14749
|
+
}) : null,
|
|
13904
14750
|
checkedOut.length > 0 && rest.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
13905
14751
|
className: GitWorkbenchPanel_module_css_default.refGroup,
|
|
13906
14752
|
children: t("refWorktrees")
|
|
@@ -13911,7 +14757,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
13911
14757
|
children: t("refBranches")
|
|
13912
14758
|
}) : null,
|
|
13913
14759
|
rest.map((ref) => row(ref, false)),
|
|
13914
|
-
matched.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
14760
|
+
matched.length === 0 && !(allLabel !== void 0 && needle.length > 0 && allLabel.toLowerCase().includes(needle)) ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
13915
14761
|
className: GitWorkbenchPanel_module_css_default.refEmpty,
|
|
13916
14762
|
children: t("refNone")
|
|
13917
14763
|
}) : null
|
|
@@ -14014,6 +14860,53 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
14014
14860
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: SYNC_GLYPH[of] })
|
|
14015
14861
|
});
|
|
14016
14862
|
}
|
|
14863
|
+
/**
|
|
14864
|
+
* Filter this list: a magnifier, not the funnel above the commit list. The two
|
|
14865
|
+
* are deliberately different glyphs because they do different things — the
|
|
14866
|
+
* funnel asks git for a different set of commits, this only hides rows already
|
|
14867
|
+
* on screen — and the drawer shows both at once.
|
|
14868
|
+
*/
|
|
14869
|
+
function FilterGlyph() {
|
|
14870
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
|
|
14871
|
+
width: "13",
|
|
14872
|
+
height: "13",
|
|
14873
|
+
viewBox: "0 0 16 16",
|
|
14874
|
+
fill: "none",
|
|
14875
|
+
stroke: "currentColor",
|
|
14876
|
+
strokeWidth: "1.25",
|
|
14877
|
+
strokeLinecap: "round",
|
|
14878
|
+
strokeLinejoin: "round",
|
|
14879
|
+
"aria-hidden": "true",
|
|
14880
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("circle", {
|
|
14881
|
+
cx: "7",
|
|
14882
|
+
cy: "7",
|
|
14883
|
+
r: "4"
|
|
14884
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M10 10l3.5 3.5" })]
|
|
14885
|
+
});
|
|
14886
|
+
}
|
|
14887
|
+
/** Nothing folded. A constant so the filtered tree does not allocate a new Set
|
|
14888
|
+
* on every render and re-run `TreeChildren`'s memo. */
|
|
14889
|
+
const EMPTY_COLLAPSED = /* @__PURE__ */ new Set();
|
|
14890
|
+
/**
|
|
14891
|
+
* Roll back: the counter-clockwise arc every editor and VCS uses for undo,
|
|
14892
|
+
* drawn in the same New UI idiom as the node glyphs beside it — 16px grid,
|
|
14893
|
+
* 1px stroke, no fill — so the row does not mix an outlined file icon with a
|
|
14894
|
+
* solid action icon.
|
|
14895
|
+
*/
|
|
14896
|
+
function RollbackGlyph() {
|
|
14897
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
|
|
14898
|
+
width: "14",
|
|
14899
|
+
height: "14",
|
|
14900
|
+
viewBox: "0 0 16 16",
|
|
14901
|
+
fill: "none",
|
|
14902
|
+
stroke: "currentColor",
|
|
14903
|
+
strokeWidth: "1.25",
|
|
14904
|
+
strokeLinecap: "round",
|
|
14905
|
+
strokeLinejoin: "round",
|
|
14906
|
+
"aria-hidden": "true",
|
|
14907
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M3.5 6.5a5 5 0 1 0 1.9-2.2" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M2.6 3.2v3.4h3.4" })]
|
|
14908
|
+
});
|
|
14909
|
+
}
|
|
14017
14910
|
const PULL_MODES = [
|
|
14018
14911
|
"ff-only",
|
|
14019
14912
|
"rebase",
|
|
@@ -14388,6 +15281,9 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
14388
15281
|
const enterTimer = (0, react.useRef)(0);
|
|
14389
15282
|
const leaveTimer = (0, react.useRef)(0);
|
|
14390
15283
|
const body = commit.body ?? "";
|
|
15284
|
+
const authorName = commit.authorName ?? "";
|
|
15285
|
+
const committerName = commit.committerName ?? "";
|
|
15286
|
+
const exactDate = formatCommitDate(commit.dateIso ?? "");
|
|
14391
15287
|
const cancel = () => {
|
|
14392
15288
|
window.clearTimeout(enterTimer.current);
|
|
14393
15289
|
window.clearTimeout(leaveTimer.current);
|
|
@@ -14438,13 +15334,20 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
14438
15334
|
onMouseLeave: hide,
|
|
14439
15335
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
14440
15336
|
className: GitWorkbenchPanel_module_css_default.commitTop,
|
|
14441
|
-
children: [
|
|
14442
|
-
|
|
14443
|
-
|
|
14444
|
-
|
|
14445
|
-
|
|
14446
|
-
|
|
14447
|
-
|
|
15337
|
+
children: [
|
|
15338
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", {
|
|
15339
|
+
className: GitWorkbenchPanel_module_css_default.commitHash,
|
|
15340
|
+
children: commit.hash
|
|
15341
|
+
}),
|
|
15342
|
+
authorName.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
15343
|
+
className: GitWorkbenchPanel_module_css_default.commitAuthor,
|
|
15344
|
+
children: authorName
|
|
15345
|
+
}) : null,
|
|
15346
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
15347
|
+
className: GitWorkbenchPanel_module_css_default.commitWhen,
|
|
15348
|
+
children: commit.when
|
|
15349
|
+
})
|
|
15350
|
+
]
|
|
14448
15351
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
14449
15352
|
className: GitWorkbenchPanel_module_css_default.commitSubjectRow,
|
|
14450
15353
|
children: [
|
|
@@ -14495,22 +15398,249 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
14495
15398
|
className: GitWorkbenchPanel_module_css_default.commitWhen,
|
|
14496
15399
|
children: commit.when
|
|
14497
15400
|
}),
|
|
14498
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopyCommitButton, {
|
|
14499
|
-
t,
|
|
14500
|
-
text: commitMessageText(commit)
|
|
15401
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopyCommitButton, {
|
|
15402
|
+
t,
|
|
15403
|
+
text: commitMessageText(commit)
|
|
15404
|
+
})
|
|
15405
|
+
]
|
|
15406
|
+
}),
|
|
15407
|
+
authorName.length > 0 || committerName.length > 0 || exactDate.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
15408
|
+
className: GitWorkbenchPanel_module_css_default.commitPopMeta,
|
|
15409
|
+
children: [
|
|
15410
|
+
authorName.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
|
|
15411
|
+
t("commitAuthor"),
|
|
15412
|
+
": ",
|
|
15413
|
+
authorName
|
|
15414
|
+
] }) : null,
|
|
15415
|
+
committerName.length > 0 && committerName !== authorName ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
|
|
15416
|
+
t("commitCommitter"),
|
|
15417
|
+
": ",
|
|
15418
|
+
committerName
|
|
15419
|
+
] }) : null,
|
|
15420
|
+
exactDate.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
|
|
15421
|
+
t("commitDate"),
|
|
15422
|
+
": ",
|
|
15423
|
+
exactDate
|
|
15424
|
+
] }) : null
|
|
15425
|
+
]
|
|
15426
|
+
}) : null,
|
|
15427
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
15428
|
+
className: GitWorkbenchPanel_module_css_default.commitPopSubject,
|
|
15429
|
+
children: commit.subject
|
|
15430
|
+
}),
|
|
15431
|
+
body.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
|
|
15432
|
+
className: GitWorkbenchPanel_module_css_default.commitPopBody,
|
|
15433
|
+
children: body
|
|
15434
|
+
}) : null
|
|
15435
|
+
]
|
|
15436
|
+
}), host) : null] });
|
|
15437
|
+
}
|
|
15438
|
+
/** The filter's own calendar — a hand-rolled 6×7 Monday-first grid (pure
|
|
15439
|
+
* arithmetic in `calendar.ts`), because the native date input renders as the
|
|
15440
|
+
* platform's bare widget and the bundle's purity gate forbids pulling in a
|
|
15441
|
+
* library. Picking a day hands `yyyy-mm-dd` to the bound the segmented
|
|
15442
|
+
* control armed; the host expands it to the whole day. */
|
|
15443
|
+
function FilterCalendar({ year, month, after, before, locale, onPick, onShift }) {
|
|
15444
|
+
const grid = monthGrid(year, month, localTodayIso());
|
|
15445
|
+
const title = new Intl.DateTimeFormat(locale, {
|
|
15446
|
+
year: "numeric",
|
|
15447
|
+
month: "long"
|
|
15448
|
+
}).format(new Date(year, month, 1));
|
|
15449
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
15450
|
+
className: GitWorkbenchPanel_module_css_default.cal,
|
|
15451
|
+
children: [
|
|
15452
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
15453
|
+
className: GitWorkbenchPanel_module_css_default.calHead,
|
|
15454
|
+
children: [
|
|
15455
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
15456
|
+
type: "button",
|
|
15457
|
+
className: GitWorkbenchPanel_module_css_default.calNav,
|
|
15458
|
+
"aria-label": "‹",
|
|
15459
|
+
onClick: () => onShift(-1),
|
|
15460
|
+
children: "‹"
|
|
15461
|
+
}),
|
|
15462
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
15463
|
+
className: GitWorkbenchPanel_module_css_default.calTitle,
|
|
15464
|
+
children: title
|
|
15465
|
+
}),
|
|
15466
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
15467
|
+
type: "button",
|
|
15468
|
+
className: GitWorkbenchPanel_module_css_default.calNav,
|
|
15469
|
+
"aria-label": "›",
|
|
15470
|
+
onClick: () => onShift(1),
|
|
15471
|
+
children: "›"
|
|
15472
|
+
})
|
|
15473
|
+
]
|
|
15474
|
+
}),
|
|
15475
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
15476
|
+
className: GitWorkbenchPanel_module_css_default.calWeek,
|
|
15477
|
+
children: weekdayLabels(locale).map((label, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: label }, index))
|
|
15478
|
+
}),
|
|
15479
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
15480
|
+
className: GitWorkbenchPanel_module_css_default.calGrid,
|
|
15481
|
+
children: grid.flat().map((cell) => cell === null ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
15482
|
+
type: "button",
|
|
15483
|
+
"aria-label": cell.iso,
|
|
15484
|
+
title: cell.iso,
|
|
15485
|
+
className: [
|
|
15486
|
+
cell.inMonth ? "" : GitWorkbenchPanel_module_css_default.calOut,
|
|
15487
|
+
cell.isToday ? GitWorkbenchPanel_module_css_default.calToday : "",
|
|
15488
|
+
inCalRange(cell.iso, after, before) ? GitWorkbenchPanel_module_css_default.calIn : "",
|
|
15489
|
+
cell.iso === after || cell.iso === before ? GitWorkbenchPanel_module_css_default.calMark : ""
|
|
15490
|
+
].filter((cls) => cls.length > 0).join(" "),
|
|
15491
|
+
onClick: () => onPick(cell.iso),
|
|
15492
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: cell.day })
|
|
15493
|
+
}, cell.iso))
|
|
15494
|
+
})
|
|
15495
|
+
]
|
|
15496
|
+
});
|
|
15497
|
+
}
|
|
15498
|
+
/**
|
|
15499
|
+
* The two node glyphs, in IntelliJ's New UI icon idiom: a 16px grid, 1px
|
|
15500
|
+
* strokes, no fill, rounded joins — outlines, where the old UI shipped filled
|
|
15501
|
+
* silhouettes. Hand-drawn here rather than imported, because the bundle purity
|
|
15502
|
+
* gate forbids an icon package and the drawer needs exactly these two; they
|
|
15503
|
+
* are shapes in that language, not JetBrains' own assets.
|
|
15504
|
+
*
|
|
15505
|
+
* `strokeWidth` is 1 against a viewBox that renders 1:1 at 16px, so every
|
|
15506
|
+
* stroke lands on a whole pixel instead of straddling two.
|
|
15507
|
+
*
|
|
15508
|
+
* Every place the drawer names a file or a directory uses these: the path
|
|
15509
|
+
* picker in the history filter, and the file tree behind all three tabs. The
|
|
15510
|
+
* CLASS names keep their `path` prefix — `scripts/verify_history_feature.py`
|
|
15511
|
+
* selects the picker's file rows by `label:has([class*="pathFileGlyph"])`.
|
|
15512
|
+
*/
|
|
15513
|
+
function PathDirGlyph() {
|
|
15514
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
|
|
15515
|
+
className: GitWorkbenchPanel_module_css_default.pathDirGlyph,
|
|
15516
|
+
width: "16",
|
|
15517
|
+
height: "16",
|
|
15518
|
+
viewBox: "0 0 16 16",
|
|
15519
|
+
fill: "none",
|
|
15520
|
+
stroke: "currentColor",
|
|
15521
|
+
strokeWidth: "1",
|
|
15522
|
+
strokeLinejoin: "round",
|
|
15523
|
+
strokeLinecap: "round",
|
|
15524
|
+
"aria-hidden": "true",
|
|
15525
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M2.5 12.75V4.25A.75.75 0 0 1 3.25 3.5H6l1.6 2h5.15A.75.75 0 0 1 13.5 6.25v6.5a.75.75 0 0 1-.75.75H3.25a.75.75 0 0 1-.75-.75Z" })
|
|
15526
|
+
});
|
|
15527
|
+
}
|
|
15528
|
+
function PathFileGlyph() {
|
|
15529
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
|
|
15530
|
+
className: GitWorkbenchPanel_module_css_default.pathFileGlyph,
|
|
15531
|
+
width: "16",
|
|
15532
|
+
height: "16",
|
|
15533
|
+
viewBox: "0 0 16 16",
|
|
15534
|
+
fill: "none",
|
|
15535
|
+
stroke: "currentColor",
|
|
15536
|
+
strokeWidth: "1",
|
|
15537
|
+
strokeLinejoin: "round",
|
|
15538
|
+
strokeLinecap: "round",
|
|
15539
|
+
"aria-hidden": "true",
|
|
15540
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M3.5 12.75V3.25A.75.75 0 0 1 4.25 2.5H9l3.5 3.5v6.75a.75.75 0 0 1-.75.75H4.25a.75.75 0 0 1-.75-.75Z" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M9 2.5v2.75a.75.75 0 0 0 .75.75h2.75" })]
|
|
15541
|
+
});
|
|
15542
|
+
}
|
|
15543
|
+
/** Files shown per expanded directory. The search box is the way to a file in
|
|
15544
|
+
* a crowded directory; the tree shows enough to browse without flooding the
|
|
15545
|
+
* list, and says so when it cut the tail. */
|
|
15546
|
+
const PATH_FILES_SHOWN = 100;
|
|
15547
|
+
/** Horizontal step per nesting level in the path picker. The whole indent now
|
|
15548
|
+
* comes from this one number: `.pathChildren` used to add a margin and a rail
|
|
15549
|
+
* of its own on top of it, so every level cost 29px and a 320px popover ran
|
|
15550
|
+
* out of width three directories deep. */
|
|
15551
|
+
const PATH_INDENT = 14;
|
|
15552
|
+
/** One level of the path picker's directory tree — directories (chevron,
|
|
15553
|
+
* subtree count) then their files (doc glyph, leaf rows). Collapsed subtrees
|
|
15554
|
+
* are not in the DOM at all, so a monorepo costs only what the reader has
|
|
15555
|
+
* opened. */
|
|
15556
|
+
/** A checkbox that also carries the tree's third state — `indeterminate` is a
|
|
15557
|
+
* DOM property, not an attribute, so it is set through the ref. */
|
|
15558
|
+
function TriStateCheckbox({ state, onChange, ariaLabel }) {
|
|
15559
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
15560
|
+
type: "checkbox",
|
|
15561
|
+
"aria-label": ariaLabel,
|
|
15562
|
+
checked: state === "on",
|
|
15563
|
+
ref: (el) => {
|
|
15564
|
+
if (el !== null) el.indeterminate = state === "partial";
|
|
15565
|
+
},
|
|
15566
|
+
onChange
|
|
15567
|
+
});
|
|
15568
|
+
}
|
|
15569
|
+
function PathTreeRows({ dirs, depth, expanded, stateOf, onToggleOpen, onTogglePath }) {
|
|
15570
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: dirs.map((dir) => {
|
|
15571
|
+
const open = expanded.includes(dir.path);
|
|
15572
|
+
const expandable = dir.children.length > 0 || dir.files.length > 0;
|
|
15573
|
+
const shown = dir.files.slice(0, PATH_FILES_SHOWN);
|
|
15574
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
15575
|
+
className: GitWorkbenchPanel_module_css_default.pathNode,
|
|
15576
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
15577
|
+
className: GitWorkbenchPanel_module_css_default.funnelRow,
|
|
15578
|
+
style: { paddingLeft: depth * PATH_INDENT + 4 },
|
|
15579
|
+
children: [
|
|
15580
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
15581
|
+
type: "button",
|
|
15582
|
+
className: GitWorkbenchPanel_module_css_default.funnelChevron,
|
|
15583
|
+
disabled: !expandable,
|
|
15584
|
+
"aria-expanded": open,
|
|
15585
|
+
onClick: () => onToggleOpen(dir.path),
|
|
15586
|
+
children: expandable ? open ? "▾" : "▸" : ""
|
|
15587
|
+
}),
|
|
15588
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(TriStateCheckbox, {
|
|
15589
|
+
state: stateOf(dir.path),
|
|
15590
|
+
ariaLabel: dir.path,
|
|
15591
|
+
onChange: () => onTogglePath(dir.path)
|
|
15592
|
+
}),
|
|
15593
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(PathDirGlyph, {}),
|
|
15594
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
15595
|
+
className: GitWorkbenchPanel_module_css_default.funnelName,
|
|
15596
|
+
title: dir.path,
|
|
15597
|
+
children: dir.name
|
|
15598
|
+
}),
|
|
15599
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
15600
|
+
className: GitWorkbenchPanel_module_css_default.funnelCount,
|
|
15601
|
+
children: dir.fileCount
|
|
14501
15602
|
})
|
|
14502
15603
|
]
|
|
14503
|
-
}),
|
|
14504
|
-
|
|
14505
|
-
|
|
14506
|
-
|
|
14507
|
-
|
|
14508
|
-
|
|
14509
|
-
|
|
14510
|
-
|
|
14511
|
-
|
|
14512
|
-
|
|
14513
|
-
|
|
15604
|
+
}), open ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
15605
|
+
className: GitWorkbenchPanel_module_css_default.pathChildren,
|
|
15606
|
+
children: [
|
|
15607
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(PathTreeRows, {
|
|
15608
|
+
dirs: dir.children,
|
|
15609
|
+
depth: depth + 1,
|
|
15610
|
+
expanded,
|
|
15611
|
+
stateOf,
|
|
15612
|
+
onToggleOpen,
|
|
15613
|
+
onTogglePath
|
|
15614
|
+
}),
|
|
15615
|
+
shown.map((file) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
15616
|
+
className: GitWorkbenchPanel_module_css_default.funnelRow,
|
|
15617
|
+
style: { paddingLeft: (depth + 1) * PATH_INDENT + 4 },
|
|
15618
|
+
children: [
|
|
15619
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
15620
|
+
className: GitWorkbenchPanel_module_css_default.funnelChevron,
|
|
15621
|
+
"aria-hidden": "true"
|
|
15622
|
+
}),
|
|
15623
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(TriStateCheckbox, {
|
|
15624
|
+
state: stateOf(`${dir.path}/${file}`),
|
|
15625
|
+
ariaLabel: `${dir.path}/${file}`,
|
|
15626
|
+
onChange: () => onTogglePath(`${dir.path}/${file}`)
|
|
15627
|
+
}),
|
|
15628
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(PathFileGlyph, {}),
|
|
15629
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
15630
|
+
className: GitWorkbenchPanel_module_css_default.funnelName,
|
|
15631
|
+
title: `${dir.path}/${file}`,
|
|
15632
|
+
children: file
|
|
15633
|
+
})
|
|
15634
|
+
]
|
|
15635
|
+
}, file)),
|
|
15636
|
+
dir.files.length > PATH_FILES_SHOWN ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
15637
|
+
className: GitWorkbenchPanel_module_css_default.funnelMore,
|
|
15638
|
+
children: ["+", dir.files.length - PATH_FILES_SHOWN]
|
|
15639
|
+
}) : null
|
|
15640
|
+
]
|
|
15641
|
+
}) : null]
|
|
15642
|
+
}, dir.path);
|
|
15643
|
+
}) });
|
|
14514
15644
|
}
|
|
14515
15645
|
/**
|
|
14516
15646
|
* The commit log as its own full-height pane.
|
|
@@ -14530,9 +15660,131 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
14530
15660
|
* what GitHub and GitLens do. The observer is rebuilt whenever the list grows,
|
|
14531
15661
|
* so a page too short to fill the pane immediately triggers the next one.
|
|
14532
15662
|
*/
|
|
14533
|
-
function CommitList({ paneRef, style, t, loading, commits, active, onSelect, hasMore, loadingMore, onLoadMore }) {
|
|
15663
|
+
function CommitList({ paneRef, style, t, loading, commits, active, onSelect, hasMore, loadingMore, onLoadMore, query, onQueryChange, error, statsPath, refName, fetchAuthors, fetchRepoTree }) {
|
|
14534
15664
|
const scrollRef = (0, react.useRef)(null);
|
|
14535
15665
|
const sentinelRef = (0, react.useRef)(null);
|
|
15666
|
+
const filterModel = (0, react.useMemo)(() => parseLogQuery(query), [query]);
|
|
15667
|
+
const chips = chipsFromFilter(filterModel);
|
|
15668
|
+
const dateCount = (filterModel.after.length > 0 ? 1 : 0) + (filterModel.before.length > 0 ? 1 : 0);
|
|
15669
|
+
const selectedCount = filterModel.users.length + filterModel.paths.length + dateCount;
|
|
15670
|
+
const [funnelOpen, setFunnelOpen] = (0, react.useState)(false);
|
|
15671
|
+
const [authors, setAuthors] = (0, react.useState)(null);
|
|
15672
|
+
const [authorsQuery, setAuthorsQuery] = (0, react.useState)("");
|
|
15673
|
+
const [pathTree, setPathTree] = (0, react.useState)(null);
|
|
15674
|
+
const [expandedDirs, setExpandedDirs] = (0, react.useState)([]);
|
|
15675
|
+
const [pathsQuery, setPathsQuery] = (0, react.useState)("");
|
|
15676
|
+
const [funnelSection, setFunnelSection] = (0, react.useState)("users");
|
|
15677
|
+
const [calMonth, setCalMonth] = (0, react.useState)(() => {
|
|
15678
|
+
const now = /* @__PURE__ */ new Date();
|
|
15679
|
+
return {
|
|
15680
|
+
year: now.getFullYear(),
|
|
15681
|
+
month: now.getMonth()
|
|
15682
|
+
};
|
|
15683
|
+
});
|
|
15684
|
+
const [calBound, setCalBound] = (0, react.useState)("after");
|
|
15685
|
+
const funnelAnchorRef = (0, react.useRef)(null);
|
|
15686
|
+
const funnelPanelRef = (0, react.useRef)(null);
|
|
15687
|
+
const [funnelBox, setFunnelBox] = (0, react.useState)(null);
|
|
15688
|
+
(0, react.useEffect)(() => {
|
|
15689
|
+
if (!funnelOpen) {
|
|
15690
|
+
setFunnelBox(null);
|
|
15691
|
+
return;
|
|
15692
|
+
}
|
|
15693
|
+
const onDown = (event) => {
|
|
15694
|
+
const target = event.target;
|
|
15695
|
+
if (funnelAnchorRef.current?.contains(target) === true) return;
|
|
15696
|
+
if (funnelPanelRef.current?.contains(target) === true) return;
|
|
15697
|
+
setFunnelOpen(false);
|
|
15698
|
+
};
|
|
15699
|
+
const onKey = (event) => {
|
|
15700
|
+
if (event.key === "Escape") setFunnelOpen(false);
|
|
15701
|
+
};
|
|
15702
|
+
const id = window.setTimeout(() => document.addEventListener("mousedown", onDown), 0);
|
|
15703
|
+
document.addEventListener("keydown", onKey);
|
|
15704
|
+
return () => {
|
|
15705
|
+
window.clearTimeout(id);
|
|
15706
|
+
document.removeEventListener("mousedown", onDown);
|
|
15707
|
+
document.removeEventListener("keydown", onKey);
|
|
15708
|
+
};
|
|
15709
|
+
}, [funnelOpen]);
|
|
15710
|
+
(0, react.useEffect)(() => {
|
|
15711
|
+
if (!funnelOpen) return;
|
|
15712
|
+
const rect = funnelAnchorRef.current?.getBoundingClientRect();
|
|
15713
|
+
if (rect === void 0) return;
|
|
15714
|
+
const width = 300;
|
|
15715
|
+
const left = Math.max(12, Math.min(rect.left + rect.width - width, window.innerWidth - width - 12));
|
|
15716
|
+
const top = rect.bottom + 4;
|
|
15717
|
+
setFunnelBox({
|
|
15718
|
+
top,
|
|
15719
|
+
left,
|
|
15720
|
+
maxHeight: Math.max(160, window.innerHeight - top - 16)
|
|
15721
|
+
});
|
|
15722
|
+
}, [funnelOpen]);
|
|
15723
|
+
(0, react.useEffect)(() => {
|
|
15724
|
+
if (!funnelOpen) return;
|
|
15725
|
+
const ctrl = new AbortController();
|
|
15726
|
+
setAuthors(null);
|
|
15727
|
+
setPathTree(null);
|
|
15728
|
+
fetchAuthors(statsPath, refName, ctrl.signal).then((roster) => {
|
|
15729
|
+
if (!ctrl.signal.aborted) setAuthors(roster);
|
|
15730
|
+
}).catch(() => {});
|
|
15731
|
+
fetchRepoTree(statsPath, ctrl.signal).then((tree) => {
|
|
15732
|
+
if (!ctrl.signal.aborted && tree !== null) setPathTree({
|
|
15733
|
+
dirs: buildDirTree(tree.paths),
|
|
15734
|
+
paths: tree.paths,
|
|
15735
|
+
truncated: tree.truncated
|
|
15736
|
+
});
|
|
15737
|
+
}).catch(() => {});
|
|
15738
|
+
return () => {
|
|
15739
|
+
ctrl.abort();
|
|
15740
|
+
};
|
|
15741
|
+
}, [
|
|
15742
|
+
funnelOpen,
|
|
15743
|
+
statsPath,
|
|
15744
|
+
refName,
|
|
15745
|
+
fetchAuthors,
|
|
15746
|
+
fetchRepoTree
|
|
15747
|
+
]);
|
|
15748
|
+
/** Every funnel interaction writes the filter through the box's grammar, so
|
|
15749
|
+
* the box, the chips and the fetch can never disagree about the query. */
|
|
15750
|
+
const applyFilter = (next) => {
|
|
15751
|
+
onQueryChange(serializeLogQuery(next));
|
|
15752
|
+
};
|
|
15753
|
+
const toggleUser = (name) => {
|
|
15754
|
+
const has = filterModel.users.includes(name);
|
|
15755
|
+
applyFilter({
|
|
15756
|
+
...filterModel,
|
|
15757
|
+
users: has ? filterModel.users.filter((user) => user !== name) : [...filterModel.users, name]
|
|
15758
|
+
});
|
|
15759
|
+
};
|
|
15760
|
+
const pathIndex = (0, react.useMemo)(() => pathTree === null ? null : buildIndex(pathTree.paths), [pathTree]);
|
|
15761
|
+
const pathState = (path) => pathIndex === null ? "off" : checkedState(filterModel.paths, path, pathIndex);
|
|
15762
|
+
const togglePath = (path) => {
|
|
15763
|
+
if (pathIndex === null) return;
|
|
15764
|
+
applyFilter({
|
|
15765
|
+
...filterModel,
|
|
15766
|
+
paths: isCovered(filterModel.paths, path) ? removePath(filterModel.paths, path, pathIndex) : addPath(filterModel.paths, path)
|
|
15767
|
+
});
|
|
15768
|
+
};
|
|
15769
|
+
const toggleDirOpen = (path) => {
|
|
15770
|
+
setExpandedDirs((prev) => prev.includes(path) ? prev.filter((p) => p !== path) : [...prev, path]);
|
|
15771
|
+
};
|
|
15772
|
+
const needle = authorsQuery.trim().toLowerCase();
|
|
15773
|
+
const matchedAuthors = authors === null ? [] : needle.length === 0 ? authors.authors : authors.authors.filter((entry) => entry.name.toLowerCase().includes(needle) || entry.email.toLowerCase().includes(needle));
|
|
15774
|
+
const DATE_PRESETS = [
|
|
15775
|
+
{
|
|
15776
|
+
key: "filterToday",
|
|
15777
|
+
value: "midnight"
|
|
15778
|
+
},
|
|
15779
|
+
{
|
|
15780
|
+
key: "filterLast7",
|
|
15781
|
+
value: "1 week ago"
|
|
15782
|
+
},
|
|
15783
|
+
{
|
|
15784
|
+
key: "filterLast30",
|
|
15785
|
+
value: "30 days ago"
|
|
15786
|
+
}
|
|
15787
|
+
];
|
|
14536
15788
|
const graph = (0, react.useMemo)(() => layoutGraph(commits.map((commit) => ({
|
|
14537
15789
|
hash: commit.hash,
|
|
14538
15790
|
parents: commit.parents ?? []
|
|
@@ -14562,39 +15814,353 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
14562
15814
|
className: GitWorkbenchPanel_module_css_default.commitsPane,
|
|
14563
15815
|
style,
|
|
14564
15816
|
"data-gs-part": "commits",
|
|
14565
|
-
children: [
|
|
14566
|
-
|
|
14567
|
-
|
|
14568
|
-
|
|
14569
|
-
|
|
15817
|
+
children: [
|
|
15818
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
15819
|
+
className: GitWorkbenchPanel_module_css_default.paneHead,
|
|
15820
|
+
children: [
|
|
15821
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
15822
|
+
className: GitWorkbenchPanel_module_css_default.paneTitle,
|
|
15823
|
+
children: t("historyLabel")
|
|
15824
|
+
}),
|
|
15825
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
15826
|
+
className: GitWorkbenchPanel_module_css_default.funnel,
|
|
15827
|
+
ref: funnelAnchorRef,
|
|
15828
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
15829
|
+
type: "button",
|
|
15830
|
+
className: funnelOpen || chips.length > 0 ? `${GitWorkbenchPanel_module_css_default.funnelButton} ${GitWorkbenchPanel_module_css_default.funnelButtonActive}` : GitWorkbenchPanel_module_css_default.funnelButton,
|
|
15831
|
+
"aria-expanded": funnelOpen,
|
|
15832
|
+
onClick: () => setFunnelOpen((isOpen) => !isOpen),
|
|
15833
|
+
children: [t("filterBy"), " ▾"]
|
|
15834
|
+
})
|
|
15835
|
+
}),
|
|
15836
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
15837
|
+
className: GitWorkbenchPanel_module_css_default.commitFilter,
|
|
15838
|
+
type: "search",
|
|
15839
|
+
value: query,
|
|
15840
|
+
onChange: (event) => onQueryChange(event.target.value),
|
|
15841
|
+
placeholder: t("historyFilterPlaceholder"),
|
|
15842
|
+
"aria-label": t("historyFilterPlaceholder"),
|
|
15843
|
+
spellCheck: false
|
|
15844
|
+
})
|
|
15845
|
+
]
|
|
15846
|
+
}),
|
|
15847
|
+
funnelOpen && funnelBox !== null ? (0, react_dom.createPortal)(/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
15848
|
+
ref: funnelPanelRef,
|
|
15849
|
+
className: GitWorkbenchPanel_module_css_default.funnelPop,
|
|
15850
|
+
style: funnelBox,
|
|
15851
|
+
role: "dialog",
|
|
15852
|
+
"aria-label": t("filterBy"),
|
|
15853
|
+
children: [
|
|
15854
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
15855
|
+
className: GitWorkbenchPanel_module_css_default.funnelTabs,
|
|
15856
|
+
role: "tablist",
|
|
15857
|
+
children: [
|
|
15858
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
15859
|
+
type: "button",
|
|
15860
|
+
role: "tab",
|
|
15861
|
+
"aria-selected": funnelSection === "users",
|
|
15862
|
+
className: funnelSection === "users" ? `${GitWorkbenchPanel_module_css_default.funnelTab} ${GitWorkbenchPanel_module_css_default.funnelTabActive}` : GitWorkbenchPanel_module_css_default.funnelTab,
|
|
15863
|
+
onClick: () => setFunnelSection("users"),
|
|
15864
|
+
children: [t("filterUsers"), filterModel.users.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
15865
|
+
className: GitWorkbenchPanel_module_css_default.funnelTabCount,
|
|
15866
|
+
children: filterModel.users.length
|
|
15867
|
+
}) : null]
|
|
15868
|
+
}),
|
|
15869
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
15870
|
+
type: "button",
|
|
15871
|
+
role: "tab",
|
|
15872
|
+
"aria-selected": funnelSection === "date",
|
|
15873
|
+
className: funnelSection === "date" ? `${GitWorkbenchPanel_module_css_default.funnelTab} ${GitWorkbenchPanel_module_css_default.funnelTabActive}` : GitWorkbenchPanel_module_css_default.funnelTab,
|
|
15874
|
+
onClick: () => setFunnelSection("date"),
|
|
15875
|
+
children: [t("filterDate"), dateCount > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
15876
|
+
className: GitWorkbenchPanel_module_css_default.funnelTabCount,
|
|
15877
|
+
children: dateCount
|
|
15878
|
+
}) : null]
|
|
15879
|
+
}),
|
|
15880
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
15881
|
+
type: "button",
|
|
15882
|
+
role: "tab",
|
|
15883
|
+
"aria-selected": funnelSection === "paths",
|
|
15884
|
+
className: funnelSection === "paths" ? `${GitWorkbenchPanel_module_css_default.funnelTab} ${GitWorkbenchPanel_module_css_default.funnelTabActive}` : GitWorkbenchPanel_module_css_default.funnelTab,
|
|
15885
|
+
onClick: () => setFunnelSection("paths"),
|
|
15886
|
+
children: [t("filterPaths"), filterModel.paths.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
15887
|
+
className: GitWorkbenchPanel_module_css_default.funnelTabCount,
|
|
15888
|
+
children: filterModel.paths.length
|
|
15889
|
+
}) : null]
|
|
15890
|
+
})
|
|
15891
|
+
]
|
|
15892
|
+
}),
|
|
15893
|
+
funnelSection === "users" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
15894
|
+
className: GitWorkbenchPanel_module_css_default.funnelPane,
|
|
15895
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
15896
|
+
className: GitWorkbenchPanel_module_css_default.funnelSearch,
|
|
15897
|
+
type: "search",
|
|
15898
|
+
value: authorsQuery,
|
|
15899
|
+
onChange: (event) => setAuthorsQuery(event.target.value),
|
|
15900
|
+
placeholder: t("filterUserSearch"),
|
|
15901
|
+
"aria-label": t("filterUserSearch"),
|
|
15902
|
+
spellCheck: false
|
|
15903
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
15904
|
+
className: GitWorkbenchPanel_module_css_default.funnelList,
|
|
15905
|
+
children: [authors === null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
15906
|
+
className: GitWorkbenchPanel_module_css_default.funnelMore,
|
|
15907
|
+
children: t("loading")
|
|
15908
|
+
}) : matchedAuthors.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
15909
|
+
className: GitWorkbenchPanel_module_css_default.funnelMore,
|
|
15910
|
+
children: authors.authors.length === 0 ? t("noCommits") : t("historyNoMatch")
|
|
15911
|
+
}) : matchedAuthors.map((entry) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
15912
|
+
className: GitWorkbenchPanel_module_css_default.funnelRow,
|
|
15913
|
+
children: [
|
|
15914
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
15915
|
+
type: "checkbox",
|
|
15916
|
+
checked: filterModel.users.includes(entry.name),
|
|
15917
|
+
onChange: () => toggleUser(entry.name)
|
|
15918
|
+
}),
|
|
15919
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
15920
|
+
className: GitWorkbenchPanel_module_css_default.funnelName,
|
|
15921
|
+
title: `${entry.name} <${entry.email}>`,
|
|
15922
|
+
children: entry.name
|
|
15923
|
+
}),
|
|
15924
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
15925
|
+
className: GitWorkbenchPanel_module_css_default.funnelCount,
|
|
15926
|
+
children: entry.count
|
|
15927
|
+
})
|
|
15928
|
+
]
|
|
15929
|
+
}, `${entry.name}\x1f${entry.email}`)), authors?.truncated === true ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
15930
|
+
className: GitWorkbenchPanel_module_css_default.funnelMore,
|
|
15931
|
+
children: t("filterAuthorsMore")
|
|
15932
|
+
}) : null]
|
|
15933
|
+
})]
|
|
15934
|
+
}) : null,
|
|
15935
|
+
funnelSection === "date" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
15936
|
+
className: GitWorkbenchPanel_module_css_default.funnelPane,
|
|
15937
|
+
children: [
|
|
15938
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
15939
|
+
className: GitWorkbenchPanel_module_css_default.funnelPresets,
|
|
15940
|
+
children: DATE_PRESETS.map((preset) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
15941
|
+
type: "button",
|
|
15942
|
+
className: filterModel.after === preset.value ? `${GitWorkbenchPanel_module_css_default.funnelPreset} ${GitWorkbenchPanel_module_css_default.funnelPresetActive}` : GitWorkbenchPanel_module_css_default.funnelPreset,
|
|
15943
|
+
onClick: () => applyFilter({
|
|
15944
|
+
...filterModel,
|
|
15945
|
+
after: filterModel.after === preset.value ? "" : preset.value
|
|
15946
|
+
}),
|
|
15947
|
+
children: t(preset.key)
|
|
15948
|
+
}, preset.key))
|
|
15949
|
+
}),
|
|
15950
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
15951
|
+
className: GitWorkbenchPanel_module_css_default.funnelCaption,
|
|
15952
|
+
children: t("filterCalendarSets")
|
|
15953
|
+
}),
|
|
15954
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
15955
|
+
className: GitWorkbenchPanel_module_css_default.funnelBounds,
|
|
15956
|
+
role: "group",
|
|
15957
|
+
"aria-label": t("filterCalendarSets"),
|
|
15958
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
15959
|
+
type: "button",
|
|
15960
|
+
"aria-pressed": calBound === "after",
|
|
15961
|
+
className: calBound === "after" ? `${GitWorkbenchPanel_module_css_default.funnelBoundBtn} ${GitWorkbenchPanel_module_css_default.funnelBoundBtnActive}` : GitWorkbenchPanel_module_css_default.funnelBoundBtn,
|
|
15962
|
+
onClick: () => setCalBound("after"),
|
|
15963
|
+
children: t("filterAfter")
|
|
15964
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
15965
|
+
type: "button",
|
|
15966
|
+
"aria-pressed": calBound === "before",
|
|
15967
|
+
className: calBound === "before" ? `${GitWorkbenchPanel_module_css_default.funnelBoundBtn} ${GitWorkbenchPanel_module_css_default.funnelBoundBtnActive}` : GitWorkbenchPanel_module_css_default.funnelBoundBtn,
|
|
15968
|
+
onClick: () => setCalBound("before"),
|
|
15969
|
+
children: t("filterBefore")
|
|
15970
|
+
})]
|
|
15971
|
+
}),
|
|
15972
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(FilterCalendar, {
|
|
15973
|
+
year: calMonth.year,
|
|
15974
|
+
month: calMonth.month,
|
|
15975
|
+
after: filterModel.after,
|
|
15976
|
+
before: filterModel.before,
|
|
15977
|
+
locale: t("filterLocale"),
|
|
15978
|
+
onPick: (iso) => applyFilter({
|
|
15979
|
+
...filterModel,
|
|
15980
|
+
[calBound]: iso
|
|
15981
|
+
}),
|
|
15982
|
+
onShift: (delta) => setCalMonth((current) => {
|
|
15983
|
+
const next = new Date(current.year, current.month + delta, 1);
|
|
15984
|
+
return {
|
|
15985
|
+
year: next.getFullYear(),
|
|
15986
|
+
month: next.getMonth()
|
|
15987
|
+
};
|
|
15988
|
+
})
|
|
15989
|
+
}),
|
|
15990
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
15991
|
+
className: GitWorkbenchPanel_module_css_default.funnelBoundRows,
|
|
15992
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
15993
|
+
className: GitWorkbenchPanel_module_css_default.funnelBoundRow,
|
|
15994
|
+
children: [
|
|
15995
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
15996
|
+
className: GitWorkbenchPanel_module_css_default.funnelBoundKey,
|
|
15997
|
+
children: t("filterAfter")
|
|
15998
|
+
}),
|
|
15999
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
16000
|
+
className: filterModel.after.length > 0 ? `${GitWorkbenchPanel_module_css_default.funnelBoundVal} ${GitWorkbenchPanel_module_css_default.funnelBoundValSet}` : GitWorkbenchPanel_module_css_default.funnelBoundVal,
|
|
16001
|
+
children: filterModel.after.length > 0 ? filterModel.after : "—"
|
|
16002
|
+
}),
|
|
16003
|
+
filterModel.after.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
16004
|
+
type: "button",
|
|
16005
|
+
className: GitWorkbenchPanel_module_css_default.funnelBoundClear,
|
|
16006
|
+
"aria-label": t("filterAfter"),
|
|
16007
|
+
onClick: () => applyFilter({
|
|
16008
|
+
...filterModel,
|
|
16009
|
+
after: ""
|
|
16010
|
+
}),
|
|
16011
|
+
children: "×"
|
|
16012
|
+
}) : null
|
|
16013
|
+
]
|
|
16014
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
16015
|
+
className: GitWorkbenchPanel_module_css_default.funnelBoundRow,
|
|
16016
|
+
children: [
|
|
16017
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
16018
|
+
className: GitWorkbenchPanel_module_css_default.funnelBoundKey,
|
|
16019
|
+
children: t("filterBefore")
|
|
16020
|
+
}),
|
|
16021
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
16022
|
+
className: filterModel.before.length > 0 ? `${GitWorkbenchPanel_module_css_default.funnelBoundVal} ${GitWorkbenchPanel_module_css_default.funnelBoundValSet}` : GitWorkbenchPanel_module_css_default.funnelBoundVal,
|
|
16023
|
+
children: filterModel.before.length > 0 ? filterModel.before : "—"
|
|
16024
|
+
}),
|
|
16025
|
+
filterModel.before.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
16026
|
+
type: "button",
|
|
16027
|
+
className: GitWorkbenchPanel_module_css_default.funnelBoundClear,
|
|
16028
|
+
"aria-label": t("filterBefore"),
|
|
16029
|
+
onClick: () => applyFilter({
|
|
16030
|
+
...filterModel,
|
|
16031
|
+
before: ""
|
|
16032
|
+
}),
|
|
16033
|
+
children: "×"
|
|
16034
|
+
}) : null
|
|
16035
|
+
]
|
|
16036
|
+
})]
|
|
16037
|
+
})
|
|
16038
|
+
]
|
|
16039
|
+
}) : null,
|
|
16040
|
+
funnelSection === "paths" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
16041
|
+
className: GitWorkbenchPanel_module_css_default.funnelPane,
|
|
16042
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
16043
|
+
className: GitWorkbenchPanel_module_css_default.funnelSearch,
|
|
16044
|
+
type: "search",
|
|
16045
|
+
value: pathsQuery,
|
|
16046
|
+
onChange: (event) => setPathsQuery(event.target.value),
|
|
16047
|
+
placeholder: t("filterPathSearch"),
|
|
16048
|
+
"aria-label": t("filterPathSearch"),
|
|
16049
|
+
spellCheck: false
|
|
16050
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
16051
|
+
className: GitWorkbenchPanel_module_css_default.funnelList,
|
|
16052
|
+
children: [pathTree === null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
16053
|
+
className: GitWorkbenchPanel_module_css_default.funnelMore,
|
|
16054
|
+
children: t("loading")
|
|
16055
|
+
}) : pathsQuery.trim().length > 0 ? (() => {
|
|
16056
|
+
const hits = searchPaths(pathTree.paths, pathsQuery).slice(0, 200);
|
|
16057
|
+
if (hits.length === 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
16058
|
+
className: GitWorkbenchPanel_module_css_default.funnelMore,
|
|
16059
|
+
children: t("historyNoMatch")
|
|
16060
|
+
});
|
|
16061
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [hits.map((hit) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
16062
|
+
className: GitWorkbenchPanel_module_css_default.funnelRow,
|
|
16063
|
+
children: [
|
|
16064
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(TriStateCheckbox, {
|
|
16065
|
+
state: pathState(hit.path),
|
|
16066
|
+
ariaLabel: hit.path,
|
|
16067
|
+
onChange: () => togglePath(hit.path)
|
|
16068
|
+
}),
|
|
16069
|
+
hit.isFile ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PathFileGlyph, {}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PathDirGlyph, {}),
|
|
16070
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
16071
|
+
className: GitWorkbenchPanel_module_css_default.funnelName,
|
|
16072
|
+
title: hit.path,
|
|
16073
|
+
children: hit.path
|
|
16074
|
+
})
|
|
16075
|
+
]
|
|
16076
|
+
}, hit.path)), searchPaths(pathTree.paths, pathsQuery).length > 200 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
16077
|
+
className: GitWorkbenchPanel_module_css_default.funnelMore,
|
|
16078
|
+
children: t("filterPathsMore")
|
|
16079
|
+
}) : null] });
|
|
16080
|
+
})() : pathTree.dirs.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
16081
|
+
className: GitWorkbenchPanel_module_css_default.funnelMore,
|
|
16082
|
+
children: t("noCommits")
|
|
16083
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PathTreeRows, {
|
|
16084
|
+
dirs: pathTree.dirs,
|
|
16085
|
+
depth: 0,
|
|
16086
|
+
expanded: expandedDirs,
|
|
16087
|
+
stateOf: pathState,
|
|
16088
|
+
onToggleOpen: toggleDirOpen,
|
|
16089
|
+
onTogglePath: togglePath
|
|
16090
|
+
}), pathTree?.truncated === true ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
16091
|
+
className: GitWorkbenchPanel_module_css_default.funnelMore,
|
|
16092
|
+
children: t("filterPathsMore")
|
|
16093
|
+
}) : null]
|
|
16094
|
+
})]
|
|
16095
|
+
}) : null,
|
|
16096
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
16097
|
+
className: GitWorkbenchPanel_module_css_default.funnelFoot,
|
|
16098
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
16099
|
+
className: selectedCount > 0 ? `${GitWorkbenchPanel_module_css_default.funnelFootCount} ${GitWorkbenchPanel_module_css_default.funnelFootCountOn}` : GitWorkbenchPanel_module_css_default.funnelFootCount,
|
|
16100
|
+
children: t("filterSelected", { count: selectedCount })
|
|
16101
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
16102
|
+
type: "button",
|
|
16103
|
+
className: GitWorkbenchPanel_module_css_default.funnelFootClear,
|
|
16104
|
+
disabled: selectedCount === 0,
|
|
16105
|
+
onClick: () => onQueryChange(""),
|
|
16106
|
+
children: t("filterClearAll")
|
|
16107
|
+
})]
|
|
16108
|
+
})
|
|
16109
|
+
]
|
|
16110
|
+
}), funnelAnchorRef.current?.closest("[data-gs-part=\"overlay\"]") ?? (typeof document === "undefined" ? null : document.body)) : null,
|
|
16111
|
+
chips.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
16112
|
+
className: GitWorkbenchPanel_module_css_default.filterChips,
|
|
16113
|
+
children: [chips.map((chip) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
16114
|
+
className: GitWorkbenchPanel_module_css_default.filterChip,
|
|
16115
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
16116
|
+
className: GitWorkbenchPanel_module_css_default.filterChipLabel,
|
|
16117
|
+
children: [
|
|
16118
|
+
chip.kind,
|
|
16119
|
+
":",
|
|
16120
|
+
chip.value
|
|
16121
|
+
]
|
|
16122
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
16123
|
+
type: "button",
|
|
16124
|
+
className: GitWorkbenchPanel_module_css_default.filterChipRemove,
|
|
16125
|
+
"aria-label": `${chip.kind} ${chip.value}`,
|
|
16126
|
+
onClick: () => onQueryChange(serializeLogQuery(removeChip(filterModel, chip.kind, chip.value))),
|
|
16127
|
+
children: "×"
|
|
16128
|
+
})]
|
|
16129
|
+
}, `${chip.kind}\x1f${chip.value}`)), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
16130
|
+
type: "button",
|
|
16131
|
+
className: GitWorkbenchPanel_module_css_default.filterClear,
|
|
16132
|
+
onClick: () => onQueryChange(""),
|
|
16133
|
+
children: t("filterClearAll")
|
|
16134
|
+
})]
|
|
16135
|
+
}) : null,
|
|
16136
|
+
commits.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
16137
|
+
className: GitWorkbenchPanel_module_css_default.empty,
|
|
16138
|
+
children: loading ? t("loading") : error !== null ? error : chips.length > 0 ? t("historyNoMatch") : t("noCommits")
|
|
16139
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
16140
|
+
className: GitWorkbenchPanel_module_css_default.commits,
|
|
16141
|
+
role: "listbox",
|
|
16142
|
+
"aria-label": t("historyLabel"),
|
|
16143
|
+
ref: scrollRef,
|
|
16144
|
+
children: [
|
|
16145
|
+
commits.map((commit, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CommitRow, {
|
|
16146
|
+
t,
|
|
16147
|
+
commit,
|
|
16148
|
+
active: commit.hash === active,
|
|
16149
|
+
onSelect,
|
|
16150
|
+
graphRow: graph.rows[index],
|
|
16151
|
+
graphWidth: graph.width
|
|
16152
|
+
}, commit.hash)),
|
|
16153
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
16154
|
+
ref: sentinelRef,
|
|
16155
|
+
className: GitWorkbenchPanel_module_css_default.commitsSentinel
|
|
16156
|
+
}),
|
|
16157
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
16158
|
+
className: GitWorkbenchPanel_module_css_default.commitsFoot,
|
|
16159
|
+
children: loadingMore ? t("loading") : hasMore ? "" : t("historyEnd")
|
|
16160
|
+
})
|
|
16161
|
+
]
|
|
14570
16162
|
})
|
|
14571
|
-
|
|
14572
|
-
className: GitWorkbenchPanel_module_css_default.empty,
|
|
14573
|
-
children: loading ? t("loading") : t("noCommits")
|
|
14574
|
-
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
14575
|
-
className: GitWorkbenchPanel_module_css_default.commits,
|
|
14576
|
-
role: "listbox",
|
|
14577
|
-
"aria-label": t("historyLabel"),
|
|
14578
|
-
ref: scrollRef,
|
|
14579
|
-
children: [
|
|
14580
|
-
commits.map((commit, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CommitRow, {
|
|
14581
|
-
t,
|
|
14582
|
-
commit,
|
|
14583
|
-
active: commit.hash === active,
|
|
14584
|
-
onSelect,
|
|
14585
|
-
graphRow: graph.rows[index],
|
|
14586
|
-
graphWidth: graph.width
|
|
14587
|
-
}, commit.hash)),
|
|
14588
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
14589
|
-
ref: sentinelRef,
|
|
14590
|
-
className: GitWorkbenchPanel_module_css_default.commitsSentinel
|
|
14591
|
-
}),
|
|
14592
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
14593
|
-
className: GitWorkbenchPanel_module_css_default.commitsFoot,
|
|
14594
|
-
children: loadingMore ? t("loading") : hasMore ? "" : t("historyEnd")
|
|
14595
|
-
})
|
|
14596
|
-
]
|
|
14597
|
-
})]
|
|
16163
|
+
]
|
|
14598
16164
|
});
|
|
14599
16165
|
}
|
|
14600
16166
|
/** Horizontal step per nesting level. */
|
|
@@ -14697,8 +16263,23 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
14697
16263
|
dirs
|
|
14698
16264
|
};
|
|
14699
16265
|
}
|
|
14700
|
-
function FileTree({ t, loading, lead, files, active, onSelect, collapsed, onCollapsedChange, onCheck, footer }) {
|
|
14701
|
-
|
|
16266
|
+
function FileTree({ t, loading, lead, files, active, onSelect, collapsed, onCollapsedChange, onCheck, onDiscard, footer, scopeKey }) {
|
|
16267
|
+
/**
|
|
16268
|
+
* The filter over this list. Local, because it describes a way of LOOKING at
|
|
16269
|
+
* the pane rather than anything the drawer stores: closing and reopening on
|
|
16270
|
+
* an unfiltered list is what someone expects, and a query kept in the panel
|
|
16271
|
+
* would have to be cleared from four places instead of one.
|
|
16272
|
+
*/
|
|
16273
|
+
const [query, setQuery] = (0, react.useState)("");
|
|
16274
|
+
const [filterOpen, setFilterOpen] = (0, react.useState)(false);
|
|
16275
|
+
const filterRef = (0, react.useRef)(null);
|
|
16276
|
+
(0, react.useEffect)(() => {
|
|
16277
|
+
setQuery("");
|
|
16278
|
+
setFilterOpen(false);
|
|
16279
|
+
}, [scopeKey]);
|
|
16280
|
+
const shownFiles = (0, react.useMemo)(() => filterFiles(files, query), [files, query]);
|
|
16281
|
+
const filtering = shownFiles !== files;
|
|
16282
|
+
const tree = (0, react.useMemo)(() => buildTree(shownFiles), [shownFiles]);
|
|
14702
16283
|
/** Default: a dir collapses when it holds more than 12 files anywhere below it. */
|
|
14703
16284
|
const effective = collapsed ?? defaultCollapsed(tree);
|
|
14704
16285
|
(0, react.useEffect)(() => {
|
|
@@ -14736,54 +16317,117 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
14736
16317
|
state: tree.check,
|
|
14737
16318
|
label: tree.check === "on" ? t("unstageAll") : t("stageAll"),
|
|
14738
16319
|
indent: 0,
|
|
14739
|
-
onToggle: () => onCheck(
|
|
16320
|
+
onToggle: () => onCheck(shownFiles, tree.check)
|
|
14740
16321
|
}) : null, /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
14741
16322
|
className: GitWorkbenchPanel_module_css_default.treeLabel,
|
|
14742
|
-
children: loading === true ? t("loading") : `${lead !== void 0 ? `${lead} · ` : ""}${t("
|
|
16323
|
+
children: loading === true ? t("loading") : `${lead !== void 0 ? `${lead} · ` : ""}${filtering ? t("filesFiltered", {
|
|
16324
|
+
shown: shownFiles.length,
|
|
16325
|
+
count: files.length
|
|
16326
|
+
}) : t("files", { count: files.length })}`
|
|
14743
16327
|
})]
|
|
14744
16328
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
14745
16329
|
className: GitWorkbenchPanel_module_css_default.treeActions,
|
|
14746
16330
|
"data-gs-part": "tree-actions",
|
|
14747
|
-
children: [
|
|
14748
|
-
|
|
14749
|
-
|
|
14750
|
-
|
|
14751
|
-
|
|
14752
|
-
|
|
14753
|
-
|
|
14754
|
-
|
|
14755
|
-
|
|
14756
|
-
|
|
14757
|
-
|
|
14758
|
-
|
|
14759
|
-
|
|
14760
|
-
|
|
14761
|
-
|
|
14762
|
-
|
|
14763
|
-
|
|
14764
|
-
|
|
14765
|
-
|
|
14766
|
-
|
|
14767
|
-
|
|
16331
|
+
children: [
|
|
16332
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
16333
|
+
type: "button",
|
|
16334
|
+
className: filterOpen || filtering ? `${GitWorkbenchPanel_module_css_default.treeIcon} ${GitWorkbenchPanel_module_css_default.treeIconOn}` : GitWorkbenchPanel_module_css_default.treeIcon,
|
|
16335
|
+
"data-gs-part": "filter-files",
|
|
16336
|
+
title: t("filterFiles"),
|
|
16337
|
+
"aria-label": t("filterFiles"),
|
|
16338
|
+
"aria-pressed": filterOpen,
|
|
16339
|
+
onClick: () => {
|
|
16340
|
+
if (filterOpen) {
|
|
16341
|
+
setQuery("");
|
|
16342
|
+
setFilterOpen(false);
|
|
16343
|
+
return;
|
|
16344
|
+
}
|
|
16345
|
+
setFilterOpen(true);
|
|
16346
|
+
window.setTimeout(() => filterRef.current?.focus(), 0);
|
|
16347
|
+
},
|
|
16348
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FilterGlyph, {})
|
|
16349
|
+
}),
|
|
16350
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
16351
|
+
type: "button",
|
|
16352
|
+
className: GitWorkbenchPanel_module_css_default.treeIcon,
|
|
16353
|
+
"data-gs-part": "expand-all",
|
|
16354
|
+
title: t("expandAll"),
|
|
16355
|
+
"aria-label": t("expandAll"),
|
|
16356
|
+
onClick: () => setAll(true),
|
|
16357
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
16358
|
+
className: `${GitWorkbenchPanel_module_css_default.treeIconGlyph} ${GitWorkbenchPanel_module_css_default.treeIconDown}`,
|
|
16359
|
+
children: "▸"
|
|
16360
|
+
})
|
|
16361
|
+
}),
|
|
16362
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
16363
|
+
type: "button",
|
|
16364
|
+
className: GitWorkbenchPanel_module_css_default.treeIcon,
|
|
16365
|
+
"data-gs-part": "collapse-all",
|
|
16366
|
+
title: t("collapseAll"),
|
|
16367
|
+
"aria-label": t("collapseAll"),
|
|
16368
|
+
onClick: () => setAll(false),
|
|
16369
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
16370
|
+
className: GitWorkbenchPanel_module_css_default.treeIconGlyph,
|
|
16371
|
+
children: "▸"
|
|
16372
|
+
})
|
|
14768
16373
|
})
|
|
14769
|
-
|
|
16374
|
+
]
|
|
14770
16375
|
})]
|
|
14771
16376
|
}),
|
|
16377
|
+
filterOpen ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
16378
|
+
className: GitWorkbenchPanel_module_css_default.treeFilter,
|
|
16379
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
16380
|
+
ref: filterRef,
|
|
16381
|
+
className: GitWorkbenchPanel_module_css_default.treeFilterInput,
|
|
16382
|
+
type: "text",
|
|
16383
|
+
value: query,
|
|
16384
|
+
placeholder: t("filterFilesPlaceholder"),
|
|
16385
|
+
"aria-label": t("filterFiles"),
|
|
16386
|
+
spellCheck: false,
|
|
16387
|
+
onChange: (event) => setQuery(event.target.value),
|
|
16388
|
+
onKeyDown: (event) => {
|
|
16389
|
+
if (event.key !== "Escape") return;
|
|
16390
|
+
if (query.length > 0) {
|
|
16391
|
+
event.stopPropagation();
|
|
16392
|
+
setQuery("");
|
|
16393
|
+
return;
|
|
16394
|
+
}
|
|
16395
|
+
event.stopPropagation();
|
|
16396
|
+
setFilterOpen(false);
|
|
16397
|
+
}
|
|
16398
|
+
}), query.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
16399
|
+
type: "button",
|
|
16400
|
+
className: GitWorkbenchPanel_module_css_default.treeFilterClear,
|
|
16401
|
+
title: t("filterFilesClear"),
|
|
16402
|
+
"aria-label": t("filterFilesClear"),
|
|
16403
|
+
onClick: () => {
|
|
16404
|
+
setQuery("");
|
|
16405
|
+
filterRef.current?.focus();
|
|
16406
|
+
},
|
|
16407
|
+
children: "×"
|
|
16408
|
+
}) : null]
|
|
16409
|
+
}) : null,
|
|
14772
16410
|
loading === true ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
14773
16411
|
className: GitWorkbenchPanel_module_css_default.treeEmpty,
|
|
14774
16412
|
"data-gs-part": "tree-loading",
|
|
14775
16413
|
children: t("loading")
|
|
16414
|
+
}) : filtering && shownFiles.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
16415
|
+
className: GitWorkbenchPanel_module_css_default.treeEmpty,
|
|
16416
|
+
"data-gs-part": "tree-no-match",
|
|
16417
|
+
children: t("filterNoMatch")
|
|
14776
16418
|
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
|
|
14777
16419
|
className: GitWorkbenchPanel_module_css_default.tree,
|
|
14778
16420
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TreeChildren, {
|
|
14779
16421
|
node: tree,
|
|
14780
16422
|
depth: 0,
|
|
14781
16423
|
active,
|
|
14782
|
-
collapsed: effective,
|
|
16424
|
+
collapsed: filtering ? EMPTY_COLLAPSED : effective,
|
|
14783
16425
|
onToggle: toggleOne,
|
|
14784
16426
|
onSelect,
|
|
14785
16427
|
onCheck,
|
|
14786
|
-
|
|
16428
|
+
onDiscard,
|
|
16429
|
+
stageLabels,
|
|
16430
|
+
discardLabel: t("discardAction")
|
|
14787
16431
|
})
|
|
14788
16432
|
}),
|
|
14789
16433
|
footer
|
|
@@ -14847,7 +16491,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
14847
16491
|
for (const child of node.dirs.values()) out.push(...filesUnder(child));
|
|
14848
16492
|
return out;
|
|
14849
16493
|
}
|
|
14850
|
-
function TreeChildren({ node, depth, active, collapsed, onToggle, onSelect, onCheck, stageLabels }) {
|
|
16494
|
+
function TreeChildren({ node, depth, active, collapsed, onToggle, onSelect, onCheck, onDiscard, stageLabels, discardLabel }) {
|
|
14851
16495
|
const dirNodes = [...node.dirs.values()].sort((a, b) => a.name.localeCompare(b.name));
|
|
14852
16496
|
const fileNodes = [...node.files].sort((a, b) => basePart(a.path).localeCompare(basePart(b.path)));
|
|
14853
16497
|
const checkColumn = onCheck !== void 0 ? TREE_CHECK_W : 0;
|
|
@@ -14875,6 +16519,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
14875
16519
|
className: `${GitWorkbenchPanel_module_css_default.chevron} ${open ? GitWorkbenchPanel_module_css_default.chevronOpen : ""}`,
|
|
14876
16520
|
children: "▸"
|
|
14877
16521
|
}),
|
|
16522
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(PathDirGlyph, {}),
|
|
14878
16523
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
14879
16524
|
className: GitWorkbenchPanel_module_css_default.treeDirName,
|
|
14880
16525
|
children: dir.name
|
|
@@ -14906,7 +16551,9 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
14906
16551
|
onToggle,
|
|
14907
16552
|
onSelect,
|
|
14908
16553
|
onCheck,
|
|
14909
|
-
|
|
16554
|
+
onDiscard,
|
|
16555
|
+
stageLabels,
|
|
16556
|
+
discardLabel
|
|
14910
16557
|
})
|
|
14911
16558
|
}) : null]
|
|
14912
16559
|
}, dir.path);
|
|
@@ -14914,45 +16561,60 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
14914
16561
|
const check = fileCheckState(file);
|
|
14915
16562
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
|
|
14916
16563
|
className: GitWorkbenchPanel_module_css_default.fileLi,
|
|
14917
|
-
children: [
|
|
14918
|
-
|
|
14919
|
-
|
|
14920
|
-
|
|
14921
|
-
|
|
14922
|
-
|
|
14923
|
-
|
|
14924
|
-
|
|
14925
|
-
|
|
14926
|
-
|
|
14927
|
-
|
|
14928
|
-
|
|
14929
|
-
|
|
14930
|
-
|
|
14931
|
-
|
|
14932
|
-
|
|
14933
|
-
|
|
14934
|
-
|
|
14935
|
-
|
|
14936
|
-
|
|
14937
|
-
|
|
14938
|
-
|
|
14939
|
-
|
|
14940
|
-
|
|
14941
|
-
|
|
14942
|
-
|
|
14943
|
-
|
|
14944
|
-
|
|
14945
|
-
|
|
14946
|
-
|
|
14947
|
-
|
|
14948
|
-
|
|
14949
|
-
|
|
14950
|
-
|
|
14951
|
-
|
|
14952
|
-
|
|
14953
|
-
|
|
14954
|
-
|
|
14955
|
-
|
|
16564
|
+
children: [
|
|
16565
|
+
onCheck !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CheckBox, {
|
|
16566
|
+
state: check,
|
|
16567
|
+
label: check === "on" ? stageLabels.unstage : stageLabels.stage,
|
|
16568
|
+
indent,
|
|
16569
|
+
onToggle: () => onCheck([file], check)
|
|
16570
|
+
}) : null,
|
|
16571
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
16572
|
+
type: "button",
|
|
16573
|
+
className: active === file.path ? `${GitWorkbenchPanel_module_css_default.file} ${GitWorkbenchPanel_module_css_default.fileActive}` : GitWorkbenchPanel_module_css_default.file,
|
|
16574
|
+
style: { paddingLeft: (onCheck !== void 0 ? 0 : indent) + TREE_LEAF_OFFSET },
|
|
16575
|
+
onClick: () => onSelect(file.path),
|
|
16576
|
+
title: file.previousPath !== void 0 ? `${file.previousPath} → ${file.path}` : file.path,
|
|
16577
|
+
children: [
|
|
16578
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(PathFileGlyph, {}),
|
|
16579
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
16580
|
+
className: GitWorkbenchPanel_module_css_default.filePath,
|
|
16581
|
+
children: basePart(file.path)
|
|
16582
|
+
}),
|
|
16583
|
+
file.binary ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
16584
|
+
className: GitWorkbenchPanel_module_css_default.fileBinary,
|
|
16585
|
+
children: "BIN"
|
|
16586
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
16587
|
+
className: GitWorkbenchPanel_module_css_default.fileCounts,
|
|
16588
|
+
children: [
|
|
16589
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
16590
|
+
className: GitWorkbenchPanel_module_css_default.fileCountAdd,
|
|
16591
|
+
children: file.addedLines > 0 ? `+${file.addedLines}` : ""
|
|
16592
|
+
}),
|
|
16593
|
+
" ",
|
|
16594
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
16595
|
+
className: GitWorkbenchPanel_module_css_default.fileCountDel,
|
|
16596
|
+
children: file.deletedLines > 0 ? `−${file.deletedLines}` : ""
|
|
16597
|
+
})
|
|
16598
|
+
]
|
|
16599
|
+
}),
|
|
16600
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
16601
|
+
className: `${GitWorkbenchPanel_module_css_default.fileStatus} ${STATUS_BADGE[file.status]}`,
|
|
16602
|
+
children: statusGlyph(file.status)
|
|
16603
|
+
})
|
|
16604
|
+
]
|
|
16605
|
+
}),
|
|
16606
|
+
onDiscard !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
16607
|
+
type: "button",
|
|
16608
|
+
className: GitWorkbenchPanel_module_css_default.fileDiscard,
|
|
16609
|
+
title: discardLabel,
|
|
16610
|
+
"aria-label": `${discardLabel ?? ""} ${file.path}`,
|
|
16611
|
+
onClick: (event) => {
|
|
16612
|
+
event.stopPropagation();
|
|
16613
|
+
onDiscard(file);
|
|
16614
|
+
},
|
|
16615
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RollbackGlyph, {})
|
|
16616
|
+
}) : null
|
|
16617
|
+
]
|
|
14956
16618
|
}, file.path);
|
|
14957
16619
|
})] });
|
|
14958
16620
|
}
|
|
@@ -15134,8 +16796,36 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
15134
16796
|
noTextDiff: "无文本差异",
|
|
15135
16797
|
noCommits: "无提交历史",
|
|
15136
16798
|
historyLabel: "提交历史",
|
|
16799
|
+
commitAuthor: "作者",
|
|
16800
|
+
commitCommitter: "提交者",
|
|
16801
|
+
commitDate: "提交时间",
|
|
16802
|
+
historyFilterPlaceholder: "筛选:user: 名字 / path: 路径 / after: 日期 / 关键词",
|
|
16803
|
+
historyNoMatch: "没有匹配的提交",
|
|
16804
|
+
filterClearAll: "清除全部",
|
|
16805
|
+
filterBy: "筛选条件",
|
|
16806
|
+
filterUsers: "用户",
|
|
16807
|
+
filterUserSearch: "搜索作者",
|
|
16808
|
+
filterAuthorsMore: "仅显示提交最多的 500 位作者",
|
|
16809
|
+
filterDate: "日期",
|
|
16810
|
+
filterToday: "今天",
|
|
16811
|
+
filterLast7: "最近 7 天",
|
|
16812
|
+
filterLast30: "最近 30 天",
|
|
16813
|
+
filterAfter: "之后",
|
|
16814
|
+
filterBefore: "之前",
|
|
16815
|
+
filterPaths: "路径",
|
|
16816
|
+
filterPathsMore: "文件过多,目录树已截断",
|
|
16817
|
+
filterPathSearch: "搜索文件或目录",
|
|
16818
|
+
filterCalendarSets: "日历写入",
|
|
16819
|
+
filterSelected: "已选 {count} 项",
|
|
16820
|
+
filterLocale: "zh-CN",
|
|
16821
|
+
allBranches: "全部分支",
|
|
15137
16822
|
expandAll: "展开全部",
|
|
15138
16823
|
collapseAll: "收起全部",
|
|
16824
|
+
filterFiles: "过滤文件",
|
|
16825
|
+
filterFilesPlaceholder: "过滤文件,空格分隔多个关键字",
|
|
16826
|
+
filterFilesClear: "清除过滤",
|
|
16827
|
+
filesFiltered: "{shown} / {count} 文件",
|
|
16828
|
+
filterNoMatch: "没有匹配的文件",
|
|
15139
16829
|
noBranch: "(无分支)",
|
|
15140
16830
|
copyCommit: "复制提交说明",
|
|
15141
16831
|
copiedCommit: "已复制",
|
|
@@ -15169,6 +16859,14 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
15169
16859
|
"op.ok.fetch": "已获取远端信息",
|
|
15170
16860
|
"op.ok.pull": "拉取完成",
|
|
15171
16861
|
"op.ok.push": "推送成功",
|
|
16862
|
+
"op.ok.discardFile": "已撤回",
|
|
16863
|
+
discardAction: "撤回改动",
|
|
16864
|
+
discardTitle: "撤回改动?",
|
|
16865
|
+
discardConfirm: "撤回",
|
|
16866
|
+
discardCancel: "取消",
|
|
16867
|
+
discardBodyRestore: "{path} 将还原成上次提交时的样子。这里的 {added} 行新增、{deleted} 行删除无法找回。",
|
|
16868
|
+
discardBodyDelete: "{path} 从未被 git 记录过,删除后无法找回。",
|
|
16869
|
+
discardBodyUnrename: "撤销重命名:{path} 改回 {previousPath},改名期间的内容改动一并丢弃。",
|
|
15172
16870
|
"op.fail.auth": "认证失败。凭据提示已被禁用,请先在终端里配置好凭据再重试。",
|
|
15173
16871
|
"op.fail.network": "网络不可达:主机名解析失败或连接不上。检查网络与远程地址后重试。",
|
|
15174
16872
|
"op.fail.no-upstream": "当前分支没有上游分支。",
|
|
@@ -15245,8 +16943,36 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
15245
16943
|
noTextDiff: "No text changes",
|
|
15246
16944
|
noCommits: "No commit history",
|
|
15247
16945
|
historyLabel: "Commit history",
|
|
16946
|
+
commitAuthor: "Author",
|
|
16947
|
+
commitCommitter: "Committer",
|
|
16948
|
+
commitDate: "Committed",
|
|
16949
|
+
historyFilterPlaceholder: "Filter: user: name / path: dir / after: date / text",
|
|
16950
|
+
historyNoMatch: "No matching commits",
|
|
16951
|
+
filterClearAll: "Clear all",
|
|
16952
|
+
filterBy: "Filter by",
|
|
16953
|
+
filterUsers: "Users",
|
|
16954
|
+
filterUserSearch: "Search authors",
|
|
16955
|
+
filterAuthorsMore: "Showing the 500 busiest authors only",
|
|
16956
|
+
filterDate: "Date",
|
|
16957
|
+
filterToday: "Today",
|
|
16958
|
+
filterLast7: "Last 7 days",
|
|
16959
|
+
filterLast30: "Last 30 days",
|
|
16960
|
+
filterAfter: "After",
|
|
16961
|
+
filterBefore: "Before",
|
|
16962
|
+
filterPaths: "Paths",
|
|
16963
|
+
filterPathsMore: "Too many files — tree truncated",
|
|
16964
|
+
filterPathSearch: "Search files or folders",
|
|
16965
|
+
filterCalendarSets: "Calendar sets",
|
|
16966
|
+
filterSelected: "{count} selected",
|
|
16967
|
+
filterLocale: "en-US",
|
|
16968
|
+
allBranches: "All branches",
|
|
15248
16969
|
expandAll: "Expand all",
|
|
15249
16970
|
collapseAll: "Collapse all",
|
|
16971
|
+
filterFiles: "Filter files",
|
|
16972
|
+
filterFilesPlaceholder: "Filter files; space-separated terms",
|
|
16973
|
+
filterFilesClear: "Clear filter",
|
|
16974
|
+
filesFiltered: "{shown} / {count} files",
|
|
16975
|
+
filterNoMatch: "No file matches",
|
|
15250
16976
|
noBranch: "(no branch)",
|
|
15251
16977
|
copyCommit: "Copy message",
|
|
15252
16978
|
copiedCommit: "Copied",
|
|
@@ -15280,6 +17006,14 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
15280
17006
|
"op.ok.fetch": "Fetched",
|
|
15281
17007
|
"op.ok.pull": "Pulled",
|
|
15282
17008
|
"op.ok.push": "Pushed",
|
|
17009
|
+
"op.ok.discardFile": "Rolled back",
|
|
17010
|
+
discardAction: "Roll back changes",
|
|
17011
|
+
discardTitle: "Roll back changes?",
|
|
17012
|
+
discardConfirm: "Roll back",
|
|
17013
|
+
discardCancel: "Cancel",
|
|
17014
|
+
discardBodyRestore: "{path} goes back to its committed content. The {added} added and {deleted} deleted lines here cannot be recovered.",
|
|
17015
|
+
discardBodyDelete: "{path} was never recorded by git. Deleting it cannot be undone.",
|
|
17016
|
+
discardBodyUnrename: "Undo the rename: {path} goes back to {previousPath}, and content changed along the way is lost.",
|
|
15283
17017
|
"op.fail.auth": "Authentication failed. Credential prompts are disabled here — set your credentials up in a terminal first.",
|
|
15284
17018
|
"op.fail.network": "The network was unreachable — the host could not be resolved or the connection failed. Check connectivity and the remote URL, then retry.",
|
|
15285
17019
|
"op.fail.no-upstream": "This branch has no upstream.",
|
|
@@ -15334,15 +17068,27 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
15334
17068
|
} }, signal);
|
|
15335
17069
|
return result.ok ? result.value : null;
|
|
15336
17070
|
},
|
|
15337
|
-
fetchCommits: async (worktreePath, ref, skip, limit, signal) => {
|
|
17071
|
+
fetchCommits: async (worktreePath, ref, skip, limit, filter, signal) => {
|
|
15338
17072
|
const result = await connection.rpc.call("/api", "gitWorkbench/commits", { args: {
|
|
15339
17073
|
worktreePath: worktreePath ?? "",
|
|
15340
17074
|
ref,
|
|
15341
17075
|
skip,
|
|
15342
|
-
limit
|
|
17076
|
+
limit,
|
|
17077
|
+
filter
|
|
17078
|
+
} }, signal);
|
|
17079
|
+
return result.ok ? result.value : null;
|
|
17080
|
+
},
|
|
17081
|
+
fetchAuthors: async (worktreePath, ref, signal) => {
|
|
17082
|
+
const result = await connection.rpc.call("/api", "gitWorkbench/authors", { args: {
|
|
17083
|
+
worktreePath: worktreePath ?? "",
|
|
17084
|
+
ref
|
|
15343
17085
|
} }, signal);
|
|
15344
17086
|
return result.ok ? result.value : null;
|
|
15345
17087
|
},
|
|
17088
|
+
fetchRepoTree: async (worktreePath, signal) => {
|
|
17089
|
+
const result = await connection.rpc.call("/api", "gitWorkbench/repoTree", { args: { worktreePath: worktreePath ?? "" } }, signal);
|
|
17090
|
+
return result.ok ? result.value : null;
|
|
17091
|
+
},
|
|
15346
17092
|
fetchCompare: async (worktreePath, base, head, signal) => {
|
|
15347
17093
|
const result = await connection.rpc.call("/api", "gitWorkbench/compareRefs", { args: {
|
|
15348
17094
|
worktreePath: worktreePath ?? "",
|
|
@@ -15382,6 +17128,13 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
15382
17128
|
const result = await connection.rpc.call("/api", "gitWorkbench/syncStatus", { args: { worktreePath: worktreePath ?? "" } }, signal);
|
|
15383
17129
|
return result.ok ? result.value : null;
|
|
15384
17130
|
},
|
|
17131
|
+
fetchDiscardPlan: async (worktreePath, path, signal) => {
|
|
17132
|
+
const result = await connection.rpc.call("/api", "gitWorkbench/discardPlan", { args: {
|
|
17133
|
+
worktreePath: worktreePath ?? "",
|
|
17134
|
+
path
|
|
17135
|
+
} }, signal);
|
|
17136
|
+
return result.ok ? result.value : null;
|
|
17137
|
+
},
|
|
15385
17138
|
runGitOp: async (op, worktreePath, payload, signal) => {
|
|
15386
17139
|
const result = await connection.rpc.call("/api", `gitWorkbench/${op}`, { args: {
|
|
15387
17140
|
worktreePath: worktreePath ?? "",
|