@young1lin/dsh-ui-gitworkbench 0.1.3 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/client.js CHANGED
@@ -487,6 +487,631 @@ 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/discard-flow.ts
794
+ /** Fallback text for a failure that arrived with nothing to say. */
795
+ const UNKNOWN_DISCARD_ERROR = "discardPlan failed";
796
+ /**
797
+ * Decide what a roll-back click does with the answer it got.
798
+ *
799
+ * @param answer - the host's reply, or the failure that replaced it.
800
+ * @returns the single next step; never null, because every answer including a
801
+ * broken one has to lead somewhere the reader can see.
802
+ */
803
+ function nextAfterPlan(answer) {
804
+ if (answer.kind === "failed") {
805
+ const error = answer.error.trim();
806
+ return {
807
+ kind: "report",
808
+ error: error.length > 0 ? error : UNKNOWN_DISCARD_ERROR
809
+ };
810
+ }
811
+ const plan = answer.plan;
812
+ if (typeof plan.error === "string" && plan.error.trim().length > 0) return {
813
+ kind: "report",
814
+ error: plan.error.trim()
815
+ };
816
+ if (plan.effect === void 0) return { kind: "refresh" };
817
+ if (plan.irreversible === false) return {
818
+ kind: "run",
819
+ effect: plan.effect
820
+ };
821
+ return {
822
+ kind: "confirm",
823
+ plan
824
+ };
825
+ }
826
+ //#endregion
827
+ //#region src/client/file-filter.ts
828
+ /**
829
+ * Narrowing a file list by typing at it.
830
+ *
831
+ * A commit that touched 140 files is a scroll, not a list, and the drawer's
832
+ * tree is the same object in every tab — so the rule lives here once and both
833
+ * the working tree and a commit's contents get it.
834
+ *
835
+ * Two decisions worth stating, because both are the kind that get "simplified"
836
+ * later:
837
+ *
838
+ * - **Terms are ANDed, in any order.** `panel css` finds
839
+ * `src/client/GitWorkbenchPanel.module.css` — which is how anyone types
840
+ * when they half-remember a path, and is the behaviour a single-substring
841
+ * match gets wrong for exactly the paths that are long enough to need
842
+ * filtering.
843
+ * - **Smart case.** An all-lowercase query ignores case; the moment the
844
+ * reader types a capital they mean it. `README` should not match
845
+ * `readme-generator`, and `readme` should still find `README.md`.
846
+ *
847
+ * The result keeps the caller's order and its element type: the tree is built
848
+ * from whatever survives, so filtering never has to know what a file is beyond
849
+ * its path.
850
+ *
851
+ * @module @young1lin/dsh-ui-gitworkbench/client/file-filter
852
+ */
853
+ /** Split a raw query into the terms every path must contain. */
854
+ function termsOf(query) {
855
+ return query.split(/\s+/).filter((term) => term.length > 0);
856
+ }
857
+ /**
858
+ * Whether one path satisfies a query.
859
+ *
860
+ * @param path - repo-relative path, as the tree lists it.
861
+ * @param query - raw text from the filter box; blank matches everything, so a
862
+ * caller that renders `filterFiles` unconditionally shows the
863
+ * whole list until something is typed.
864
+ */
865
+ function matchesPath(path, query) {
866
+ const terms = termsOf(query);
867
+ if (terms.length === 0) return true;
868
+ return terms.every((term) => {
869
+ return term.toLowerCase() !== term ? path.includes(term) : path.toLowerCase().includes(term.toLowerCase());
870
+ });
871
+ }
872
+ /**
873
+ * Keep the files whose path satisfies the query, in the order given.
874
+ *
875
+ * @param files - anything carrying a `path`; the tree's own file objects.
876
+ * @param query - raw text from the filter box.
877
+ * @returns the same array instance when nothing is filtered out, so a blank
878
+ * query costs no re-render downstream.
879
+ */
880
+ function filterFiles(files, query) {
881
+ if (termsOf(query).length === 0) return files;
882
+ return files.filter((file) => matchesPath(file.path, query));
883
+ }
884
+ //#endregion
885
+ //#region src/client/path-select.ts
886
+ /**
887
+ * Index a raw path list for child lookup.
888
+ * @param paths - repo-relative file paths, exactly as `repoTree` returned.
889
+ */
890
+ function buildIndex(paths) {
891
+ const dirs = /* @__PURE__ */ new Map();
892
+ const files = /* @__PURE__ */ new Map();
893
+ const noteDir = (dir) => {
894
+ if (!dirs.has(dir)) dirs.set(dir, []);
895
+ };
896
+ noteDir("");
897
+ for (const path of paths) {
898
+ if (path.length === 0) continue;
899
+ const parts = path.split("/");
900
+ let dir = "";
901
+ for (let i = 0; i < parts.length - 1; i += 1) {
902
+ const childDir = dir === "" ? parts[i] : `${dir}/${parts[i]}`;
903
+ noteDir(childDir);
904
+ const list = dirs.get(dir);
905
+ if (!list.includes(childDir)) list.push(childDir);
906
+ dir = childDir;
907
+ }
908
+ const list = files.get(dir) ?? [];
909
+ list.push(path);
910
+ files.set(dir, list);
911
+ }
912
+ for (const list of dirs.values()) list.sort();
913
+ for (const list of files.values()) list.sort();
914
+ return {
915
+ dirs,
916
+ files
917
+ };
918
+ }
919
+ /** Children of a directory, alphabetical by full path — the tree's order. */
920
+ function childrenOf(index, dir) {
921
+ return [...index.dirs.get(dir) ?? [], ...index.files.get(dir) ?? []].sort();
922
+ }
923
+ /**
924
+ * Is `p` selected — itself ticked, or inside a ticked directory?
925
+ * (Segment-boundary prefix: `src` does not cover `src2`.)
926
+ */
927
+ function isCovered(paths, p) {
928
+ return paths.some((tick) => tick === p || p.startsWith(`${tick}/`));
929
+ }
930
+ /**
931
+ * Tick a path. No-op when an ancestor already covers it; absorbs every
932
+ * descendant it covers, keeping the set minimal — one folder chip, never the
933
+ * pile of files under it.
934
+ */
935
+ function addPath(paths, p) {
936
+ if (isCovered(paths, p)) return paths;
937
+ return [...paths.filter((tick) => !(tick === p || tick.startsWith(`${p}/`))), p];
938
+ }
939
+ /**
940
+ * Untick a path. Removing an exact tick drops it; removing a file COVERED by
941
+ * a ticked folder replaces that folder with its other children, level by
942
+ * level down to the file — the standard cascade-out.
943
+ */
944
+ function removePath(paths, p, index) {
945
+ const out = [];
946
+ for (const tick of paths) {
947
+ if (tick !== p && !p.startsWith(`${tick}/`)) {
948
+ out.push(tick);
949
+ continue;
950
+ }
951
+ let dir = tick;
952
+ while (dir !== p) {
953
+ const rest = p.slice(dir.length + 1);
954
+ const nextName = dir === "" ? p.split("/")[0] : rest.split("/")[0];
955
+ const next = dir === "" ? nextName : `${dir}/${nextName}`;
956
+ for (const child of childrenOf(index, dir)) if (child !== next) out.push(child);
957
+ dir = next;
958
+ }
959
+ }
960
+ return out;
961
+ }
962
+ /** Every file under a directory (empty for a file path). */
963
+ function filesUnder$1(index, dir) {
964
+ const out = [];
965
+ const stack = [dir];
966
+ while (stack.length > 0) {
967
+ const current = stack.pop();
968
+ out.push(...index.files.get(current) ?? []);
969
+ stack.push(...index.dirs.get(current) ?? []);
970
+ }
971
+ return out;
972
+ }
973
+ /**
974
+ * A row's checkbox state, derived: `on` when covered — by an ancestor tick OR
975
+ * by every file under it being covered individually; `partial` when a
976
+ * directory holds some but not all of its files; else `off`.
977
+ */
978
+ function checkedState(paths, p, index) {
979
+ if (isCovered(paths, p)) return "on";
980
+ const files = filesUnder$1(index, p);
981
+ if (files.length === 0) return "off";
982
+ const covered = files.filter((file) => isCovered(paths, file)).length;
983
+ return covered === files.length ? "on" : covered > 0 ? "partial" : "off";
984
+ }
985
+ //#endregion
986
+ //#region src/client/calendar.ts
987
+ const DAY_MS = 864e5;
988
+ function toIso(date) {
989
+ const m = String(date.getUTCMonth() + 1).padStart(2, "0");
990
+ const d = String(date.getUTCDate()).padStart(2, "0");
991
+ return `${date.getUTCFullYear()}-${m}-${d}`;
992
+ }
993
+ /**
994
+ * The 6×7 Monday-first grid for one month.
995
+ * @param year - displayed year.
996
+ * @param month - displayed month, 0-based like `Date`.
997
+ * @param todayIso - what counts as today, for the accent; `''` accents nothing.
998
+ */
999
+ function monthGrid(year, month, todayIso) {
1000
+ const first = new Date(Date.UTC(year, month, 1));
1001
+ const lead = (first.getUTCDay() + 6) % 7;
1002
+ const start = /* @__PURE__ */ new Date(first.getTime() - lead * DAY_MS);
1003
+ const weeks = [];
1004
+ for (let w = 0; w < 6; w += 1) {
1005
+ const row = [];
1006
+ for (let d = 0; d < 7; d += 1) {
1007
+ const date = new Date(start.getTime() + (w * 7 + d) * DAY_MS);
1008
+ const iso = toIso(date);
1009
+ row.push({
1010
+ iso,
1011
+ day: date.getUTCDate(),
1012
+ inMonth: date.getUTCMonth() === month,
1013
+ isToday: iso === todayIso
1014
+ });
1015
+ }
1016
+ weeks.push(row);
1017
+ }
1018
+ return weeks;
1019
+ }
1020
+ /**
1021
+ * Single-letter weekday header row, Monday-first, via the viewer's own locale
1022
+ * (or an explicit one, which is what the test does).
1023
+ * @param locale - BCP 47 tag; undefined means the runtime default.
1024
+ */
1025
+ function weekdayLabels(locale) {
1026
+ const labels = [];
1027
+ for (let d = 2; d <= 8; d += 1) labels.push(new Intl.DateTimeFormat(locale, { weekday: "narrow" }).format(new Date(Date.UTC(2023, 0, d))));
1028
+ return labels;
1029
+ }
1030
+ /**
1031
+ * Whether a day falls strictly BETWEEN the two bounds, so the grid can tint
1032
+ * the span the filter admits rather than only its two endpoints.
1033
+ *
1034
+ * Both bounds have to be `yyyy-mm-dd` for a range to exist: the bounds also
1035
+ * accept approxidate text (`1 week ago`), which names no grid day at all, and
1036
+ * lexical comparison on iso dates is the same as chronological. An inverted
1037
+ * pair (after > before) is a range git returns nothing for, and it tints
1038
+ * nothing here for the same reason.
1039
+ *
1040
+ * @param iso - the cell's day.
1041
+ * @param after - lower bound, exclusive here (it renders as an endpoint).
1042
+ * @param before - upper bound, exclusive here.
1043
+ */
1044
+ function inCalRange(iso, after, before) {
1045
+ if (!ISO_DAY.test(after) || !ISO_DAY.test(before)) return false;
1046
+ return iso > after && iso < before;
1047
+ }
1048
+ const ISO_DAY = /^\d{4}-\d{2}-\d{2}$/;
1049
+ /** Today as `yyyy-mm-dd` in the viewer's local timezone (for `todayIso`). */
1050
+ function localTodayIso() {
1051
+ const now = /* @__PURE__ */ new Date();
1052
+ const m = String(now.getMonth() + 1).padStart(2, "0");
1053
+ const d = String(now.getDate()).padStart(2, "0");
1054
+ return `${now.getFullYear()}-${m}-${d}`;
1055
+ }
1056
+ //#endregion
1057
+ //#region src/client/active-file.ts
1058
+ /** No filter — a shared empty so callers keep a stable reference. */
1059
+ const NO_PATHS = [];
1060
+ /**
1061
+ * Whether a file is what a pathspec selected: the file itself, or anything in
1062
+ * its subtree.
1063
+ *
1064
+ * A pathspec from the picker is either a file path or a directory path with no
1065
+ * trailing slash (`dir.path` / the file's full path — `path-select.ts`), and
1066
+ * the two cases are told apart by the file rather than by the spec: `===` is
1067
+ * the file, `spec + '/'` prefix is the subtree. Guessing which KIND a spec is
1068
+ * from its string alone is what a trailing-slash convention would force, and
1069
+ * it would be wrong for any file without an extension.
1070
+ */
1071
+ function covers(spec, path) {
1072
+ return path === spec || path.startsWith(`${spec}/`);
1073
+ }
1074
+ /**
1075
+ * The file a view should highlight.
1076
+ *
1077
+ * The order of preference, and why it is this order:
1078
+ *
1079
+ * 1. **The selection, if this view has it.** Stepping down a filtered list is
1080
+ * the whole point of filtering; changing the file under the reader every
1081
+ * time they move a row would undo it. This also means an explicit click
1082
+ * outranks the filter — the reader looked somewhere on purpose.
1083
+ * 2. **A file the filter names EXACTLY.** Ticking `xx/aa/dd.ts` is a statement
1084
+ * about that file; ticking `xx` is a statement about a region. When a
1085
+ * commit touches both kinds, the named file is the more specific intent, so
1086
+ * it wins. (The two can only coexist across disjoint trees: the picker's
1087
+ * invariant is that no ticked path covers another.)
1088
+ * 3. **A file under a filtered directory.**
1089
+ * 4. **The first file.** No filter, or nothing in this commit matched it —
1090
+ * the behaviour before any of this existed.
1091
+ *
1092
+ * Ties inside 2 and 3 go to the commit's own file order, which is the order
1093
+ * the tree renders: the highlight lands on the topmost matching row, so it is
1094
+ * where the reader is already looking and never needs a scroll to find. The
1095
+ * alternative — first match in FILTER order — would be arbitrary, since that
1096
+ * order is an artifact of the sequence the boxes were ticked in and is never
1097
+ * shown anywhere.
1098
+ *
1099
+ * @param files - the view's files, in the order the tree shows them.
1100
+ * @param filterPaths - active path filter; empty on views that have none.
1101
+ * @param previous - the currently selected path, or null.
1102
+ * @returns the path to highlight, or null when there are no files at all.
1103
+ */
1104
+ function preferredFile(files, filterPaths, previous) {
1105
+ if (previous !== null && files.some((file) => file.path === previous)) return previous;
1106
+ if (filterPaths.length > 0) {
1107
+ const exact = files.find((file) => filterPaths.some((spec) => file.path === spec));
1108
+ if (exact !== void 0) return exact.path;
1109
+ const under = files.find((file) => filterPaths.some((spec) => covers(spec, file.path)));
1110
+ if (under !== void 0) return under.path;
1111
+ }
1112
+ return files[0]?.path ?? null;
1113
+ }
1114
+ //#endregion
490
1115
  //#region src/client/stage-tree.ts
491
1116
  /**
492
1117
  * One file's tick.
@@ -11613,7 +12238,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
11613
12238
  }
11614
12239
  //#endregion
11615
12240
  //#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}";
12241
+ 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
12242
  const tagId = "@young1lin/dsh-ui-gitworkbench/GitWorkbenchPanel.module.css";
11618
12243
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
11619
12244
  const tag = document.createElement("style");
@@ -11623,196 +12248,262 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
11623
12248
  document.head.appendChild(tag);
11624
12249
  }
11625
12250
  var GitWorkbenchPanel_module_css_default = {
11626
- "resizerActive": "SD8qLW_resizerActive",
11627
- "stAdded": "SD8qLW_stAdded",
11628
- "sliderRow": "SD8qLW_sliderRow",
11629
- "empty": "SD8qLW_empty",
11630
- "btnBehind": "SD8qLW_btnBehind",
11631
- "cardBehind": "SD8qLW_cardBehind",
11632
- "headerTotalsDel": "SD8qLW_headerTotalsDel",
11633
- "refValue": "SD8qLW_refValue",
11634
- "opBannerBad": "SD8qLW_opBannerBad",
11635
- "refLabel": "SD8qLW_refLabel",
11636
- "tabs": "SD8qLW_tabs",
12251
+ "treeDirActive": "SD8qLW_treeDirActive",
12252
+ "funnelFootClear": "SD8qLW_funnelFootClear",
12253
+ "overlayMax": "SD8qLW_overlayMax",
12254
+ "cardBranch": "SD8qLW_cardBranch",
12255
+ "funnelMore": "SD8qLW_funnelMore",
11637
12256
  "lineHunk": "SD8qLW_lineHunk",
11638
- "sliderValue": "SD8qLW_sliderValue",
11639
- "commitHasBody": "SD8qLW_commitHasBody",
11640
- "elideHead": "SD8qLW_elideHead",
11641
- "headerView": "SD8qLW_headerView",
11642
- "refSearch": "SD8qLW_refSearch",
11643
- "commitHash": "SD8qLW_commitHash",
11644
- "cardAdded": "SD8qLW_cardAdded",
11645
- "opBanner": "SD8qLW_opBanner",
11646
- "opBannerOk": "SD8qLW_opBannerOk",
11647
- "paletteRowActive": "SD8qLW_paletteRowActive",
11648
- "renameLine": "SD8qLW_renameLine",
11649
- "treeDir": "SD8qLW_treeDir",
11650
- "cardSep": "SD8qLW_cardSep",
11651
- "cssArea": "SD8qLW_cssArea",
11652
- "headerTotalsDim": "SD8qLW_headerTotalsDim",
11653
- "commitsSentinel": "SD8qLW_commitsSentinel",
11654
- "stDeleted": "SD8qLW_stDeleted",
11655
- "miniBtnPrimary": "SD8qLW_miniBtnPrimary",
11656
- "refButton": "SD8qLW_refButton",
11657
- "headerTotals": "SD8qLW_headerTotals",
11658
- "chevron": "SD8qLW_chevron",
11659
- "stRenamed": "SD8qLW_stRenamed",
11660
- "gutter": "SD8qLW_gutter",
11661
- "fileBinary": "SD8qLW_fileBinary",
11662
- "commitsFoot": "SD8qLW_commitsFoot",
12257
+ "treeDirCounts": "SD8qLW_treeDirCounts",
12258
+ "btnAhead": "SD8qLW_btnAhead",
12259
+ "chipLight": "SD8qLW_chipLight",
11663
12260
  "commitSubject": "SD8qLW_commitSubject",
11664
- "headerBranch": "SD8qLW_headerBranch",
11665
- "headerPathMain": "SD8qLW_headerPathMain",
11666
- "commitSubjectRow": "SD8qLW_commitSubjectRow",
11667
- "chevronOpen": "SD8qLW_chevronOpen",
11668
- "fileCounts": "SD8qLW_fileCounts",
11669
- "commitStaged": "SD8qLW_commitStaged",
11670
- "treeEmpty": "SD8qLW_treeEmpty",
11671
- "btnCount": "SD8qLW_btnCount",
11672
- "chipSystem": "SD8qLW_chipSystem",
12261
+ "calHead": "SD8qLW_calHead",
12262
+ "funnelTabCount": "SD8qLW_funnelTabCount",
12263
+ "funnelSearch": "SD8qLW_funnelSearch",
12264
+ "commitPopMeta": "SD8qLW_commitPopMeta",
12265
+ "confirmBody": "SD8qLW_confirmBody",
12266
+ "headerView": "SD8qLW_headerView",
11673
12267
  "commit": "SD8qLW_commit",
11674
- "treeDirName": "SD8qLW_treeDirName",
11675
- "cardFiles": "SD8qLW_cardFiles",
11676
- "commitBtn": "SD8qLW_commitBtn",
11677
- "paneTitle": "SD8qLW_paneTitle",
11678
- "body": "SD8qLW_body",
11679
- "wordAdd": "SD8qLW_wordAdd",
12268
+ "lnNew": "SD8qLW_lnNew",
11680
12269
  "syncUpstream": "SD8qLW_syncUpstream",
11681
- "treeLead": "SD8qLW_treeLead",
11682
- "headerRight": "SD8qLW_headerRight",
11683
- "scopeRow": "SD8qLW_scopeRow",
11684
- "elideTail": "SD8qLW_elideTail",
11685
- "overlayMax": "SD8qLW_overlayMax",
11686
- "lineAdd": "SD8qLW_lineAdd",
11687
- "treeDirCount": "SD8qLW_treeDirCount",
11688
- "treeCol": "SD8qLW_treeCol",
11689
- "header": "SD8qLW_header",
11690
- "segmentChip": "SD8qLW_segmentChip",
11691
- "wtCurrent": "SD8qLW_wtCurrent",
11692
- "scopeHint": "SD8qLW_scopeHint",
11693
- "cardWt": "SD8qLW_cardWt",
11694
- "refRowName": "SD8qLW_refRowName",
11695
- "cardDeleted": "SD8qLW_cardDeleted",
11696
- "chipLight": "SD8qLW_chipLight",
11697
- "overlay": "SD8qLW_overlay",
11698
- "swatch": "SD8qLW_swatch",
11699
- "themeRail": "SD8qLW_themeRail",
11700
- "lineDel": "SD8qLW_lineDel",
11701
- "treeIcon": "SD8qLW_treeIcon",
11702
- "segment": "SD8qLW_segment",
11703
- "themeNote": "SD8qLW_themeNote",
11704
- "refRow": "SD8qLW_refRow",
11705
- "cardAhead": "SD8qLW_cardAhead",
11706
- "commitRow": "SD8qLW_commitRow",
11707
- "refRowSpacer": "SD8qLW_refRowSpacer",
11708
- "signDel": "SD8qLW_signDel",
11709
- "commitLine": "SD8qLW_commitLine",
11710
- "tree": "SD8qLW_tree",
11711
- "themeGroup": "SD8qLW_themeGroup",
11712
- "headerDetached": "SD8qLW_headerDetached",
11713
- "bgEmpty": "SD8qLW_bgEmpty",
11714
- "commitLead": "SD8qLW_commitLead",
11715
- "headerViewRef": "SD8qLW_headerViewRef",
11716
- "fileActive": "SD8qLW_fileActive",
11717
- "scopeBtn": "SD8qLW_scopeBtn",
11718
- "commitCopy": "SD8qLW_commitCopy",
11719
- "syncLevel": "SD8qLW_syncLevel",
11720
12270
  "bgPreview": "SD8qLW_bgPreview",
11721
- "commitMessage": "SD8qLW_commitMessage",
11722
- "commitAmend": "SD8qLW_commitAmend",
11723
- "graphCell": "SD8qLW_graphCell",
11724
- "paneHead": "SD8qLW_paneHead",
11725
- "refFoot": "SD8qLW_refFoot",
11726
- "treeSub": "SD8qLW_treeSub",
11727
- "cardBranchName": "SD8qLW_cardBranchName",
11728
- "cardGlyph": "SD8qLW_cardGlyph",
11729
- "segmented": "SD8qLW_segmented",
11730
- "commitPopBody": "SD8qLW_commitPopBody",
11731
- "gsFade": "SD8qLW_gsFade",
11732
- "treeDirCounts": "SD8qLW_treeDirCounts",
11733
- "diffPane": "SD8qLW_diffPane",
11734
- "syncSpacer": "SD8qLW_syncSpacer",
11735
- "commitBox": "SD8qLW_commitBox",
11736
- "compareBar": "SD8qLW_compareBar",
12271
+ "funnelBoundBtnActive": "SD8qLW_funnelBoundBtnActive",
12272
+ "card": "SD8qLW_card",
11737
12273
  "wordDel": "SD8qLW_wordDel",
11738
- "code": "SD8qLW_code",
11739
- "treeIconDown": "SD8qLW_treeIconDown",
11740
- "btnClose": "SD8qLW_btnClose",
11741
- "treeIconGlyph": "SD8qLW_treeIconGlyph",
11742
- "treeTools": "SD8qLW_treeTools",
11743
- "scopeBtnActive": "SD8qLW_scopeBtnActive",
11744
- "filePath": "SD8qLW_filePath",
11745
- "commitActive": "SD8qLW_commitActive",
11746
- "btn": "SD8qLW_btn",
11747
- "treeActions": "SD8qLW_treeActions",
11748
- "lnOld": "SD8qLW_lnOld",
11749
- "btnAhead": "SD8qLW_btnAhead",
11750
- "lineContext": "SD8qLW_lineContext",
11751
- "treeRow": "SD8qLW_treeRow",
12274
+ "commitRefMore": "SD8qLW_commitRefMore",
12275
+ "themeRowSplit": "SD8qLW_themeRowSplit",
12276
+ "cardFiles": "SD8qLW_cardFiles",
12277
+ "confirmActions": "SD8qLW_confirmActions",
12278
+ "cal": "SD8qLW_cal",
11752
12279
  "fileStatus": "SD8qLW_fileStatus",
11753
- "commitPopTop": "SD8qLW_commitPopTop",
11754
- "settingsPop": "SD8qLW_settingsPop",
11755
- "chipDark": "SD8qLW_chipDark",
11756
- "refList": "SD8qLW_refList",
11757
- "commits": "SD8qLW_commits",
11758
- "cardDetached": "SD8qLW_cardDetached",
11759
- "treeDirLi": "SD8qLW_treeDirLi",
11760
- "signAdd": "SD8qLW_signAdd",
12280
+ "themeNote": "SD8qLW_themeNote",
12281
+ "btnCount": "SD8qLW_btnCount",
12282
+ "sliderValue": "SD8qLW_sliderValue",
12283
+ "bgEmpty": "SD8qLW_bgEmpty",
12284
+ "cardGlyph": "SD8qLW_cardGlyph",
12285
+ "filterChipRemove": "SD8qLW_filterChipRemove",
12286
+ "scopeHint": "SD8qLW_scopeHint",
12287
+ "confirmTitle": "SD8qLW_confirmTitle",
12288
+ "commitStaged": "SD8qLW_commitStaged",
12289
+ "fileCounts": "SD8qLW_fileCounts",
12290
+ "file": "SD8qLW_file",
12291
+ "btnPrimary": "SD8qLW_btnPrimary",
11761
12292
  "refRowActive": "SD8qLW_refRowActive",
12293
+ "funnelPop": "SD8qLW_funnelPop",
12294
+ "tree": "SD8qLW_tree",
12295
+ "overlay": "SD8qLW_overlay",
12296
+ "commitRef": "SD8qLW_commitRef",
12297
+ "headerTotalsDim": "SD8qLW_headerTotalsDim",
11762
12298
  "treeWrap": "SD8qLW_treeWrap",
12299
+ "miniBtnPrimary": "SD8qLW_miniBtnPrimary",
12300
+ "elide": "SD8qLW_elide",
12301
+ "funnelTab": "SD8qLW_funnelTab",
11763
12302
  "checkBox": "SD8qLW_checkBox",
11764
- "refGroup": "SD8qLW_refGroup",
11765
- "stUntracked": "SD8qLW_stUntracked",
12303
+ "resizer": "SD8qLW_resizer",
12304
+ "tabActive": "SD8qLW_tabActive",
12305
+ "pathDirGlyph": "SD8qLW_pathDirGlyph",
12306
+ "cardWt": "SD8qLW_cardWt",
11766
12307
  "themeLabel": "SD8qLW_themeLabel",
11767
- "commitWhen": "SD8qLW_commitWhen",
11768
- "diffPre": "SD8qLW_diffPre",
11769
- "btnPrimary": "SD8qLW_btnPrimary",
11770
- "headerTotalsAdd": "SD8qLW_headerTotalsAdd",
11771
- "paletteRow": "SD8qLW_paletteRow",
11772
- "themeDirty": "SD8qLW_themeDirty",
11773
- "commitPop": "SD8qLW_commitPop",
11774
- "lnNew": "SD8qLW_lnNew",
12308
+ "headerRight": "SD8qLW_headerRight",
12309
+ "signAdd": "SD8qLW_signAdd",
12310
+ "scopeBtn": "SD8qLW_scopeBtn",
12311
+ "cardBranchName": "SD8qLW_cardBranchName",
12312
+ "wtCurrent": "SD8qLW_wtCurrent",
12313
+ "menuPop": "SD8qLW_menuPop",
12314
+ "miniBtn": "SD8qLW_miniBtn",
11775
12315
  "segmentActive": "SD8qLW_segmentActive",
11776
- "theme": "SD8qLW_theme",
11777
- "checkMark": "SD8qLW_checkMark",
11778
- "stModified": "SD8qLW_stModified",
11779
- "btnIcon": "SD8qLW_btnIcon",
12316
+ "stDeleted": "SD8qLW_stDeleted",
12317
+ "refButton": "SD8qLW_refButton",
12318
+ "calGrid": "SD8qLW_calGrid",
12319
+ "segmented": "SD8qLW_segmented",
12320
+ "filterChips": "SD8qLW_filterChips",
12321
+ "pathNode": "SD8qLW_pathNode",
12322
+ "treeActions": "SD8qLW_treeActions",
12323
+ "diffPre": "SD8qLW_diffPre",
12324
+ "lineContext": "SD8qLW_lineContext",
12325
+ "funnelRow": "SD8qLW_funnelRow",
12326
+ "commitWhen": "SD8qLW_commitWhen",
12327
+ "funnelButton": "SD8qLW_funnelButton",
12328
+ "btnBehind": "SD8qLW_btnBehind",
11780
12329
  "gsSlide": "SD8qLW_gsSlide",
11781
- "paneDividerActive": "SD8qLW_paneDividerActive",
11782
- "tab": "SD8qLW_tab",
11783
- "commitsPane": "SD8qLW_commitsPane",
12330
+ "stModified": "SD8qLW_stModified",
12331
+ "btn": "SD8qLW_btn",
12332
+ "chipSystem": "SD8qLW_chipSystem",
11784
12333
  "checkMarkOn": "SD8qLW_checkMarkOn",
11785
- "tabActive": "SD8qLW_tabActive",
11786
- "card": "SD8qLW_card",
11787
- "treeLabel": "SD8qLW_treeLabel",
11788
- "headerPicker": "SD8qLW_headerPicker",
11789
- "elide": "SD8qLW_elide",
12334
+ "funnel": "SD8qLW_funnel",
12335
+ "refPop": "SD8qLW_refPop",
12336
+ "treeIconOn": "SD8qLW_treeIconOn",
12337
+ "scopeBtnActive": "SD8qLW_scopeBtnActive",
12338
+ "segmentChip": "SD8qLW_segmentChip",
12339
+ "paneDividerActive": "SD8qLW_paneDividerActive",
12340
+ "commitPop": "SD8qLW_commitPop",
12341
+ "opBanner": "SD8qLW_opBanner",
12342
+ "funnelFootCountOn": "SD8qLW_funnelFootCountOn",
12343
+ "cardAdded": "SD8qLW_cardAdded",
12344
+ "drawer": "SD8qLW_drawer",
12345
+ "headerTotalsAdd": "SD8qLW_headerTotalsAdd",
11790
12346
  "refPicker": "SD8qLW_refPicker",
11791
- "fileCountAdd": "SD8qLW_fileCountAdd",
11792
- "fileCountDel": "SD8qLW_fileCountDel",
11793
- "cardBranch": "SD8qLW_cardBranch",
11794
- "syncBar": "SD8qLW_syncBar",
11795
- "commitTop": "SD8qLW_commitTop",
11796
- "fileLi": "SD8qLW_fileLi",
11797
- "commitRef": "SD8qLW_commitRef",
12347
+ "refFoot": "SD8qLW_refFoot",
12348
+ "treeTools": "SD8qLW_treeTools",
12349
+ "commitAmend": "SD8qLW_commitAmend",
12350
+ "pullGroup": "SD8qLW_pullGroup",
12351
+ "headerViewRef": "SD8qLW_headerViewRef",
12352
+ "refRow": "SD8qLW_refRow",
12353
+ "stRenamed": "SD8qLW_stRenamed",
12354
+ "funnelPresetActive": "SD8qLW_funnelPresetActive",
12355
+ "commitPopBody": "SD8qLW_commitPopBody",
12356
+ "renameLine": "SD8qLW_renameLine",
12357
+ "signDel": "SD8qLW_signDel",
12358
+ "funnelTabs": "SD8qLW_funnelTabs",
12359
+ "refGroup": "SD8qLW_refGroup",
12360
+ "commitsFoot": "SD8qLW_commitsFoot",
12361
+ "commitsPane": "SD8qLW_commitsPane",
12362
+ "funnelChevron": "SD8qLW_funnelChevron",
12363
+ "cssArea": "SD8qLW_cssArea",
11798
12364
  "refEmpty": "SD8qLW_refEmpty",
11799
- "resizer": "SD8qLW_resizer",
11800
- "drawer": "SD8qLW_drawer",
11801
- "themeRowSplit": "SD8qLW_themeRowSplit",
11802
- "compareArrow": "SD8qLW_compareArrow",
12365
+ "checkMark": "SD8qLW_checkMark",
12366
+ "themeGroup": "SD8qLW_themeGroup",
11803
12367
  "paneDivider": "SD8qLW_paneDivider",
12368
+ "elideHead": "SD8qLW_elideHead",
12369
+ "funnelFoot": "SD8qLW_funnelFoot",
12370
+ "fileCountDel": "SD8qLW_fileCountDel",
12371
+ "filePath": "SD8qLW_filePath",
12372
+ "cardDetached": "SD8qLW_cardDetached",
12373
+ "refLabel": "SD8qLW_refLabel",
12374
+ "stAdded": "SD8qLW_stAdded",
12375
+ "body": "SD8qLW_body",
12376
+ "filterChip": "SD8qLW_filterChip",
12377
+ "calNav": "SD8qLW_calNav",
12378
+ "calOut": "SD8qLW_calOut",
12379
+ "syncSpacer": "SD8qLW_syncSpacer",
12380
+ "treeDirLi": "SD8qLW_treeDirLi",
12381
+ "paneHead": "SD8qLW_paneHead",
12382
+ "chevronOpen": "SD8qLW_chevronOpen",
11804
12383
  "headerLeft": "SD8qLW_headerLeft",
11805
- "commitPopSubject": "SD8qLW_commitPopSubject",
11806
- "menuPop": "SD8qLW_menuPop",
11807
- "treeDirActive": "SD8qLW_treeDirActive",
12384
+ "scopeRow": "SD8qLW_scopeRow",
12385
+ "pathFileGlyph": "SD8qLW_pathFileGlyph",
12386
+ "treeEmpty": "SD8qLW_treeEmpty",
12387
+ "commitHasBody": "SD8qLW_commitHasBody",
12388
+ "lineDel": "SD8qLW_lineDel",
12389
+ "fileLi": "SD8qLW_fileLi",
12390
+ "segment": "SD8qLW_segment",
12391
+ "syncBar": "SD8qLW_syncBar",
12392
+ "treeLead": "SD8qLW_treeLead",
12393
+ "lineAdd": "SD8qLW_lineAdd",
11808
12394
  "line": "SD8qLW_line",
11809
- "miniBtn": "SD8qLW_miniBtn",
11810
- "refCaret": "SD8qLW_refCaret",
12395
+ "cardSep": "SD8qLW_cardSep",
12396
+ "gutter": "SD8qLW_gutter",
12397
+ "commitBox": "SD8qLW_commitBox",
12398
+ "calTitle": "SD8qLW_calTitle",
12399
+ "treeRow": "SD8qLW_treeRow",
12400
+ "paletteRowActive": "SD8qLW_paletteRowActive",
12401
+ "header": "SD8qLW_header",
12402
+ "commitMessage": "SD8qLW_commitMessage",
12403
+ "treeLabel": "SD8qLW_treeLabel",
12404
+ "lnOld": "SD8qLW_lnOld",
12405
+ "funnelBoundClear": "SD8qLW_funnelBoundClear",
12406
+ "headerPicker": "SD8qLW_headerPicker",
12407
+ "fileActive": "SD8qLW_fileActive",
12408
+ "chipDark": "SD8qLW_chipDark",
12409
+ "commitPopTop": "SD8qLW_commitPopTop",
12410
+ "treeDirCount": "SD8qLW_treeDirCount",
12411
+ "commitLead": "SD8qLW_commitLead",
12412
+ "paletteRow": "SD8qLW_paletteRow",
12413
+ "gsFade": "SD8qLW_gsFade",
12414
+ "filterClear": "SD8qLW_filterClear",
12415
+ "funnelTabActive": "SD8qLW_funnelTabActive",
12416
+ "opBannerBad": "SD8qLW_opBannerBad",
12417
+ "headerTotalsDel": "SD8qLW_headerTotalsDel",
12418
+ "refRowName": "SD8qLW_refRowName",
12419
+ "funnelName": "SD8qLW_funnelName",
12420
+ "elideTail": "SD8qLW_elideTail",
12421
+ "fileBinary": "SD8qLW_fileBinary",
12422
+ "treeIconDown": "SD8qLW_treeIconDown",
12423
+ "stUntracked": "SD8qLW_stUntracked",
12424
+ "confirmScrim": "SD8qLW_confirmScrim",
12425
+ "headerPathMain": "SD8qLW_headerPathMain",
12426
+ "theme": "SD8qLW_theme",
12427
+ "funnelBoundVal": "SD8qLW_funnelBoundVal",
12428
+ "sliderRow": "SD8qLW_sliderRow",
12429
+ "treeFilter": "SD8qLW_treeFilter",
12430
+ "commitFilter": "SD8qLW_commitFilter",
12431
+ "funnelBoundKey": "SD8qLW_funnelBoundKey",
12432
+ "graphCell": "SD8qLW_graphCell",
12433
+ "code": "SD8qLW_code",
12434
+ "opBannerOk": "SD8qLW_opBannerOk",
12435
+ "compareArrow": "SD8qLW_compareArrow",
12436
+ "treeSub": "SD8qLW_treeSub",
12437
+ "pathChildren": "SD8qLW_pathChildren",
12438
+ "commitHash": "SD8qLW_commitHash",
12439
+ "confirmBox": "SD8qLW_confirmBox",
12440
+ "treeFilterInput": "SD8qLW_treeFilterInput",
12441
+ "treeIcon": "SD8qLW_treeIcon",
12442
+ "calIn": "SD8qLW_calIn",
12443
+ "commitLine": "SD8qLW_commitLine",
12444
+ "chevron": "SD8qLW_chevron",
12445
+ "themeDirty": "SD8qLW_themeDirty",
12446
+ "refValue": "SD8qLW_refValue",
12447
+ "commits": "SD8qLW_commits",
12448
+ "funnelBounds": "SD8qLW_funnelBounds",
12449
+ "treeDir": "SD8qLW_treeDir",
12450
+ "headerBranch": "SD8qLW_headerBranch",
12451
+ "treeFilterClear": "SD8qLW_treeFilterClear",
12452
+ "commitTop": "SD8qLW_commitTop",
12453
+ "empty": "SD8qLW_empty",
12454
+ "swatch": "SD8qLW_swatch",
12455
+ "calMark": "SD8qLW_calMark",
12456
+ "commitPopSubject": "SD8qLW_commitPopSubject",
12457
+ "tab": "SD8qLW_tab",
12458
+ "treeDirName": "SD8qLW_treeDirName",
12459
+ "diffPane": "SD8qLW_diffPane",
12460
+ "commitActive": "SD8qLW_commitActive",
12461
+ "commitSubjectRow": "SD8qLW_commitSubjectRow",
12462
+ "refRowSpacer": "SD8qLW_refRowSpacer",
12463
+ "fileDiscard": "SD8qLW_fileDiscard",
11811
12464
  "checkMarkPartial": "SD8qLW_checkMarkPartial",
11812
- "pullGroup": "SD8qLW_pullGroup",
11813
- "commitRefMore": "SD8qLW_commitRefMore",
11814
- "refPop": "SD8qLW_refPop",
11815
- "file": "SD8qLW_file"
12465
+ "calToday": "SD8qLW_calToday",
12466
+ "btnClose": "SD8qLW_btnClose",
12467
+ "filterChipLabel": "SD8qLW_filterChipLabel",
12468
+ "headerTotals": "SD8qLW_headerTotals",
12469
+ "themeRail": "SD8qLW_themeRail",
12470
+ "treeCol": "SD8qLW_treeCol",
12471
+ "cardDeleted": "SD8qLW_cardDeleted",
12472
+ "treeIconGlyph": "SD8qLW_treeIconGlyph",
12473
+ "paneTitle": "SD8qLW_paneTitle",
12474
+ "settingsPop": "SD8qLW_settingsPop",
12475
+ "commitCopy": "SD8qLW_commitCopy",
12476
+ "commitsSentinel": "SD8qLW_commitsSentinel",
12477
+ "commitAuthor": "SD8qLW_commitAuthor",
12478
+ "wordAdd": "SD8qLW_wordAdd",
12479
+ "cardAhead": "SD8qLW_cardAhead",
12480
+ "funnelBoundRows": "SD8qLW_funnelBoundRows",
12481
+ "refSearch": "SD8qLW_refSearch",
12482
+ "headerDetached": "SD8qLW_headerDetached",
12483
+ "btnIcon": "SD8qLW_btnIcon",
12484
+ "funnelPane": "SD8qLW_funnelPane",
12485
+ "refList": "SD8qLW_refList",
12486
+ "funnelFootCount": "SD8qLW_funnelFootCount",
12487
+ "funnelCount": "SD8qLW_funnelCount",
12488
+ "cardBehind": "SD8qLW_cardBehind",
12489
+ "calWeek": "SD8qLW_calWeek",
12490
+ "funnelBoundBtn": "SD8qLW_funnelBoundBtn",
12491
+ "funnelCaption": "SD8qLW_funnelCaption",
12492
+ "funnelList": "SD8qLW_funnelList",
12493
+ "btnDanger": "SD8qLW_btnDanger",
12494
+ "fileCountAdd": "SD8qLW_fileCountAdd",
12495
+ "compareBar": "SD8qLW_compareBar",
12496
+ "syncLevel": "SD8qLW_syncLevel",
12497
+ "tabs": "SD8qLW_tabs",
12498
+ "resizerActive": "SD8qLW_resizerActive",
12499
+ "commitBtn": "SD8qLW_commitBtn",
12500
+ "funnelPresets": "SD8qLW_funnelPresets",
12501
+ "commitRow": "SD8qLW_commitRow",
12502
+ "funnelButtonActive": "SD8qLW_funnelButtonActive",
12503
+ "funnelPreset": "SD8qLW_funnelPreset",
12504
+ "funnelBoundValSet": "SD8qLW_funnelBoundValSet",
12505
+ "refCaret": "SD8qLW_refCaret",
12506
+ "funnelBoundRow": "SD8qLW_funnelBoundRow"
11816
12507
  };
11817
12508
  //#endregion
11818
12509
  //#region src/client/GitWorkbenchPanel.tsx
@@ -12037,7 +12728,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
12037
12728
  renamed: GitWorkbenchPanel_module_css_default.stRenamed,
12038
12729
  deleted: GitWorkbenchPanel_module_css_default.stDeleted
12039
12730
  };
12040
- function GitWorkbenchPanel({ sessionId, useSessions, t, fetchStats, fetchFileDiff, fetchWorktreeStatus, fetchSessionBinding, fetchCommitStats, fetchCommits, fetchCompare, fetchStyle, saveStyle, fetchSync, runGitOp }) {
12731
+ function GitWorkbenchPanel({ sessionId, useSessions, t, fetchStats, fetchFileDiff, fetchWorktreeStatus, fetchSessionBinding, fetchCommitStats, fetchCommits, fetchAuthors, fetchRepoTree, fetchCompare, fetchStyle, saveStyle, fetchSync, runGitOp, fetchDiscardPlan }) {
12041
12732
  const worktreePath = useSessions((state) => state?.byId?.[sessionId]?.cwd);
12042
12733
  /** Whether the session's agent has a turn in flight — the store mirrors it
12043
12734
  * live, so it is the signal for polling faster while there is something to
@@ -12082,6 +12773,22 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
12082
12773
  /** First page of the history list in flight — the pane says "loading", not
12083
12774
  * "no commit history", which is a claim about the repository. */
12084
12775
  const [historyLoading, setHistoryLoading] = (0, react.useState)(false);
12776
+ /** Why the history list is empty when it is git's word, not the log's: a
12777
+ * bad filter pattern or date, with the stderr tail to say so. */
12778
+ const [historyError, setHistoryError] = (0, react.useState)(null);
12779
+ /** The history filter box's raw text. Parsed into the LogFilter the host
12780
+ * compiles into git log arguments — the funnel popup writes here too: one
12781
+ * grammar, one filter, however the criterion arrived. */
12782
+ const [historyQuery, setHistoryQuery] = (0, react.useState)("");
12783
+ const historyFilterKey = serializeLogQuery(parseLogQuery(historyQuery));
12784
+ /** Debounced by KEY, not by text: "liam " and "liam" are the same query and
12785
+ * must not refetch. 300ms is a keystroke's pause, not a page's wait. */
12786
+ const [liveFilterKey, setLiveFilterKey] = (0, react.useState)("");
12787
+ (0, react.useEffect)(() => {
12788
+ const id = window.setTimeout(() => setLiveFilterKey(historyFilterKey), 300);
12789
+ return () => window.clearTimeout(id);
12790
+ }, [historyFilterKey]);
12791
+ const liveFilter = (0, react.useMemo)(() => liveFilterKey.length === 0 ? emptyQueryFilter() : parseLogQuery(liveFilterKey), [liveFilterKey]);
12085
12792
  const [loadingMore, setLoadingMore] = (0, react.useState)(false);
12086
12793
  /** In-flight marker for paging, read synchronously — see {@link loadMoreCommits}. */
12087
12794
  const loadingRef = (0, react.useRef)(false);
@@ -12409,12 +13116,14 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
12409
13116
  setCommitHash(null);
12410
13117
  setCommitStats(null);
12411
13118
  setHistoryLoading(true);
12412
- fetchCommits(statsPath, effectiveHistoryRef, 0, HISTORY_PAGE, ctrl.signal).then((page) => {
13119
+ setHistoryError(null);
13120
+ fetchCommits(statsPath, effectiveHistoryRef, 0, HISTORY_PAGE, liveFilter, ctrl.signal).then((page) => {
12413
13121
  if (!alive) return;
12414
13122
  setHistoryLoading(false);
12415
13123
  if (page === null) return;
12416
13124
  setHistoryCommits(page.commits);
12417
13125
  setHistoryHasMore(page.hasMore);
13126
+ setHistoryError(page.error ?? null);
12418
13127
  }).catch(() => {
12419
13128
  if (alive) setHistoryLoading(false);
12420
13129
  });
@@ -12427,7 +13136,8 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
12427
13136
  statsPath,
12428
13137
  effectiveHistoryRef,
12429
13138
  fetchCommits,
12430
- gen
13139
+ gen,
13140
+ liveFilter
12431
13141
  ]);
12432
13142
  (0, react.useEffect)(() => {
12433
13143
  if (tab !== "history" || commitHash !== null) return;
@@ -12553,6 +13263,25 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
12553
13263
  refresh();
12554
13264
  }
12555
13265
  };
13266
+ /**
13267
+ * Put a failure the drawer produced itself into the same banner git failures
13268
+ * use.
13269
+ *
13270
+ * Roll-back is the caller: it asks the host what a file's roll-back would do
13271
+ * before it does anything, and that question can fail on its own, with no
13272
+ * `runOp` behind it to report through. Everything else the drawer does is
13273
+ * either a git call or has a visible result of its own.
13274
+ */
13275
+ const reportOpError = (op, error) => {
13276
+ setOpResult({
13277
+ op,
13278
+ result: {
13279
+ ok: false,
13280
+ failure: "unknown",
13281
+ error
13282
+ }
13283
+ });
13284
+ };
12556
13285
  /** Wait for the git lock, so a queued tick batch waits out a heavy
12557
13286
  * operation instead of being refused by it. */
12558
13287
  const waitNotBusy = async () => {
@@ -12718,7 +13447,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
12718
13447
  loadingRef.current = true;
12719
13448
  setLoadingMore(true);
12720
13449
  const ctrl = new AbortController();
12721
- fetchCommits(statsPath, effectiveHistoryRef, historyCommits.length, HISTORY_PAGE, ctrl.signal).then((page) => {
13450
+ fetchCommits(statsPath, effectiveHistoryRef, historyCommits.length, HISTORY_PAGE, liveFilter, ctrl.signal).then((page) => {
12722
13451
  if (page === null) return;
12723
13452
  setHistoryCommits((prev) => [...prev, ...page.commits]);
12724
13453
  setHistoryHasMore(page.hasMore);
@@ -12760,6 +13489,11 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
12760
13489
  onLoadMoreCommits: loadMoreCommits,
12761
13490
  historyRef: effectiveHistoryRef,
12762
13491
  onHistoryRef: setHistoryRef,
13492
+ historyQuery,
13493
+ onHistoryQuery: setHistoryQuery,
13494
+ historyError,
13495
+ fetchAuthors,
13496
+ fetchRepoTree,
12763
13497
  branches,
12764
13498
  worktreeBranches,
12765
13499
  branchesTruncated,
@@ -12801,6 +13535,8 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
12801
13535
  busy,
12802
13536
  opResult,
12803
13537
  runOp,
13538
+ fetchDiscardPlan,
13539
+ onOpError: reportOpError,
12804
13540
  pendingTicks,
12805
13541
  onTick: queueTicks,
12806
13542
  fetchFileDiff: fetchDiffForView,
@@ -12923,7 +13659,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
12923
13659
  ]
12924
13660
  });
12925
13661
  }
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 }) {
13662
+ 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, onOpError, pendingTicks, onTick, fetchFileDiff, viewKey, gen, collapsed, onCollapsedChange }) {
12927
13663
  const body = shown ?? EMPTY_STATS;
12928
13664
  /** The file list with ticks still awaiting git laid over them. The tree and
12929
13665
  * the commit box read this, so a click moves its box and the "N ticked"
@@ -12934,8 +13670,16 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
12934
13670
  * while a refresh lands over it. Derived once and handed to both the header
12935
13671
  * and the tree: spelling it twice is what let the header get it wrong. */
12936
13672
  const pending = showsPending(treeLoading, body.files.length);
12937
- const active = selected !== null && body.files.some((file) => file.path === selected) ? selected : body.files[0]?.path ?? null;
13673
+ /** The history filter's paths, which decide what a commit OPENS on. Only the
13674
+ * history tab has one: the changes and compare trees are not filtered, and
13675
+ * steering their default selection by a query the reader cannot see from
13676
+ * there would be a spooky action. */
13677
+ const activeFilterPaths = (0, react.useMemo)(() => tab === "history" ? parseLogQuery(historyQuery).paths : NO_PATHS, [tab, historyQuery]);
13678
+ const active = preferredFile(body.files, activeFilterPaths, selected);
12938
13679
  const activeFile = body.files.find((file) => file.path === active) ?? null;
13680
+ /** The file whose roll-back is being asked about; `plan` is null while the
13681
+ * host is still being asked what it would do. */
13682
+ const [discardPending, setDiscardPending] = (0, react.useState)(null);
12939
13683
  const [fetched, setFetched] = (0, react.useState)(/* @__PURE__ */ new Map());
12940
13684
  const [loading, setLoading] = (0, react.useState)(false);
12941
13685
  const bundled = active === null ? "" : segments.get(active) ?? "";
@@ -12967,6 +13711,56 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
12967
13711
  setFetched(/* @__PURE__ */ new Map());
12968
13712
  }, [gen]);
12969
13713
  const selectAndReveal = (path) => onSelect(path);
13714
+ /**
13715
+ * Roll-back, in two steps that are deliberately not one.
13716
+ *
13717
+ * The click asks the host what rolling this file back would DO, and only the
13718
+ * answer opens the dialog. Deriving the wording from the clicked row instead
13719
+ * would mean describing a file as the last poll saw it: the difference
13720
+ * between "goes back to its committed content" and "leaves the disk and
13721
+ * cannot come back" is the entire subject of the question being asked, and it
13722
+ * is exactly the thing a stale row gets wrong.
13723
+ *
13724
+ * `recover` — a deleted file coming back — shows no dialog at all. It loses
13725
+ * nothing, and a confirmation in front of a pure gain is how people learn to
13726
+ * dismiss confirmations without reading them.
13727
+ *
13728
+ * Every other answer is `nextAfterPlan`'s to classify, and the one it exists
13729
+ * for is failure: a plan that never arrives reports, where it used to leave
13730
+ * the reader looking at a button that did nothing.
13731
+ */
13732
+ const askDiscard = (file) => {
13733
+ setDiscardPending({
13734
+ file,
13735
+ plan: null
13736
+ });
13737
+ (async () => {
13738
+ const next = nextAfterPlan(await fetchDiscardPlan(statsPath, file.path, new AbortController().signal));
13739
+ if (next.kind === "confirm") {
13740
+ setDiscardPending({
13741
+ file,
13742
+ plan: next.plan
13743
+ });
13744
+ return;
13745
+ }
13746
+ setDiscardPending(null);
13747
+ if (next.kind === "run") runOp("discardFile", {
13748
+ path: file.path,
13749
+ expectedEffect: next.effect
13750
+ });
13751
+ else if (next.kind === "refresh") onRefresh();
13752
+ else onOpError("discardFile", next.error);
13753
+ })();
13754
+ };
13755
+ const confirmDiscard = () => {
13756
+ const pending = discardPending;
13757
+ if (pending === null || pending.plan === null) return;
13758
+ setDiscardPending(null);
13759
+ runOp("discardFile", {
13760
+ path: pending.file.path,
13761
+ expectedEffect: pending.plan.effect
13762
+ });
13763
+ };
12970
13764
  const drawerRef = (0, react.useRef)(null);
12971
13765
  const commitsRef = (0, react.useRef)(null);
12972
13766
  const treeRef = (0, react.useRef)(null);
@@ -13202,7 +13996,8 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
13202
13996
  branches,
13203
13997
  worktreeBranches,
13204
13998
  truncated: branchesTruncated,
13205
- onPick: onHistoryRef
13999
+ onPick: onHistoryRef,
14000
+ allLabel: t("allBranches")
13206
14001
  })
13207
14002
  }) : null,
13208
14003
  tab === "changes" && sync !== null && sync.hasRemote ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SyncBar, {
@@ -13231,7 +14026,14 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
13231
14026
  onSelect: onSelectCommit,
13232
14027
  hasMore: hasMoreCommits,
13233
14028
  loadingMore,
13234
- onLoadMore: onLoadMoreCommits
14029
+ onLoadMore: onLoadMoreCommits,
14030
+ query: historyQuery,
14031
+ onQueryChange: onHistoryQuery,
14032
+ error: historyError,
14033
+ statsPath,
14034
+ refName: historyRef,
14035
+ fetchAuthors,
14036
+ fetchRepoTree
13235
14037
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PaneDivider, {
13236
14038
  label: t("resizeCommits"),
13237
14039
  onDrag: paneDrag("commits", commitsRef)
@@ -13243,6 +14045,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
13243
14045
  "data-gs-part": "tree",
13244
14046
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FileTree, {
13245
14047
  t,
14048
+ scopeKey: viewKey,
13246
14049
  loading: pending,
13247
14050
  lead: tab === "changes" ? t("workingTree") : void 0,
13248
14051
  files: tickedFiles,
@@ -13255,6 +14058,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
13255
14058
  const paths = pathsFor(checked, action);
13256
14059
  if (paths.length > 0) onTick(action, paths);
13257
14060
  } : void 0,
14061
+ onDiscard: tab === "changes" ? askDiscard : void 0,
13258
14062
  footer: tab === "changes" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CommitBox, {
13259
14063
  t,
13260
14064
  files: tickedFiles,
@@ -13303,6 +14107,86 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
13303
14107
  })]
13304
14108
  })
13305
14109
  ]
14110
+ }),
14111
+ discardPending?.plan != null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DiscardConfirm, {
14112
+ t,
14113
+ file: discardPending.file,
14114
+ plan: discardPending.plan,
14115
+ onCancel: () => setDiscardPending(null),
14116
+ onConfirm: confirmDiscard
14117
+ }) : null
14118
+ ]
14119
+ })
14120
+ });
14121
+ }
14122
+ /**
14123
+ * The one dialog in this drawer, because this is the one act it cannot undo.
14124
+ *
14125
+ * It never asks a generic "are you sure": the body names the file and states
14126
+ * which of the three consequences is about to happen, in the host's own reading
14127
+ * of that file taken moments ago. Cancel holds the initial focus and Escape
14128
+ * closes, because the default answer to an irreversible question is no.
14129
+ *
14130
+ * There is deliberately no "don't ask again". This is the only path in the
14131
+ * drawer with nothing behind it, and a checkbox whose whole function is to
14132
+ * switch off the last guard is a feature that eventually gets clicked.
14133
+ */
14134
+ function DiscardConfirm({ t, file, plan, onCancel, onConfirm }) {
14135
+ const cancelRef = (0, react.useRef)(null);
14136
+ (0, react.useEffect)(() => {
14137
+ cancelRef.current?.focus();
14138
+ }, []);
14139
+ (0, react.useEffect)(() => {
14140
+ const onKey = (event) => {
14141
+ if (event.key !== "Escape") return;
14142
+ event.stopPropagation();
14143
+ onCancel();
14144
+ };
14145
+ window.addEventListener("keydown", onKey, true);
14146
+ return () => {
14147
+ window.removeEventListener("keydown", onKey, true);
14148
+ };
14149
+ }, [onCancel]);
14150
+ const body = plan.effect === "delete" ? t("discardBodyDelete", { path: file.path }) : plan.effect === "unrename" ? t("discardBodyUnrename", {
14151
+ path: file.path,
14152
+ previousPath: plan.previousPath ?? ""
14153
+ }) : t("discardBodyRestore", {
14154
+ path: file.path,
14155
+ added: file.addedLines,
14156
+ deleted: file.deletedLines
14157
+ });
14158
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
14159
+ className: GitWorkbenchPanel_module_css_default.confirmScrim,
14160
+ onClick: onCancel,
14161
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
14162
+ className: GitWorkbenchPanel_module_css_default.confirmBox,
14163
+ role: "alertdialog",
14164
+ "aria-modal": "true",
14165
+ "aria-label": t("discardTitle"),
14166
+ onClick: (event) => event.stopPropagation(),
14167
+ children: [
14168
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
14169
+ className: GitWorkbenchPanel_module_css_default.confirmTitle,
14170
+ children: t("discardTitle")
14171
+ }),
14172
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
14173
+ className: GitWorkbenchPanel_module_css_default.confirmBody,
14174
+ children: body
14175
+ }),
14176
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
14177
+ className: GitWorkbenchPanel_module_css_default.confirmActions,
14178
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
14179
+ ref: cancelRef,
14180
+ type: "button",
14181
+ className: GitWorkbenchPanel_module_css_default.btn,
14182
+ onClick: onCancel,
14183
+ children: t("discardCancel")
14184
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
14185
+ type: "button",
14186
+ className: `${GitWorkbenchPanel_module_css_default.btn} ${GitWorkbenchPanel_module_css_default.btnDanger}`,
14187
+ onClick: onConfirm,
14188
+ children: t("discardConfirm")
14189
+ })]
13306
14190
  })
13307
14191
  ]
13308
14192
  })
@@ -13835,7 +14719,12 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
13835
14719
  * checked-out branch is the likeliest thing to want. Enter takes the first
13836
14720
  * match, so a distinctive substring plus Enter reaches any branch in the list.
13837
14721
  */
13838
- function RefPicker({ t, label, value, branches, worktreeBranches, truncated, onPick }) {
14722
+ /** Sentinel ref meaning "walk every ref" same string the host special-cases
14723
+ * into `--all`. A real ref cannot begin with a dash, so it collides with
14724
+ * nothing; defined separately on both halves (client bundles import no host
14725
+ * values), tied by this comment and the probe. */
14726
+ const ALL_REFS = "--all";
14727
+ function RefPicker({ t, label, value, branches, worktreeBranches, truncated, onPick, allLabel }) {
13839
14728
  const [open, setOpen] = (0, react.useState)(false);
13840
14729
  const [query, setQuery] = (0, react.useState)("");
13841
14730
  const rootRef = useDismissable(open, setOpen);
@@ -13876,7 +14765,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
13876
14765
  title: value.length > 0 ? value : void 0,
13877
14766
  onClick: () => setOpen((isOpen) => !isOpen),
13878
14767
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Elided, {
13879
- text: value.length > 0 ? value : "—",
14768
+ text: value === ALL_REFS && allLabel !== void 0 ? allLabel : value.length > 0 ? value : "—",
13880
14769
  className: GitWorkbenchPanel_module_css_default.refValue
13881
14770
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
13882
14771
  className: GitWorkbenchPanel_module_css_default.refCaret,
@@ -13901,6 +14790,18 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
13901
14790
  role: "listbox",
13902
14791
  "aria-label": label,
13903
14792
  children: [
14793
+ allLabel !== void 0 && (needle.length === 0 || allLabel.toLowerCase().includes(needle)) ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
14794
+ type: "button",
14795
+ role: "option",
14796
+ "aria-selected": value === ALL_REFS,
14797
+ className: value === ALL_REFS ? `${GitWorkbenchPanel_module_css_default.refRow} ${GitWorkbenchPanel_module_css_default.refRowActive}` : GitWorkbenchPanel_module_css_default.refRow,
14798
+ title: allLabel,
14799
+ onClick: () => choose(ALL_REFS),
14800
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: GitWorkbenchPanel_module_css_default.refRowSpacer }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Elided, {
14801
+ text: allLabel,
14802
+ className: GitWorkbenchPanel_module_css_default.refRowName
14803
+ })]
14804
+ }) : null,
13904
14805
  checkedOut.length > 0 && rest.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
13905
14806
  className: GitWorkbenchPanel_module_css_default.refGroup,
13906
14807
  children: t("refWorktrees")
@@ -13911,7 +14812,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
13911
14812
  children: t("refBranches")
13912
14813
  }) : null,
13913
14814
  rest.map((ref) => row(ref, false)),
13914
- matched.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
14815
+ matched.length === 0 && !(allLabel !== void 0 && needle.length > 0 && allLabel.toLowerCase().includes(needle)) ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
13915
14816
  className: GitWorkbenchPanel_module_css_default.refEmpty,
13916
14817
  children: t("refNone")
13917
14818
  }) : null
@@ -14014,6 +14915,53 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
14014
14915
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: SYNC_GLYPH[of] })
14015
14916
  });
14016
14917
  }
14918
+ /**
14919
+ * Filter this list: a magnifier, not the funnel above the commit list. The two
14920
+ * are deliberately different glyphs because they do different things — the
14921
+ * funnel asks git for a different set of commits, this only hides rows already
14922
+ * on screen — and the drawer shows both at once.
14923
+ */
14924
+ function FilterGlyph() {
14925
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
14926
+ width: "13",
14927
+ height: "13",
14928
+ viewBox: "0 0 16 16",
14929
+ fill: "none",
14930
+ stroke: "currentColor",
14931
+ strokeWidth: "1.25",
14932
+ strokeLinecap: "round",
14933
+ strokeLinejoin: "round",
14934
+ "aria-hidden": "true",
14935
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("circle", {
14936
+ cx: "7",
14937
+ cy: "7",
14938
+ r: "4"
14939
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M10 10l3.5 3.5" })]
14940
+ });
14941
+ }
14942
+ /** Nothing folded. A constant so the filtered tree does not allocate a new Set
14943
+ * on every render and re-run `TreeChildren`'s memo. */
14944
+ const EMPTY_COLLAPSED = /* @__PURE__ */ new Set();
14945
+ /**
14946
+ * Roll back: the counter-clockwise arc every editor and VCS uses for undo,
14947
+ * drawn in the same New UI idiom as the node glyphs beside it — 16px grid,
14948
+ * 1px stroke, no fill — so the row does not mix an outlined file icon with a
14949
+ * solid action icon.
14950
+ */
14951
+ function RollbackGlyph() {
14952
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
14953
+ width: "14",
14954
+ height: "14",
14955
+ viewBox: "0 0 16 16",
14956
+ fill: "none",
14957
+ stroke: "currentColor",
14958
+ strokeWidth: "1.25",
14959
+ strokeLinecap: "round",
14960
+ strokeLinejoin: "round",
14961
+ "aria-hidden": "true",
14962
+ 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" })]
14963
+ });
14964
+ }
14017
14965
  const PULL_MODES = [
14018
14966
  "ff-only",
14019
14967
  "rebase",
@@ -14388,6 +15336,9 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
14388
15336
  const enterTimer = (0, react.useRef)(0);
14389
15337
  const leaveTimer = (0, react.useRef)(0);
14390
15338
  const body = commit.body ?? "";
15339
+ const authorName = commit.authorName ?? "";
15340
+ const committerName = commit.committerName ?? "";
15341
+ const exactDate = formatCommitDate(commit.dateIso ?? "");
14391
15342
  const cancel = () => {
14392
15343
  window.clearTimeout(enterTimer.current);
14393
15344
  window.clearTimeout(leaveTimer.current);
@@ -14438,13 +15389,20 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
14438
15389
  onMouseLeave: hide,
14439
15390
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
14440
15391
  className: GitWorkbenchPanel_module_css_default.commitTop,
14441
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", {
14442
- className: GitWorkbenchPanel_module_css_default.commitHash,
14443
- children: commit.hash
14444
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
14445
- className: GitWorkbenchPanel_module_css_default.commitWhen,
14446
- children: commit.when
14447
- })]
15392
+ children: [
15393
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", {
15394
+ className: GitWorkbenchPanel_module_css_default.commitHash,
15395
+ children: commit.hash
15396
+ }),
15397
+ authorName.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
15398
+ className: GitWorkbenchPanel_module_css_default.commitAuthor,
15399
+ children: authorName
15400
+ }) : null,
15401
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
15402
+ className: GitWorkbenchPanel_module_css_default.commitWhen,
15403
+ children: commit.when
15404
+ })
15405
+ ]
14448
15406
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
14449
15407
  className: GitWorkbenchPanel_module_css_default.commitSubjectRow,
14450
15408
  children: [
@@ -14500,17 +15458,244 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
14500
15458
  text: commitMessageText(commit)
14501
15459
  })
14502
15460
  ]
14503
- }),
14504
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
14505
- className: GitWorkbenchPanel_module_css_default.commitPopSubject,
14506
- children: commit.subject
14507
- }),
14508
- body.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
14509
- className: GitWorkbenchPanel_module_css_default.commitPopBody,
14510
- children: body
14511
- }) : null
14512
- ]
14513
- }), host) : null] });
15461
+ }),
15462
+ authorName.length > 0 || committerName.length > 0 || exactDate.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
15463
+ className: GitWorkbenchPanel_module_css_default.commitPopMeta,
15464
+ children: [
15465
+ authorName.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
15466
+ t("commitAuthor"),
15467
+ ": ",
15468
+ authorName
15469
+ ] }) : null,
15470
+ committerName.length > 0 && committerName !== authorName ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
15471
+ t("commitCommitter"),
15472
+ ": ",
15473
+ committerName
15474
+ ] }) : null,
15475
+ exactDate.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
15476
+ t("commitDate"),
15477
+ ": ",
15478
+ exactDate
15479
+ ] }) : null
15480
+ ]
15481
+ }) : null,
15482
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
15483
+ className: GitWorkbenchPanel_module_css_default.commitPopSubject,
15484
+ children: commit.subject
15485
+ }),
15486
+ body.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
15487
+ className: GitWorkbenchPanel_module_css_default.commitPopBody,
15488
+ children: body
15489
+ }) : null
15490
+ ]
15491
+ }), host) : null] });
15492
+ }
15493
+ /** The filter's own calendar — a hand-rolled 6×7 Monday-first grid (pure
15494
+ * arithmetic in `calendar.ts`), because the native date input renders as the
15495
+ * platform's bare widget and the bundle's purity gate forbids pulling in a
15496
+ * library. Picking a day hands `yyyy-mm-dd` to the bound the segmented
15497
+ * control armed; the host expands it to the whole day. */
15498
+ function FilterCalendar({ year, month, after, before, locale, onPick, onShift }) {
15499
+ const grid = monthGrid(year, month, localTodayIso());
15500
+ const title = new Intl.DateTimeFormat(locale, {
15501
+ year: "numeric",
15502
+ month: "long"
15503
+ }).format(new Date(year, month, 1));
15504
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
15505
+ className: GitWorkbenchPanel_module_css_default.cal,
15506
+ children: [
15507
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
15508
+ className: GitWorkbenchPanel_module_css_default.calHead,
15509
+ children: [
15510
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
15511
+ type: "button",
15512
+ className: GitWorkbenchPanel_module_css_default.calNav,
15513
+ "aria-label": "‹",
15514
+ onClick: () => onShift(-1),
15515
+ children: "‹"
15516
+ }),
15517
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
15518
+ className: GitWorkbenchPanel_module_css_default.calTitle,
15519
+ children: title
15520
+ }),
15521
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
15522
+ type: "button",
15523
+ className: GitWorkbenchPanel_module_css_default.calNav,
15524
+ "aria-label": "›",
15525
+ onClick: () => onShift(1),
15526
+ children: "›"
15527
+ })
15528
+ ]
15529
+ }),
15530
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
15531
+ className: GitWorkbenchPanel_module_css_default.calWeek,
15532
+ children: weekdayLabels(locale).map((label, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: label }, index))
15533
+ }),
15534
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
15535
+ className: GitWorkbenchPanel_module_css_default.calGrid,
15536
+ children: grid.flat().map((cell) => cell === null ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
15537
+ type: "button",
15538
+ "aria-label": cell.iso,
15539
+ title: cell.iso,
15540
+ className: [
15541
+ cell.inMonth ? "" : GitWorkbenchPanel_module_css_default.calOut,
15542
+ cell.isToday ? GitWorkbenchPanel_module_css_default.calToday : "",
15543
+ inCalRange(cell.iso, after, before) ? GitWorkbenchPanel_module_css_default.calIn : "",
15544
+ cell.iso === after || cell.iso === before ? GitWorkbenchPanel_module_css_default.calMark : ""
15545
+ ].filter((cls) => cls.length > 0).join(" "),
15546
+ onClick: () => onPick(cell.iso),
15547
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: cell.day })
15548
+ }, cell.iso))
15549
+ })
15550
+ ]
15551
+ });
15552
+ }
15553
+ /**
15554
+ * The two node glyphs, in IntelliJ's New UI icon idiom: a 16px grid, 1px
15555
+ * strokes, no fill, rounded joins — outlines, where the old UI shipped filled
15556
+ * silhouettes. Hand-drawn here rather than imported, because the bundle purity
15557
+ * gate forbids an icon package and the drawer needs exactly these two; they
15558
+ * are shapes in that language, not JetBrains' own assets.
15559
+ *
15560
+ * `strokeWidth` is 1 against a viewBox that renders 1:1 at 16px, so every
15561
+ * stroke lands on a whole pixel instead of straddling two.
15562
+ *
15563
+ * Every place the drawer names a file or a directory uses these: the path
15564
+ * picker in the history filter, and the file tree behind all three tabs. The
15565
+ * CLASS names keep their `path` prefix — `scripts/verify_history_feature.py`
15566
+ * selects the picker's file rows by `label:has([class*="pathFileGlyph"])`.
15567
+ */
15568
+ function PathDirGlyph() {
15569
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
15570
+ className: GitWorkbenchPanel_module_css_default.pathDirGlyph,
15571
+ width: "16",
15572
+ height: "16",
15573
+ viewBox: "0 0 16 16",
15574
+ fill: "none",
15575
+ stroke: "currentColor",
15576
+ strokeWidth: "1",
15577
+ strokeLinejoin: "round",
15578
+ strokeLinecap: "round",
15579
+ "aria-hidden": "true",
15580
+ 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" })
15581
+ });
15582
+ }
15583
+ function PathFileGlyph() {
15584
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
15585
+ className: GitWorkbenchPanel_module_css_default.pathFileGlyph,
15586
+ width: "16",
15587
+ height: "16",
15588
+ viewBox: "0 0 16 16",
15589
+ fill: "none",
15590
+ stroke: "currentColor",
15591
+ strokeWidth: "1",
15592
+ strokeLinejoin: "round",
15593
+ strokeLinecap: "round",
15594
+ "aria-hidden": "true",
15595
+ 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" })]
15596
+ });
15597
+ }
15598
+ /** Files shown per expanded directory. The search box is the way to a file in
15599
+ * a crowded directory; the tree shows enough to browse without flooding the
15600
+ * list, and says so when it cut the tail. */
15601
+ const PATH_FILES_SHOWN = 100;
15602
+ /** Horizontal step per nesting level in the path picker. The whole indent now
15603
+ * comes from this one number: `.pathChildren` used to add a margin and a rail
15604
+ * of its own on top of it, so every level cost 29px and a 320px popover ran
15605
+ * out of width three directories deep. */
15606
+ const PATH_INDENT = 14;
15607
+ /** One level of the path picker's directory tree — directories (chevron,
15608
+ * subtree count) then their files (doc glyph, leaf rows). Collapsed subtrees
15609
+ * are not in the DOM at all, so a monorepo costs only what the reader has
15610
+ * opened. */
15611
+ /** A checkbox that also carries the tree's third state — `indeterminate` is a
15612
+ * DOM property, not an attribute, so it is set through the ref. */
15613
+ function TriStateCheckbox({ state, onChange, ariaLabel }) {
15614
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
15615
+ type: "checkbox",
15616
+ "aria-label": ariaLabel,
15617
+ checked: state === "on",
15618
+ ref: (el) => {
15619
+ if (el !== null) el.indeterminate = state === "partial";
15620
+ },
15621
+ onChange
15622
+ });
15623
+ }
15624
+ function PathTreeRows({ dirs, depth, expanded, stateOf, onToggleOpen, onTogglePath }) {
15625
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: dirs.map((dir) => {
15626
+ const open = expanded.includes(dir.path);
15627
+ const expandable = dir.children.length > 0 || dir.files.length > 0;
15628
+ const shown = dir.files.slice(0, PATH_FILES_SHOWN);
15629
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
15630
+ className: GitWorkbenchPanel_module_css_default.pathNode,
15631
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
15632
+ className: GitWorkbenchPanel_module_css_default.funnelRow,
15633
+ style: { paddingLeft: depth * PATH_INDENT + 4 },
15634
+ children: [
15635
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
15636
+ type: "button",
15637
+ className: GitWorkbenchPanel_module_css_default.funnelChevron,
15638
+ disabled: !expandable,
15639
+ "aria-expanded": open,
15640
+ onClick: () => onToggleOpen(dir.path),
15641
+ children: expandable ? open ? "▾" : "▸" : ""
15642
+ }),
15643
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TriStateCheckbox, {
15644
+ state: stateOf(dir.path),
15645
+ ariaLabel: dir.path,
15646
+ onChange: () => onTogglePath(dir.path)
15647
+ }),
15648
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PathDirGlyph, {}),
15649
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
15650
+ className: GitWorkbenchPanel_module_css_default.funnelName,
15651
+ title: dir.path,
15652
+ children: dir.name
15653
+ }),
15654
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
15655
+ className: GitWorkbenchPanel_module_css_default.funnelCount,
15656
+ children: dir.fileCount
15657
+ })
15658
+ ]
15659
+ }), open ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
15660
+ className: GitWorkbenchPanel_module_css_default.pathChildren,
15661
+ children: [
15662
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PathTreeRows, {
15663
+ dirs: dir.children,
15664
+ depth: depth + 1,
15665
+ expanded,
15666
+ stateOf,
15667
+ onToggleOpen,
15668
+ onTogglePath
15669
+ }),
15670
+ shown.map((file) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
15671
+ className: GitWorkbenchPanel_module_css_default.funnelRow,
15672
+ style: { paddingLeft: (depth + 1) * PATH_INDENT + 4 },
15673
+ children: [
15674
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
15675
+ className: GitWorkbenchPanel_module_css_default.funnelChevron,
15676
+ "aria-hidden": "true"
15677
+ }),
15678
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TriStateCheckbox, {
15679
+ state: stateOf(`${dir.path}/${file}`),
15680
+ ariaLabel: `${dir.path}/${file}`,
15681
+ onChange: () => onTogglePath(`${dir.path}/${file}`)
15682
+ }),
15683
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PathFileGlyph, {}),
15684
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
15685
+ className: GitWorkbenchPanel_module_css_default.funnelName,
15686
+ title: `${dir.path}/${file}`,
15687
+ children: file
15688
+ })
15689
+ ]
15690
+ }, file)),
15691
+ dir.files.length > PATH_FILES_SHOWN ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
15692
+ className: GitWorkbenchPanel_module_css_default.funnelMore,
15693
+ children: ["+", dir.files.length - PATH_FILES_SHOWN]
15694
+ }) : null
15695
+ ]
15696
+ }) : null]
15697
+ }, dir.path);
15698
+ }) });
14514
15699
  }
14515
15700
  /**
14516
15701
  * The commit log as its own full-height pane.
@@ -14530,9 +15715,131 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
14530
15715
  * what GitHub and GitLens do. The observer is rebuilt whenever the list grows,
14531
15716
  * so a page too short to fill the pane immediately triggers the next one.
14532
15717
  */
14533
- function CommitList({ paneRef, style, t, loading, commits, active, onSelect, hasMore, loadingMore, onLoadMore }) {
15718
+ function CommitList({ paneRef, style, t, loading, commits, active, onSelect, hasMore, loadingMore, onLoadMore, query, onQueryChange, error, statsPath, refName, fetchAuthors, fetchRepoTree }) {
14534
15719
  const scrollRef = (0, react.useRef)(null);
14535
15720
  const sentinelRef = (0, react.useRef)(null);
15721
+ const filterModel = (0, react.useMemo)(() => parseLogQuery(query), [query]);
15722
+ const chips = chipsFromFilter(filterModel);
15723
+ const dateCount = (filterModel.after.length > 0 ? 1 : 0) + (filterModel.before.length > 0 ? 1 : 0);
15724
+ const selectedCount = filterModel.users.length + filterModel.paths.length + dateCount;
15725
+ const [funnelOpen, setFunnelOpen] = (0, react.useState)(false);
15726
+ const [authors, setAuthors] = (0, react.useState)(null);
15727
+ const [authorsQuery, setAuthorsQuery] = (0, react.useState)("");
15728
+ const [pathTree, setPathTree] = (0, react.useState)(null);
15729
+ const [expandedDirs, setExpandedDirs] = (0, react.useState)([]);
15730
+ const [pathsQuery, setPathsQuery] = (0, react.useState)("");
15731
+ const [funnelSection, setFunnelSection] = (0, react.useState)("users");
15732
+ const [calMonth, setCalMonth] = (0, react.useState)(() => {
15733
+ const now = /* @__PURE__ */ new Date();
15734
+ return {
15735
+ year: now.getFullYear(),
15736
+ month: now.getMonth()
15737
+ };
15738
+ });
15739
+ const [calBound, setCalBound] = (0, react.useState)("after");
15740
+ const funnelAnchorRef = (0, react.useRef)(null);
15741
+ const funnelPanelRef = (0, react.useRef)(null);
15742
+ const [funnelBox, setFunnelBox] = (0, react.useState)(null);
15743
+ (0, react.useEffect)(() => {
15744
+ if (!funnelOpen) {
15745
+ setFunnelBox(null);
15746
+ return;
15747
+ }
15748
+ const onDown = (event) => {
15749
+ const target = event.target;
15750
+ if (funnelAnchorRef.current?.contains(target) === true) return;
15751
+ if (funnelPanelRef.current?.contains(target) === true) return;
15752
+ setFunnelOpen(false);
15753
+ };
15754
+ const onKey = (event) => {
15755
+ if (event.key === "Escape") setFunnelOpen(false);
15756
+ };
15757
+ const id = window.setTimeout(() => document.addEventListener("mousedown", onDown), 0);
15758
+ document.addEventListener("keydown", onKey);
15759
+ return () => {
15760
+ window.clearTimeout(id);
15761
+ document.removeEventListener("mousedown", onDown);
15762
+ document.removeEventListener("keydown", onKey);
15763
+ };
15764
+ }, [funnelOpen]);
15765
+ (0, react.useEffect)(() => {
15766
+ if (!funnelOpen) return;
15767
+ const rect = funnelAnchorRef.current?.getBoundingClientRect();
15768
+ if (rect === void 0) return;
15769
+ const width = 300;
15770
+ const left = Math.max(12, Math.min(rect.left + rect.width - width, window.innerWidth - width - 12));
15771
+ const top = rect.bottom + 4;
15772
+ setFunnelBox({
15773
+ top,
15774
+ left,
15775
+ maxHeight: Math.max(160, window.innerHeight - top - 16)
15776
+ });
15777
+ }, [funnelOpen]);
15778
+ (0, react.useEffect)(() => {
15779
+ if (!funnelOpen) return;
15780
+ const ctrl = new AbortController();
15781
+ setAuthors(null);
15782
+ setPathTree(null);
15783
+ fetchAuthors(statsPath, refName, ctrl.signal).then((roster) => {
15784
+ if (!ctrl.signal.aborted) setAuthors(roster);
15785
+ }).catch(() => {});
15786
+ fetchRepoTree(statsPath, ctrl.signal).then((tree) => {
15787
+ if (!ctrl.signal.aborted && tree !== null) setPathTree({
15788
+ dirs: buildDirTree(tree.paths),
15789
+ paths: tree.paths,
15790
+ truncated: tree.truncated
15791
+ });
15792
+ }).catch(() => {});
15793
+ return () => {
15794
+ ctrl.abort();
15795
+ };
15796
+ }, [
15797
+ funnelOpen,
15798
+ statsPath,
15799
+ refName,
15800
+ fetchAuthors,
15801
+ fetchRepoTree
15802
+ ]);
15803
+ /** Every funnel interaction writes the filter through the box's grammar, so
15804
+ * the box, the chips and the fetch can never disagree about the query. */
15805
+ const applyFilter = (next) => {
15806
+ onQueryChange(serializeLogQuery(next));
15807
+ };
15808
+ const toggleUser = (name) => {
15809
+ const has = filterModel.users.includes(name);
15810
+ applyFilter({
15811
+ ...filterModel,
15812
+ users: has ? filterModel.users.filter((user) => user !== name) : [...filterModel.users, name]
15813
+ });
15814
+ };
15815
+ const pathIndex = (0, react.useMemo)(() => pathTree === null ? null : buildIndex(pathTree.paths), [pathTree]);
15816
+ const pathState = (path) => pathIndex === null ? "off" : checkedState(filterModel.paths, path, pathIndex);
15817
+ const togglePath = (path) => {
15818
+ if (pathIndex === null) return;
15819
+ applyFilter({
15820
+ ...filterModel,
15821
+ paths: isCovered(filterModel.paths, path) ? removePath(filterModel.paths, path, pathIndex) : addPath(filterModel.paths, path)
15822
+ });
15823
+ };
15824
+ const toggleDirOpen = (path) => {
15825
+ setExpandedDirs((prev) => prev.includes(path) ? prev.filter((p) => p !== path) : [...prev, path]);
15826
+ };
15827
+ const needle = authorsQuery.trim().toLowerCase();
15828
+ const matchedAuthors = authors === null ? [] : needle.length === 0 ? authors.authors : authors.authors.filter((entry) => entry.name.toLowerCase().includes(needle) || entry.email.toLowerCase().includes(needle));
15829
+ const DATE_PRESETS = [
15830
+ {
15831
+ key: "filterToday",
15832
+ value: "midnight"
15833
+ },
15834
+ {
15835
+ key: "filterLast7",
15836
+ value: "1 week ago"
15837
+ },
15838
+ {
15839
+ key: "filterLast30",
15840
+ value: "30 days ago"
15841
+ }
15842
+ ];
14536
15843
  const graph = (0, react.useMemo)(() => layoutGraph(commits.map((commit) => ({
14537
15844
  hash: commit.hash,
14538
15845
  parents: commit.parents ?? []
@@ -14562,39 +15869,353 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
14562
15869
  className: GitWorkbenchPanel_module_css_default.commitsPane,
14563
15870
  style,
14564
15871
  "data-gs-part": "commits",
14565
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
14566
- className: GitWorkbenchPanel_module_css_default.paneHead,
14567
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
14568
- className: GitWorkbenchPanel_module_css_default.paneTitle,
14569
- children: t("historyLabel")
15872
+ children: [
15873
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
15874
+ className: GitWorkbenchPanel_module_css_default.paneHead,
15875
+ children: [
15876
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
15877
+ className: GitWorkbenchPanel_module_css_default.paneTitle,
15878
+ children: t("historyLabel")
15879
+ }),
15880
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
15881
+ className: GitWorkbenchPanel_module_css_default.funnel,
15882
+ ref: funnelAnchorRef,
15883
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
15884
+ type: "button",
15885
+ className: funnelOpen || chips.length > 0 ? `${GitWorkbenchPanel_module_css_default.funnelButton} ${GitWorkbenchPanel_module_css_default.funnelButtonActive}` : GitWorkbenchPanel_module_css_default.funnelButton,
15886
+ "aria-expanded": funnelOpen,
15887
+ onClick: () => setFunnelOpen((isOpen) => !isOpen),
15888
+ children: [t("filterBy"), " ▾"]
15889
+ })
15890
+ }),
15891
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
15892
+ className: GitWorkbenchPanel_module_css_default.commitFilter,
15893
+ type: "search",
15894
+ value: query,
15895
+ onChange: (event) => onQueryChange(event.target.value),
15896
+ placeholder: t("historyFilterPlaceholder"),
15897
+ "aria-label": t("historyFilterPlaceholder"),
15898
+ spellCheck: false
15899
+ })
15900
+ ]
15901
+ }),
15902
+ funnelOpen && funnelBox !== null ? (0, react_dom.createPortal)(/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
15903
+ ref: funnelPanelRef,
15904
+ className: GitWorkbenchPanel_module_css_default.funnelPop,
15905
+ style: funnelBox,
15906
+ role: "dialog",
15907
+ "aria-label": t("filterBy"),
15908
+ children: [
15909
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
15910
+ className: GitWorkbenchPanel_module_css_default.funnelTabs,
15911
+ role: "tablist",
15912
+ children: [
15913
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
15914
+ type: "button",
15915
+ role: "tab",
15916
+ "aria-selected": funnelSection === "users",
15917
+ className: funnelSection === "users" ? `${GitWorkbenchPanel_module_css_default.funnelTab} ${GitWorkbenchPanel_module_css_default.funnelTabActive}` : GitWorkbenchPanel_module_css_default.funnelTab,
15918
+ onClick: () => setFunnelSection("users"),
15919
+ children: [t("filterUsers"), filterModel.users.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
15920
+ className: GitWorkbenchPanel_module_css_default.funnelTabCount,
15921
+ children: filterModel.users.length
15922
+ }) : null]
15923
+ }),
15924
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
15925
+ type: "button",
15926
+ role: "tab",
15927
+ "aria-selected": funnelSection === "date",
15928
+ className: funnelSection === "date" ? `${GitWorkbenchPanel_module_css_default.funnelTab} ${GitWorkbenchPanel_module_css_default.funnelTabActive}` : GitWorkbenchPanel_module_css_default.funnelTab,
15929
+ onClick: () => setFunnelSection("date"),
15930
+ children: [t("filterDate"), dateCount > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
15931
+ className: GitWorkbenchPanel_module_css_default.funnelTabCount,
15932
+ children: dateCount
15933
+ }) : null]
15934
+ }),
15935
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
15936
+ type: "button",
15937
+ role: "tab",
15938
+ "aria-selected": funnelSection === "paths",
15939
+ className: funnelSection === "paths" ? `${GitWorkbenchPanel_module_css_default.funnelTab} ${GitWorkbenchPanel_module_css_default.funnelTabActive}` : GitWorkbenchPanel_module_css_default.funnelTab,
15940
+ onClick: () => setFunnelSection("paths"),
15941
+ children: [t("filterPaths"), filterModel.paths.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
15942
+ className: GitWorkbenchPanel_module_css_default.funnelTabCount,
15943
+ children: filterModel.paths.length
15944
+ }) : null]
15945
+ })
15946
+ ]
15947
+ }),
15948
+ funnelSection === "users" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
15949
+ className: GitWorkbenchPanel_module_css_default.funnelPane,
15950
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
15951
+ className: GitWorkbenchPanel_module_css_default.funnelSearch,
15952
+ type: "search",
15953
+ value: authorsQuery,
15954
+ onChange: (event) => setAuthorsQuery(event.target.value),
15955
+ placeholder: t("filterUserSearch"),
15956
+ "aria-label": t("filterUserSearch"),
15957
+ spellCheck: false
15958
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
15959
+ className: GitWorkbenchPanel_module_css_default.funnelList,
15960
+ children: [authors === null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
15961
+ className: GitWorkbenchPanel_module_css_default.funnelMore,
15962
+ children: t("loading")
15963
+ }) : matchedAuthors.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
15964
+ className: GitWorkbenchPanel_module_css_default.funnelMore,
15965
+ children: authors.authors.length === 0 ? t("noCommits") : t("historyNoMatch")
15966
+ }) : matchedAuthors.map((entry) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
15967
+ className: GitWorkbenchPanel_module_css_default.funnelRow,
15968
+ children: [
15969
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
15970
+ type: "checkbox",
15971
+ checked: filterModel.users.includes(entry.name),
15972
+ onChange: () => toggleUser(entry.name)
15973
+ }),
15974
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
15975
+ className: GitWorkbenchPanel_module_css_default.funnelName,
15976
+ title: `${entry.name} <${entry.email}>`,
15977
+ children: entry.name
15978
+ }),
15979
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
15980
+ className: GitWorkbenchPanel_module_css_default.funnelCount,
15981
+ children: entry.count
15982
+ })
15983
+ ]
15984
+ }, `${entry.name}\x1f${entry.email}`)), authors?.truncated === true ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
15985
+ className: GitWorkbenchPanel_module_css_default.funnelMore,
15986
+ children: t("filterAuthorsMore")
15987
+ }) : null]
15988
+ })]
15989
+ }) : null,
15990
+ funnelSection === "date" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
15991
+ className: GitWorkbenchPanel_module_css_default.funnelPane,
15992
+ children: [
15993
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
15994
+ className: GitWorkbenchPanel_module_css_default.funnelPresets,
15995
+ children: DATE_PRESETS.map((preset) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
15996
+ type: "button",
15997
+ className: filterModel.after === preset.value ? `${GitWorkbenchPanel_module_css_default.funnelPreset} ${GitWorkbenchPanel_module_css_default.funnelPresetActive}` : GitWorkbenchPanel_module_css_default.funnelPreset,
15998
+ onClick: () => applyFilter({
15999
+ ...filterModel,
16000
+ after: filterModel.after === preset.value ? "" : preset.value
16001
+ }),
16002
+ children: t(preset.key)
16003
+ }, preset.key))
16004
+ }),
16005
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
16006
+ className: GitWorkbenchPanel_module_css_default.funnelCaption,
16007
+ children: t("filterCalendarSets")
16008
+ }),
16009
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
16010
+ className: GitWorkbenchPanel_module_css_default.funnelBounds,
16011
+ role: "group",
16012
+ "aria-label": t("filterCalendarSets"),
16013
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
16014
+ type: "button",
16015
+ "aria-pressed": calBound === "after",
16016
+ className: calBound === "after" ? `${GitWorkbenchPanel_module_css_default.funnelBoundBtn} ${GitWorkbenchPanel_module_css_default.funnelBoundBtnActive}` : GitWorkbenchPanel_module_css_default.funnelBoundBtn,
16017
+ onClick: () => setCalBound("after"),
16018
+ children: t("filterAfter")
16019
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
16020
+ type: "button",
16021
+ "aria-pressed": calBound === "before",
16022
+ className: calBound === "before" ? `${GitWorkbenchPanel_module_css_default.funnelBoundBtn} ${GitWorkbenchPanel_module_css_default.funnelBoundBtnActive}` : GitWorkbenchPanel_module_css_default.funnelBoundBtn,
16023
+ onClick: () => setCalBound("before"),
16024
+ children: t("filterBefore")
16025
+ })]
16026
+ }),
16027
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FilterCalendar, {
16028
+ year: calMonth.year,
16029
+ month: calMonth.month,
16030
+ after: filterModel.after,
16031
+ before: filterModel.before,
16032
+ locale: t("filterLocale"),
16033
+ onPick: (iso) => applyFilter({
16034
+ ...filterModel,
16035
+ [calBound]: iso
16036
+ }),
16037
+ onShift: (delta) => setCalMonth((current) => {
16038
+ const next = new Date(current.year, current.month + delta, 1);
16039
+ return {
16040
+ year: next.getFullYear(),
16041
+ month: next.getMonth()
16042
+ };
16043
+ })
16044
+ }),
16045
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
16046
+ className: GitWorkbenchPanel_module_css_default.funnelBoundRows,
16047
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
16048
+ className: GitWorkbenchPanel_module_css_default.funnelBoundRow,
16049
+ children: [
16050
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
16051
+ className: GitWorkbenchPanel_module_css_default.funnelBoundKey,
16052
+ children: t("filterAfter")
16053
+ }),
16054
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
16055
+ className: filterModel.after.length > 0 ? `${GitWorkbenchPanel_module_css_default.funnelBoundVal} ${GitWorkbenchPanel_module_css_default.funnelBoundValSet}` : GitWorkbenchPanel_module_css_default.funnelBoundVal,
16056
+ children: filterModel.after.length > 0 ? filterModel.after : "—"
16057
+ }),
16058
+ filterModel.after.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
16059
+ type: "button",
16060
+ className: GitWorkbenchPanel_module_css_default.funnelBoundClear,
16061
+ "aria-label": t("filterAfter"),
16062
+ onClick: () => applyFilter({
16063
+ ...filterModel,
16064
+ after: ""
16065
+ }),
16066
+ children: "×"
16067
+ }) : null
16068
+ ]
16069
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
16070
+ className: GitWorkbenchPanel_module_css_default.funnelBoundRow,
16071
+ children: [
16072
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
16073
+ className: GitWorkbenchPanel_module_css_default.funnelBoundKey,
16074
+ children: t("filterBefore")
16075
+ }),
16076
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
16077
+ className: filterModel.before.length > 0 ? `${GitWorkbenchPanel_module_css_default.funnelBoundVal} ${GitWorkbenchPanel_module_css_default.funnelBoundValSet}` : GitWorkbenchPanel_module_css_default.funnelBoundVal,
16078
+ children: filterModel.before.length > 0 ? filterModel.before : "—"
16079
+ }),
16080
+ filterModel.before.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
16081
+ type: "button",
16082
+ className: GitWorkbenchPanel_module_css_default.funnelBoundClear,
16083
+ "aria-label": t("filterBefore"),
16084
+ onClick: () => applyFilter({
16085
+ ...filterModel,
16086
+ before: ""
16087
+ }),
16088
+ children: "×"
16089
+ }) : null
16090
+ ]
16091
+ })]
16092
+ })
16093
+ ]
16094
+ }) : null,
16095
+ funnelSection === "paths" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
16096
+ className: GitWorkbenchPanel_module_css_default.funnelPane,
16097
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
16098
+ className: GitWorkbenchPanel_module_css_default.funnelSearch,
16099
+ type: "search",
16100
+ value: pathsQuery,
16101
+ onChange: (event) => setPathsQuery(event.target.value),
16102
+ placeholder: t("filterPathSearch"),
16103
+ "aria-label": t("filterPathSearch"),
16104
+ spellCheck: false
16105
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
16106
+ className: GitWorkbenchPanel_module_css_default.funnelList,
16107
+ children: [pathTree === null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
16108
+ className: GitWorkbenchPanel_module_css_default.funnelMore,
16109
+ children: t("loading")
16110
+ }) : pathsQuery.trim().length > 0 ? (() => {
16111
+ const hits = searchPaths(pathTree.paths, pathsQuery).slice(0, 200);
16112
+ if (hits.length === 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
16113
+ className: GitWorkbenchPanel_module_css_default.funnelMore,
16114
+ children: t("historyNoMatch")
16115
+ });
16116
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [hits.map((hit) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
16117
+ className: GitWorkbenchPanel_module_css_default.funnelRow,
16118
+ children: [
16119
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TriStateCheckbox, {
16120
+ state: pathState(hit.path),
16121
+ ariaLabel: hit.path,
16122
+ onChange: () => togglePath(hit.path)
16123
+ }),
16124
+ hit.isFile ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PathFileGlyph, {}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PathDirGlyph, {}),
16125
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
16126
+ className: GitWorkbenchPanel_module_css_default.funnelName,
16127
+ title: hit.path,
16128
+ children: hit.path
16129
+ })
16130
+ ]
16131
+ }, hit.path)), searchPaths(pathTree.paths, pathsQuery).length > 200 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
16132
+ className: GitWorkbenchPanel_module_css_default.funnelMore,
16133
+ children: t("filterPathsMore")
16134
+ }) : null] });
16135
+ })() : pathTree.dirs.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
16136
+ className: GitWorkbenchPanel_module_css_default.funnelMore,
16137
+ children: t("noCommits")
16138
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PathTreeRows, {
16139
+ dirs: pathTree.dirs,
16140
+ depth: 0,
16141
+ expanded: expandedDirs,
16142
+ stateOf: pathState,
16143
+ onToggleOpen: toggleDirOpen,
16144
+ onTogglePath: togglePath
16145
+ }), pathTree?.truncated === true ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
16146
+ className: GitWorkbenchPanel_module_css_default.funnelMore,
16147
+ children: t("filterPathsMore")
16148
+ }) : null]
16149
+ })]
16150
+ }) : null,
16151
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
16152
+ className: GitWorkbenchPanel_module_css_default.funnelFoot,
16153
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
16154
+ className: selectedCount > 0 ? `${GitWorkbenchPanel_module_css_default.funnelFootCount} ${GitWorkbenchPanel_module_css_default.funnelFootCountOn}` : GitWorkbenchPanel_module_css_default.funnelFootCount,
16155
+ children: t("filterSelected", { count: selectedCount })
16156
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
16157
+ type: "button",
16158
+ className: GitWorkbenchPanel_module_css_default.funnelFootClear,
16159
+ disabled: selectedCount === 0,
16160
+ onClick: () => onQueryChange(""),
16161
+ children: t("filterClearAll")
16162
+ })]
16163
+ })
16164
+ ]
16165
+ }), funnelAnchorRef.current?.closest("[data-gs-part=\"overlay\"]") ?? (typeof document === "undefined" ? null : document.body)) : null,
16166
+ chips.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
16167
+ className: GitWorkbenchPanel_module_css_default.filterChips,
16168
+ children: [chips.map((chip) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
16169
+ className: GitWorkbenchPanel_module_css_default.filterChip,
16170
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
16171
+ className: GitWorkbenchPanel_module_css_default.filterChipLabel,
16172
+ children: [
16173
+ chip.kind,
16174
+ ":",
16175
+ chip.value
16176
+ ]
16177
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
16178
+ type: "button",
16179
+ className: GitWorkbenchPanel_module_css_default.filterChipRemove,
16180
+ "aria-label": `${chip.kind} ${chip.value}`,
16181
+ onClick: () => onQueryChange(serializeLogQuery(removeChip(filterModel, chip.kind, chip.value))),
16182
+ children: "×"
16183
+ })]
16184
+ }, `${chip.kind}\x1f${chip.value}`)), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
16185
+ type: "button",
16186
+ className: GitWorkbenchPanel_module_css_default.filterClear,
16187
+ onClick: () => onQueryChange(""),
16188
+ children: t("filterClearAll")
16189
+ })]
16190
+ }) : null,
16191
+ commits.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
16192
+ className: GitWorkbenchPanel_module_css_default.empty,
16193
+ children: loading ? t("loading") : error !== null ? error : chips.length > 0 ? t("historyNoMatch") : t("noCommits")
16194
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
16195
+ className: GitWorkbenchPanel_module_css_default.commits,
16196
+ role: "listbox",
16197
+ "aria-label": t("historyLabel"),
16198
+ ref: scrollRef,
16199
+ children: [
16200
+ commits.map((commit, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CommitRow, {
16201
+ t,
16202
+ commit,
16203
+ active: commit.hash === active,
16204
+ onSelect,
16205
+ graphRow: graph.rows[index],
16206
+ graphWidth: graph.width
16207
+ }, commit.hash)),
16208
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
16209
+ ref: sentinelRef,
16210
+ className: GitWorkbenchPanel_module_css_default.commitsSentinel
16211
+ }),
16212
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
16213
+ className: GitWorkbenchPanel_module_css_default.commitsFoot,
16214
+ children: loadingMore ? t("loading") : hasMore ? "" : t("historyEnd")
16215
+ })
16216
+ ]
14570
16217
  })
14571
- }), commits.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
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
- })]
16218
+ ]
14598
16219
  });
14599
16220
  }
14600
16221
  /** Horizontal step per nesting level. */
@@ -14697,8 +16318,23 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
14697
16318
  dirs
14698
16319
  };
14699
16320
  }
14700
- function FileTree({ t, loading, lead, files, active, onSelect, collapsed, onCollapsedChange, onCheck, footer }) {
14701
- const tree = (0, react.useMemo)(() => buildTree(files), [files]);
16321
+ function FileTree({ t, loading, lead, files, active, onSelect, collapsed, onCollapsedChange, onCheck, onDiscard, footer, scopeKey }) {
16322
+ /**
16323
+ * The filter over this list. Local, because it describes a way of LOOKING at
16324
+ * the pane rather than anything the drawer stores: closing and reopening on
16325
+ * an unfiltered list is what someone expects, and a query kept in the panel
16326
+ * would have to be cleared from four places instead of one.
16327
+ */
16328
+ const [query, setQuery] = (0, react.useState)("");
16329
+ const [filterOpen, setFilterOpen] = (0, react.useState)(false);
16330
+ const filterRef = (0, react.useRef)(null);
16331
+ (0, react.useEffect)(() => {
16332
+ setQuery("");
16333
+ setFilterOpen(false);
16334
+ }, [scopeKey]);
16335
+ const shownFiles = (0, react.useMemo)(() => filterFiles(files, query), [files, query]);
16336
+ const filtering = shownFiles !== files;
16337
+ const tree = (0, react.useMemo)(() => buildTree(shownFiles), [shownFiles]);
14702
16338
  /** Default: a dir collapses when it holds more than 12 files anywhere below it. */
14703
16339
  const effective = collapsed ?? defaultCollapsed(tree);
14704
16340
  (0, react.useEffect)(() => {
@@ -14736,54 +16372,117 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
14736
16372
  state: tree.check,
14737
16373
  label: tree.check === "on" ? t("unstageAll") : t("stageAll"),
14738
16374
  indent: 0,
14739
- onToggle: () => onCheck(files, tree.check)
16375
+ onToggle: () => onCheck(shownFiles, tree.check)
14740
16376
  }) : null, /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
14741
16377
  className: GitWorkbenchPanel_module_css_default.treeLabel,
14742
- children: loading === true ? t("loading") : `${lead !== void 0 ? `${lead} · ` : ""}${t("files", { count: files.length })}`
16378
+ children: loading === true ? t("loading") : `${lead !== void 0 ? `${lead} · ` : ""}${filtering ? t("filesFiltered", {
16379
+ shown: shownFiles.length,
16380
+ count: files.length
16381
+ }) : t("files", { count: files.length })}`
14743
16382
  })]
14744
16383
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
14745
16384
  className: GitWorkbenchPanel_module_css_default.treeActions,
14746
16385
  "data-gs-part": "tree-actions",
14747
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
14748
- type: "button",
14749
- className: GitWorkbenchPanel_module_css_default.treeIcon,
14750
- "data-gs-part": "expand-all",
14751
- title: t("expandAll"),
14752
- "aria-label": t("expandAll"),
14753
- onClick: () => setAll(true),
14754
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
14755
- className: `${GitWorkbenchPanel_module_css_default.treeIconGlyph} ${GitWorkbenchPanel_module_css_default.treeIconDown}`,
14756
- children: "▸"
14757
- })
14758
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
14759
- type: "button",
14760
- className: GitWorkbenchPanel_module_css_default.treeIcon,
14761
- "data-gs-part": "collapse-all",
14762
- title: t("collapseAll"),
14763
- "aria-label": t("collapseAll"),
14764
- onClick: () => setAll(false),
14765
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
14766
- className: GitWorkbenchPanel_module_css_default.treeIconGlyph,
14767
- children: ""
16386
+ children: [
16387
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
16388
+ type: "button",
16389
+ className: filterOpen || filtering ? `${GitWorkbenchPanel_module_css_default.treeIcon} ${GitWorkbenchPanel_module_css_default.treeIconOn}` : GitWorkbenchPanel_module_css_default.treeIcon,
16390
+ "data-gs-part": "filter-files",
16391
+ title: t("filterFiles"),
16392
+ "aria-label": t("filterFiles"),
16393
+ "aria-pressed": filterOpen,
16394
+ onClick: () => {
16395
+ if (filterOpen) {
16396
+ setQuery("");
16397
+ setFilterOpen(false);
16398
+ return;
16399
+ }
16400
+ setFilterOpen(true);
16401
+ window.setTimeout(() => filterRef.current?.focus(), 0);
16402
+ },
16403
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FilterGlyph, {})
16404
+ }),
16405
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
16406
+ type: "button",
16407
+ className: GitWorkbenchPanel_module_css_default.treeIcon,
16408
+ "data-gs-part": "expand-all",
16409
+ title: t("expandAll"),
16410
+ "aria-label": t("expandAll"),
16411
+ onClick: () => setAll(true),
16412
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
16413
+ className: `${GitWorkbenchPanel_module_css_default.treeIconGlyph} ${GitWorkbenchPanel_module_css_default.treeIconDown}`,
16414
+ children: "▸"
16415
+ })
16416
+ }),
16417
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
16418
+ type: "button",
16419
+ className: GitWorkbenchPanel_module_css_default.treeIcon,
16420
+ "data-gs-part": "collapse-all",
16421
+ title: t("collapseAll"),
16422
+ "aria-label": t("collapseAll"),
16423
+ onClick: () => setAll(false),
16424
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
16425
+ className: GitWorkbenchPanel_module_css_default.treeIconGlyph,
16426
+ children: "▸"
16427
+ })
14768
16428
  })
14769
- })]
16429
+ ]
14770
16430
  })]
14771
16431
  }),
16432
+ filterOpen ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
16433
+ className: GitWorkbenchPanel_module_css_default.treeFilter,
16434
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
16435
+ ref: filterRef,
16436
+ className: GitWorkbenchPanel_module_css_default.treeFilterInput,
16437
+ type: "text",
16438
+ value: query,
16439
+ placeholder: t("filterFilesPlaceholder"),
16440
+ "aria-label": t("filterFiles"),
16441
+ spellCheck: false,
16442
+ onChange: (event) => setQuery(event.target.value),
16443
+ onKeyDown: (event) => {
16444
+ if (event.key !== "Escape") return;
16445
+ if (query.length > 0) {
16446
+ event.stopPropagation();
16447
+ setQuery("");
16448
+ return;
16449
+ }
16450
+ event.stopPropagation();
16451
+ setFilterOpen(false);
16452
+ }
16453
+ }), query.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
16454
+ type: "button",
16455
+ className: GitWorkbenchPanel_module_css_default.treeFilterClear,
16456
+ title: t("filterFilesClear"),
16457
+ "aria-label": t("filterFilesClear"),
16458
+ onClick: () => {
16459
+ setQuery("");
16460
+ filterRef.current?.focus();
16461
+ },
16462
+ children: "×"
16463
+ }) : null]
16464
+ }) : null,
14772
16465
  loading === true ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
14773
16466
  className: GitWorkbenchPanel_module_css_default.treeEmpty,
14774
16467
  "data-gs-part": "tree-loading",
14775
16468
  children: t("loading")
16469
+ }) : filtering && shownFiles.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
16470
+ className: GitWorkbenchPanel_module_css_default.treeEmpty,
16471
+ "data-gs-part": "tree-no-match",
16472
+ children: t("filterNoMatch")
14776
16473
  }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
14777
16474
  className: GitWorkbenchPanel_module_css_default.tree,
14778
16475
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TreeChildren, {
14779
16476
  node: tree,
14780
16477
  depth: 0,
14781
16478
  active,
14782
- collapsed: effective,
16479
+ collapsed: filtering ? EMPTY_COLLAPSED : effective,
14783
16480
  onToggle: toggleOne,
14784
16481
  onSelect,
14785
16482
  onCheck,
14786
- stageLabels
16483
+ onDiscard,
16484
+ stageLabels,
16485
+ discardLabel: t("discardAction")
14787
16486
  })
14788
16487
  }),
14789
16488
  footer
@@ -14847,7 +16546,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
14847
16546
  for (const child of node.dirs.values()) out.push(...filesUnder(child));
14848
16547
  return out;
14849
16548
  }
14850
- function TreeChildren({ node, depth, active, collapsed, onToggle, onSelect, onCheck, stageLabels }) {
16549
+ function TreeChildren({ node, depth, active, collapsed, onToggle, onSelect, onCheck, onDiscard, stageLabels, discardLabel }) {
14851
16550
  const dirNodes = [...node.dirs.values()].sort((a, b) => a.name.localeCompare(b.name));
14852
16551
  const fileNodes = [...node.files].sort((a, b) => basePart(a.path).localeCompare(basePart(b.path)));
14853
16552
  const checkColumn = onCheck !== void 0 ? TREE_CHECK_W : 0;
@@ -14875,6 +16574,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
14875
16574
  className: `${GitWorkbenchPanel_module_css_default.chevron} ${open ? GitWorkbenchPanel_module_css_default.chevronOpen : ""}`,
14876
16575
  children: "▸"
14877
16576
  }),
16577
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PathDirGlyph, {}),
14878
16578
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
14879
16579
  className: GitWorkbenchPanel_module_css_default.treeDirName,
14880
16580
  children: dir.name
@@ -14906,7 +16606,9 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
14906
16606
  onToggle,
14907
16607
  onSelect,
14908
16608
  onCheck,
14909
- stageLabels
16609
+ onDiscard,
16610
+ stageLabels,
16611
+ discardLabel
14910
16612
  })
14911
16613
  }) : null]
14912
16614
  }, dir.path);
@@ -14914,45 +16616,60 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
14914
16616
  const check = fileCheckState(file);
14915
16617
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
14916
16618
  className: GitWorkbenchPanel_module_css_default.fileLi,
14917
- children: [onCheck !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CheckBox, {
14918
- state: check,
14919
- label: check === "on" ? stageLabels.unstage : stageLabels.stage,
14920
- indent,
14921
- onToggle: () => onCheck([file], check)
14922
- }) : null, /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
14923
- type: "button",
14924
- className: active === file.path ? `${GitWorkbenchPanel_module_css_default.file} ${GitWorkbenchPanel_module_css_default.fileActive}` : GitWorkbenchPanel_module_css_default.file,
14925
- style: { paddingLeft: (onCheck !== void 0 ? 0 : indent) + TREE_LEAF_OFFSET },
14926
- onClick: () => onSelect(file.path),
14927
- title: file.previousPath !== void 0 ? `${file.previousPath} ${file.path}` : file.path,
14928
- children: [
14929
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
14930
- className: `${GitWorkbenchPanel_module_css_default.fileStatus} ${STATUS_BADGE[file.status]}`,
14931
- children: statusGlyph(file.status)
14932
- }),
14933
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
14934
- className: GitWorkbenchPanel_module_css_default.filePath,
14935
- children: basePart(file.path)
14936
- }),
14937
- file.binary ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
14938
- className: GitWorkbenchPanel_module_css_default.fileBinary,
14939
- children: "BIN"
14940
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
14941
- className: GitWorkbenchPanel_module_css_default.fileCounts,
14942
- children: [
14943
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
14944
- className: GitWorkbenchPanel_module_css_default.fileCountAdd,
14945
- children: file.addedLines > 0 ? `+${file.addedLines}` : ""
14946
- }),
14947
- " ",
14948
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
14949
- className: GitWorkbenchPanel_module_css_default.fileCountDel,
14950
- children: file.deletedLines > 0 ? `−${file.deletedLines}` : ""
14951
- })
14952
- ]
14953
- })
14954
- ]
14955
- })]
16619
+ children: [
16620
+ onCheck !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CheckBox, {
16621
+ state: check,
16622
+ label: check === "on" ? stageLabels.unstage : stageLabels.stage,
16623
+ indent,
16624
+ onToggle: () => onCheck([file], check)
16625
+ }) : null,
16626
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
16627
+ type: "button",
16628
+ className: active === file.path ? `${GitWorkbenchPanel_module_css_default.file} ${GitWorkbenchPanel_module_css_default.fileActive}` : GitWorkbenchPanel_module_css_default.file,
16629
+ style: { paddingLeft: (onCheck !== void 0 ? 0 : indent) + TREE_LEAF_OFFSET },
16630
+ onClick: () => onSelect(file.path),
16631
+ title: file.previousPath !== void 0 ? `${file.previousPath} → ${file.path}` : file.path,
16632
+ children: [
16633
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PathFileGlyph, {}),
16634
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
16635
+ className: GitWorkbenchPanel_module_css_default.filePath,
16636
+ children: basePart(file.path)
16637
+ }),
16638
+ file.binary ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
16639
+ className: GitWorkbenchPanel_module_css_default.fileBinary,
16640
+ children: "BIN"
16641
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
16642
+ className: GitWorkbenchPanel_module_css_default.fileCounts,
16643
+ children: [
16644
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
16645
+ className: GitWorkbenchPanel_module_css_default.fileCountAdd,
16646
+ children: file.addedLines > 0 ? `+${file.addedLines}` : ""
16647
+ }),
16648
+ " ",
16649
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
16650
+ className: GitWorkbenchPanel_module_css_default.fileCountDel,
16651
+ children: file.deletedLines > 0 ? `−${file.deletedLines}` : ""
16652
+ })
16653
+ ]
16654
+ }),
16655
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
16656
+ className: `${GitWorkbenchPanel_module_css_default.fileStatus} ${STATUS_BADGE[file.status]}`,
16657
+ children: statusGlyph(file.status)
16658
+ })
16659
+ ]
16660
+ }),
16661
+ onDiscard !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
16662
+ type: "button",
16663
+ className: GitWorkbenchPanel_module_css_default.fileDiscard,
16664
+ title: discardLabel,
16665
+ "aria-label": `${discardLabel ?? ""} ${file.path}`,
16666
+ onClick: (event) => {
16667
+ event.stopPropagation();
16668
+ onDiscard(file);
16669
+ },
16670
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RollbackGlyph, {})
16671
+ }) : null
16672
+ ]
14956
16673
  }, file.path);
14957
16674
  })] });
14958
16675
  }
@@ -15134,8 +16851,36 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
15134
16851
  noTextDiff: "无文本差异",
15135
16852
  noCommits: "无提交历史",
15136
16853
  historyLabel: "提交历史",
16854
+ commitAuthor: "作者",
16855
+ commitCommitter: "提交者",
16856
+ commitDate: "提交时间",
16857
+ historyFilterPlaceholder: "筛选:user: 名字 / path: 路径 / after: 日期 / 关键词",
16858
+ historyNoMatch: "没有匹配的提交",
16859
+ filterClearAll: "清除全部",
16860
+ filterBy: "筛选条件",
16861
+ filterUsers: "用户",
16862
+ filterUserSearch: "搜索作者",
16863
+ filterAuthorsMore: "仅显示提交最多的 500 位作者",
16864
+ filterDate: "日期",
16865
+ filterToday: "今天",
16866
+ filterLast7: "最近 7 天",
16867
+ filterLast30: "最近 30 天",
16868
+ filterAfter: "之后",
16869
+ filterBefore: "之前",
16870
+ filterPaths: "路径",
16871
+ filterPathsMore: "文件过多,目录树已截断",
16872
+ filterPathSearch: "搜索文件或目录",
16873
+ filterCalendarSets: "日历写入",
16874
+ filterSelected: "已选 {count} 项",
16875
+ filterLocale: "zh-CN",
16876
+ allBranches: "全部分支",
15137
16877
  expandAll: "展开全部",
15138
16878
  collapseAll: "收起全部",
16879
+ filterFiles: "过滤文件",
16880
+ filterFilesPlaceholder: "过滤文件,空格分隔多个关键字",
16881
+ filterFilesClear: "清除过滤",
16882
+ filesFiltered: "{shown} / {count} 文件",
16883
+ filterNoMatch: "没有匹配的文件",
15139
16884
  noBranch: "(无分支)",
15140
16885
  copyCommit: "复制提交说明",
15141
16886
  copiedCommit: "已复制",
@@ -15169,6 +16914,14 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
15169
16914
  "op.ok.fetch": "已获取远端信息",
15170
16915
  "op.ok.pull": "拉取完成",
15171
16916
  "op.ok.push": "推送成功",
16917
+ "op.ok.discardFile": "已撤回",
16918
+ discardAction: "撤回改动",
16919
+ discardTitle: "撤回改动?",
16920
+ discardConfirm: "撤回",
16921
+ discardCancel: "取消",
16922
+ discardBodyRestore: "{path} 将还原成上次提交时的样子。这里的 {added} 行新增、{deleted} 行删除无法找回。",
16923
+ discardBodyDelete: "{path} 从未被 git 记录过,删除后无法找回。",
16924
+ discardBodyUnrename: "撤销重命名:{path} 改回 {previousPath},改名期间的内容改动一并丢弃。",
15172
16925
  "op.fail.auth": "认证失败。凭据提示已被禁用,请先在终端里配置好凭据再重试。",
15173
16926
  "op.fail.network": "网络不可达:主机名解析失败或连接不上。检查网络与远程地址后重试。",
15174
16927
  "op.fail.no-upstream": "当前分支没有上游分支。",
@@ -15245,8 +16998,36 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
15245
16998
  noTextDiff: "No text changes",
15246
16999
  noCommits: "No commit history",
15247
17000
  historyLabel: "Commit history",
17001
+ commitAuthor: "Author",
17002
+ commitCommitter: "Committer",
17003
+ commitDate: "Committed",
17004
+ historyFilterPlaceholder: "Filter: user: name / path: dir / after: date / text",
17005
+ historyNoMatch: "No matching commits",
17006
+ filterClearAll: "Clear all",
17007
+ filterBy: "Filter by",
17008
+ filterUsers: "Users",
17009
+ filterUserSearch: "Search authors",
17010
+ filterAuthorsMore: "Showing the 500 busiest authors only",
17011
+ filterDate: "Date",
17012
+ filterToday: "Today",
17013
+ filterLast7: "Last 7 days",
17014
+ filterLast30: "Last 30 days",
17015
+ filterAfter: "After",
17016
+ filterBefore: "Before",
17017
+ filterPaths: "Paths",
17018
+ filterPathsMore: "Too many files — tree truncated",
17019
+ filterPathSearch: "Search files or folders",
17020
+ filterCalendarSets: "Calendar sets",
17021
+ filterSelected: "{count} selected",
17022
+ filterLocale: "en-US",
17023
+ allBranches: "All branches",
15248
17024
  expandAll: "Expand all",
15249
17025
  collapseAll: "Collapse all",
17026
+ filterFiles: "Filter files",
17027
+ filterFilesPlaceholder: "Filter files; space-separated terms",
17028
+ filterFilesClear: "Clear filter",
17029
+ filesFiltered: "{shown} / {count} files",
17030
+ filterNoMatch: "No file matches",
15250
17031
  noBranch: "(no branch)",
15251
17032
  copyCommit: "Copy message",
15252
17033
  copiedCommit: "Copied",
@@ -15280,6 +17061,14 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
15280
17061
  "op.ok.fetch": "Fetched",
15281
17062
  "op.ok.pull": "Pulled",
15282
17063
  "op.ok.push": "Pushed",
17064
+ "op.ok.discardFile": "Rolled back",
17065
+ discardAction: "Roll back changes",
17066
+ discardTitle: "Roll back changes?",
17067
+ discardConfirm: "Roll back",
17068
+ discardCancel: "Cancel",
17069
+ discardBodyRestore: "{path} goes back to its committed content. The {added} added and {deleted} deleted lines here cannot be recovered.",
17070
+ discardBodyDelete: "{path} was never recorded by git. Deleting it cannot be undone.",
17071
+ discardBodyUnrename: "Undo the rename: {path} goes back to {previousPath}, and content changed along the way is lost.",
15283
17072
  "op.fail.auth": "Authentication failed. Credential prompts are disabled here — set your credentials up in a terminal first.",
15284
17073
  "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
17074
  "op.fail.no-upstream": "This branch has no upstream.",
@@ -15334,15 +17123,27 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
15334
17123
  } }, signal);
15335
17124
  return result.ok ? result.value : null;
15336
17125
  },
15337
- fetchCommits: async (worktreePath, ref, skip, limit, signal) => {
17126
+ fetchCommits: async (worktreePath, ref, skip, limit, filter, signal) => {
15338
17127
  const result = await connection.rpc.call("/api", "gitWorkbench/commits", { args: {
15339
17128
  worktreePath: worktreePath ?? "",
15340
17129
  ref,
15341
17130
  skip,
15342
- limit
17131
+ limit,
17132
+ filter
17133
+ } }, signal);
17134
+ return result.ok ? result.value : null;
17135
+ },
17136
+ fetchAuthors: async (worktreePath, ref, signal) => {
17137
+ const result = await connection.rpc.call("/api", "gitWorkbench/authors", { args: {
17138
+ worktreePath: worktreePath ?? "",
17139
+ ref
15343
17140
  } }, signal);
15344
17141
  return result.ok ? result.value : null;
15345
17142
  },
17143
+ fetchRepoTree: async (worktreePath, signal) => {
17144
+ const result = await connection.rpc.call("/api", "gitWorkbench/repoTree", { args: { worktreePath: worktreePath ?? "" } }, signal);
17145
+ return result.ok ? result.value : null;
17146
+ },
15346
17147
  fetchCompare: async (worktreePath, base, head, signal) => {
15347
17148
  const result = await connection.rpc.call("/api", "gitWorkbench/compareRefs", { args: {
15348
17149
  worktreePath: worktreePath ?? "",
@@ -15382,6 +17183,27 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
15382
17183
  const result = await connection.rpc.call("/api", "gitWorkbench/syncStatus", { args: { worktreePath: worktreePath ?? "" } }, signal);
15383
17184
  return result.ok ? result.value : null;
15384
17185
  },
17186
+ fetchDiscardPlan: async (worktreePath, path, signal) => {
17187
+ try {
17188
+ const result = await connection.rpc.call("/api", "gitWorkbench/discardPlan", { args: {
17189
+ worktreePath: worktreePath ?? "",
17190
+ path
17191
+ } }, signal);
17192
+ if (result.ok && result.value !== void 0) return {
17193
+ kind: "plan",
17194
+ plan: result.value
17195
+ };
17196
+ return {
17197
+ kind: "failed",
17198
+ error: result.error?.message ?? ""
17199
+ };
17200
+ } catch (error) {
17201
+ return {
17202
+ kind: "failed",
17203
+ error: error instanceof Error ? error.message : String(error)
17204
+ };
17205
+ }
17206
+ },
15385
17207
  runGitOp: async (op, worktreePath, payload, signal) => {
15386
17208
  const result = await connection.rpc.call("/api", `gitWorkbench/${op}`, { args: {
15387
17209
  worktreePath: worktreePath ?? "",