@young1lin/dsh-ui-gitworkbench 0.1.15 → 0.1.16

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.
Files changed (41) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/CHANGELOG_EN.md +16 -0
  3. package/README.md +4 -3
  4. package/README_EN.md +1 -1
  5. package/lib/client.js +1519 -477
  6. package/lib/dir-listing.js +34 -0
  7. package/lib/fs-remove.js +5 -36
  8. package/lib/index.js +192 -41
  9. package/lib/path-lock.js +54 -0
  10. package/lib/worktree.js +83 -0
  11. package/lib/write-checked.js +1 -1
  12. package/package.json +1 -1
  13. package/src/client/ChromeGlyph.tsx +5 -0
  14. package/src/client/CodeEditor.tsx +19 -1
  15. package/src/client/DiffViews.tsx +116 -121
  16. package/src/client/FileBrowser.tsx +196 -23
  17. package/src/client/GitWorkbenchPanel.module.css +1 -0
  18. package/src/client/GitWorkbenchPanel.tsx +100 -12
  19. package/src/client/SideRails.tsx +106 -0
  20. package/src/client/diff-cells.tsx +147 -0
  21. package/src/client/diff-nav.ts +4 -1
  22. package/src/client/dir-tree.ts +31 -1
  23. package/src/client/file-rows.ts +40 -0
  24. package/src/client/h-rail.ts +70 -0
  25. package/src/client/ignored-cache.ts +193 -0
  26. package/src/client/index.ts +22 -3
  27. package/src/client/locales.ts +12 -2
  28. package/src/client/row-heights.ts +225 -0
  29. package/src/client/styles/changes.css +33 -2
  30. package/src/client/styles/controls.css +5 -0
  31. package/src/client/styles/files.css +5 -0
  32. package/src/client/styles/rails.css +72 -0
  33. package/src/client/use-row-window.ts +7 -3
  34. package/src/client/use-variable-row-window.ts +210 -0
  35. package/src/dir-listing.ts +47 -0
  36. package/src/fs-remove.ts +5 -36
  37. package/src/index.ts +217 -43
  38. package/src/path-lock.ts +56 -0
  39. package/src/types/dsh-shim.d.ts +12 -2
  40. package/src/worktree.ts +97 -0
  41. package/src/write-checked.ts +1 -1
package/lib/client.js CHANGED
@@ -459,7 +459,7 @@ window.__ModuleLoader__.load({
459
459
  }
460
460
  //#endregion
461
461
  //#region \0dsh-css:src/client/GitWorkbenchPanel.module.css.mjs
462
- 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_paneDividerY{cursor:row-resize;width:auto;height:7px}.SD8qLW_paneDividerY:before{width:auto;height:1px;inset:3px 0 auto}.SD8qLW_paneDividerY:after{width:28px;height:3px;transition:opacity .14s,width .14s}.SD8qLW_paneDividerY.SD8qLW_paneDividerActive:after{width:48px;height:3px}.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);border-radius:var(--gs-r-pill);background:var(--gs-accent-bg);color:var(--gs-accent);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{border-radius:var(--gs-r-pill);background:var(--gs-neutral-bg);color:var(--gs-fg-muted);font-size:var(--gs-t-meta);font-variant-numeric:tabular-nums;flex:none;padding:1px 8px}.SD8qLW_headerDetached{border-radius:var(--gs-r-pill);background:var(--gs-warn-bg);color:var(--gs-warn);font-size:var(--gs-t-meta);letter-spacing:.02em;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-del)}.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-control);background:var(--gs-bg);flex:none;gap:2px;margin:8px 8px 0;padding:2px;display:flex}.SD8qLW_funnelTab{box-sizing:border-box;min-width:0;height:var(--gs-h-compact);border-radius:calc(var(--gs-r-control) - 3px);color:var(--gs-fg-dim);font-family:inherit;font-size:var(--gs-t-dense);white-space:nowrap;cursor:pointer;background:0 0;border:1px solid #0000;flex:1;padding:0 8px;line-height:1;transition:background .12s,color .12s}.SD8qLW_funnelTab:hover{color:var(--gs-fg)}.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);font-size:var(--gs-t-meta);letter-spacing:.04em;text-transform:uppercase;flex:none;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{box-sizing:border-box;min-width:0;height:var(--gs-h-compact);border-radius:calc(var(--gs-r-control) - 3px);color:var(--gs-fg-dim);font-family:inherit;font-size:var(--gs-t-dense);cursor:pointer;background:0 0;border:1px solid #0000;flex:1;padding:0 6px;line-height:1;transition:background .12s,color .12s}.SD8qLW_funnelBoundBtn:hover{color:var(--gs-fg)}.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{width:var(--gs-h-compact);height:var(--gs-h-compact);border-radius:var(--gs-r-control);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:var(--gs-t-meta);line-height:14px}.SD8qLW_calGrid button{height:var(--gs-h-compact);border-radius:var(--gs-r-control);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);min-width:0;font-size:var(--gs-t-meta);align-items:center;gap:4px;padding:3px 6px;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-del)}.SD8qLW_funnelSearch{box-sizing:border-box;height:var(--gs-h-compact);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: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);border-radius:var(--gs-r-pill);background-clip:content-box;border:3px solid #0000}.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-size:var(--gs-t-meta);font-variant-numeric:tabular-nums;flex:none}.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_funnelFoot{border-top:1px solid var(--gs-border-soft);background:var(--gs-bg);font-size:var(--gs-t-meta);flex:none;justify-content:space-between;align-items:center;gap:8px;padding:6px 10px;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);font-family:inherit;font-size:var(--gs-t-meta);cursor:pointer;background:0 0;border:none;flex:none;padding:2px 6px;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-wrap:wrap;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{height:var(--gs-commit-row);flex:none;align-items:stretch;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:auto;align-items:center;gap:10px;margin:1px 0;padding:4px 8px;transition:background .12s,border-color .12s;display:flex;overflow:hidden}.SD8qLW_commitsPane[data-layout=columns] .SD8qLW_commit{flex-direction:column;justify-content:center;align-items:stretch;gap:2px}.SD8qLW_commitsPane[data-layout=columns] .SD8qLW_commitSubjectRow{flex:none}.SD8qLW_commitTop{justify-content:space-between;align-items:center;gap:8px;min-width:0;display:flex}.SD8qLW_commit:hover{background:var(--gs-raise)}.SD8qLW_commitMeta{flex:none;align-items:baseline;gap:8px;display:flex}.SD8qLW_commitHash{color:var(--gs-info);flex:none}.SD8qLW_commitSubjectRow{flex:auto;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:0 auto;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_body[data-stacked]{flex-direction:column}.SD8qLW_bodyRow{flex:1 1 0;min-width:0;min-height:0;display:flex}.SD8qLW_body[data-stacked] .SD8qLW_commitsPane{width:auto;min-width:0;max-width:none;height:34%;min-height:var(--gs-min-commits-tall);max-height:max(var(--gs-min-commits-tall), calc(100% - var(--gs-min-stacked-lower)));flex:none}.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;border-radius:var(--gs-r-control);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-raise);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{border-radius:var(--gs-r-pill);background:var(--gs-neutral-bg);color:var(--gs-fg-dim);font-size:var(--gs-t-meta);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;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;border-left:2px solid #0000;align-items:center;gap:8px;padding:5px 14px;display:flex}.SD8qLW_file:hover{background:var(--gs-raise)}.SD8qLW_fileStatus{border-radius:var(--gs-r-control);width:18px;height:18px;font-weight:700;font-size:var(--gs-t-meta);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{border-radius:var(--gs-r-control);background:var(--gs-neutral-bg);color:var(--gs-fg-dim);letter-spacing:.06em;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-direction:column;flex:1 1 0;display:flex;overflow:auto}.SD8qLW_diffWrap{flex-direction:column;flex:auto;width:100%;min-height:0;display:flex}.SD8qLW_diffScroll{outline:none;flex:1 1 0;min-height:0;overflow:auto}.SD8qLW_diffNav{border-bottom:1px solid var(--gs-border-soft);flex:none;justify-content:flex-end;align-items:center;gap:4px;padding:3px 12px 3px 0;display:flex}.SD8qLW_diffNav .SD8qLW_blockBtn{padding-inline:6px;line-height:0}.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);flex:none;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_crMark{color:var(--gs-fg-faint);user-select:none;font-size:.9em}.SD8qLW_sidePane{flex-direction:column;width:100%;min-width:0;height:100%;display:flex}.SD8qLW_sideScroll{flex:1 1 0;min-height:0;overflow:hidden auto}.SD8qLW_sideCols{align-items:stretch;min-height:100%;display:flex}.SD8qLW_sideCol{flex:none;min-width:0;overflow-x:auto}.SD8qLW_sideColRight{flex:1 1 0}.SD8qLW_sideColGrid{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;grid-template-columns:3.2em 1fr;align-content:start;padding:8px 0 16px;font-weight:400;line-height:20px;display:grid}.SD8qLW_sideColGridBlame{grid-template-columns:13em 3.2em 1fr}.SD8qLW_blameCell{box-sizing:border-box;border-right:1px solid var(--gs-border-soft);color:var(--gs-fg-faint);font-size:var(--gs-t-meta);white-space:pre;text-overflow:ellipsis;user-select:none;padding:0 8px 0 10px;overflow:hidden}.SD8qLW_sideTabs{z-index:5;border-bottom:1px solid var(--gs-border-soft);background:var(--gs-surface-2);flex-wrap:wrap;flex:none;align-items:flex-start;gap:0 16px;padding:6px 16px 0;display:flex}.SD8qLW_sideTab{font-family:inherit;font-size:var(--gs-t-meta);color:var(--gs-fg-dim);cursor:pointer;background:0 0;border:none;padding:2px 0 5px;font-weight:500;line-height:16px;position:relative}.SD8qLW_sideTab:after{content:\"\";background:0 0;border-radius:2px;height:2px;position:absolute;bottom:1px;left:0;right:0}.SD8qLW_sideTabActive{color:var(--gs-accent)}.SD8qLW_sideTabActive:after{background:var(--gs-accent)}.SD8qLW_sideNotice{border-bottom:1px solid var(--gs-border-soft);color:var(--gs-info);font-size:var(--gs-t-dense);flex:none;padding:8px 16px}.SD8qLW_sideNum{box-sizing:border-box;text-align:right;color:var(--gs-fg-fainter);user-select:none;font-variant-numeric:tabular-nums;padding:0 8px}.SD8qLW_sideNumAdd{background:var(--gs-add-num,var(--gs-add-line))}.SD8qLW_sideNumDel{background:var(--gs-del-num,var(--gs-del-line))}.SD8qLW_sideCode{white-space:pre;min-height:20px;padding:0 16px 0 10px}.SD8qLW_sideSpacer{grid-column:1/-1}.SD8qLW_diffSpacer{flex:none}.SD8qLW_sideCodeSame{color:var(--gs-fg-muted)}.SD8qLW_sideCodeAdd{background:var(--gs-add-line)}.SD8qLW_sideCodeDel{background:var(--gs-del-line)}.SD8qLW_sideCellBlock{position:relative}.SD8qLW_sideNum.SD8qLW_sideBlockHot{box-shadow:inset 1px 0 var(--gs-accent)}.SD8qLW_sideCode.SD8qLW_sideBlockHot{box-shadow:inset -1px 0 var(--gs-accent)}.SD8qLW_sideNum.SD8qLW_sideBlockHot.SD8qLW_sideBlockHotFirst{box-shadow:inset 1px 0 var(--gs-accent), inset 0 1px var(--gs-accent)}.SD8qLW_sideCode.SD8qLW_sideBlockHot.SD8qLW_sideBlockHotFirst{box-shadow:inset -1px 0 var(--gs-accent), inset 0 1px var(--gs-accent)}.SD8qLW_sideNum.SD8qLW_sideBlockHot.SD8qLW_sideBlockHotLast{box-shadow:inset 1px 0 var(--gs-accent), inset 0 -1px var(--gs-accent)}.SD8qLW_sideCode.SD8qLW_sideBlockHot.SD8qLW_sideBlockHotLast{box-shadow:inset -1px 0 var(--gs-accent), inset 0 -1px var(--gs-accent)}.SD8qLW_sideNum.SD8qLW_sideBlockHot.SD8qLW_sideBlockHotFirst.SD8qLW_sideBlockHotLast{box-shadow:inset 1px 0 var(--gs-accent), inset 0 1px var(--gs-accent), inset 0 -1px var(--gs-accent)}.SD8qLW_sideCode.SD8qLW_sideBlockHot.SD8qLW_sideBlockHotFirst.SD8qLW_sideBlockHotLast{box-shadow:inset -1px 0 var(--gs-accent), inset 0 1px var(--gs-accent), inset 0 -1px var(--gs-accent)}.SD8qLW_blockBar{z-index:4;border:1px solid var(--gs-border);border-radius:var(--gs-r-control);background:var(--gs-surface-2,var(--gs-bg));box-shadow:0 4px 14px var(--gs-shadow);white-space:nowrap;gap:4px;padding:2px;display:flex;position:absolute;left:0}.SD8qLW_blockBtn{border:1px solid var(--gs-border);border-radius:var(--gs-r-pill);background:var(--gs-bg);color:var(--gs-fg-dim);font-family:inherit;font-size:var(--gs-t-meta);cursor:pointer;padding:0 8px;line-height:16px}.SD8qLW_blockBtn:hover:not(:disabled){color:var(--gs-fg);border-color:var(--gs-accent)}.SD8qLW_blockBtn:disabled{opacity:.45;cursor:default}.SD8qLW_blockBtnDanger{color:var(--gs-del);border-color:var(--gs-del-bg,var(--gs-border))}.SD8qLW_blockBtnDanger:hover:not(:disabled){color:var(--gs-del);border-color:var(--gs-del)}.SD8qLW_sideArmable{cursor:text}.SD8qLW_cmHost{position:relative}.SD8qLW_cmHost[data-editable]:before{content:\"\";background:var(--gs-accent);opacity:.32;pointer-events:none;z-index:201;border-radius:1px;width:2px;transition:opacity .14s;position:absolute;top:0;bottom:0;left:0}.SD8qLW_cmHost[data-editable]:focus-within:before{opacity:1}.SD8qLW_sideNav{align-items:center;gap:4px;margin-left:auto;padding:2px 0 5px;display:flex}.SD8qLW_sideNav .SD8qLW_blockBtn{padding-inline:6px;line-height:0}.SD8qLW_sideNavCount{color:var(--gs-fg-faint);font-size:var(--gs-t-meta);white-space:nowrap}.SD8qLW_sideCurrentBlockActions{flex-wrap:wrap;align-items:flex-start;gap:4px;margin-left:4px;padding:2px 0 5px;display:flex}.SD8qLW_sideActions{align-items:flex-start;gap:4px;margin-left:auto;padding:2px 0 5px;display:flex}.SD8qLW_sideActionsAdjacent{margin-left:0}.SD8qLW_sideSaveReady{color:var(--gs-accent);border-color:var(--gs-accent)}.SD8qLW_sideBanner{border-bottom:1px solid var(--gs-border-soft);background:var(--gs-del-bg,transparent);color:var(--gs-del);font-size:var(--gs-t-dense);flex-direction:column;flex:none;gap:6px;padding:8px 16px;display:flex}.SD8qLW_sideBannerTitle{font-weight:600}.SD8qLW_sideBannerActs{gap:4px;display:flex}.SD8qLW_sideEditCell{position:relative}.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,.SD8qLW_layoutButton{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,.SD8qLW_layoutButton: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,.SD8qLW_layoutButton: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,.SD8qLW_layoutButton{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_layoutSwitch{flex:none;gap:2px;margin-left:auto;display:inline-flex}.SD8qLW_layoutButton{width:var(--gs-h-compact);padding:0}.SD8qLW_layoutButton.SD8qLW_layoutButtonOn{color:var(--gs-accent);border-color:var(--gs-accent)}.SD8qLW_layoutGlyph{display:block}.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,.SD8qLW_fileActive,.SD8qLW_commitActive,.SD8qLW_funnelTabActive,.SD8qLW_funnelBoundBtnActive,.SD8qLW_funnelPresetActive,.SD8qLW_fbRow.SD8qLW_fbRowActive{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);font-weight:600}.SD8qLW_btnPrimary:hover:not(:disabled){background:var(--gs-accent);color:var(--gs-on-accent);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);border-radius:var(--gs-r-pill);background-clip:padding-box;border:3px solid #0000}.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);font-weight:600}.SD8qLW_btn.SD8qLW_btnDanger:hover:not(:disabled){border-color:var(--gs-del);background:var(--gs-del);color:var(--gs-on-accent)}.SD8qLW_fbTree{min-width:var(--gs-min-tree);background:var(--gs-surface-2);flex-direction:column;flex:none;gap:4px;padding:6px;display:flex;overflow:auto}.SD8qLW_fbSearch{box-sizing:border-box;width:100%;font:inherit;font-size:var(--gs-t-meta);color:var(--gs-fg);background:var(--gs-surface);border:1px solid var(--gs-border);border-radius:var(--gs-r-control);flex:none;padding:4px 6px}.SD8qLW_fbSearch:focus{border-color:var(--gs-accent);outline:none}.SD8qLW_fbNote{font-size:var(--gs-t-meta);color:var(--gs-fg-dim);flex:none;padding:2px 4px}.SD8qLW_fbList{margin:0;padding:0;list-style:none}.SD8qLW_fbRow{width:100%;font:inherit;font-size:var(--gs-t-dense);color:var(--gs-fg-dim);text-align:left;border-radius:var(--gs-r-control);cursor:pointer;white-space:nowrap;text-overflow:ellipsis;background:0 0;border:0;align-items:center;gap:4px;padding:2px 6px 2px 4px;display:flex;overflow:hidden}.SD8qLW_fbRow:hover{background:var(--gs-raise)}.SD8qLW_fbDirName,.SD8qLW_fbFileName{text-overflow:ellipsis;overflow:hidden}.SD8qLW_fbMain{min-width:var(--gs-min-diff);background:var(--gs-surface);flex-direction:column;flex:1 1 0;min-height:0;display:flex}.SD8qLW_fbHeader{border-bottom:1px solid var(--gs-border);flex:none;align-items:flex-start;gap:8px;padding:4px 8px 0;display:flex}.SD8qLW_fbPath{font-size:var(--gs-t-meta);color:var(--gs-fg-dim);text-overflow:ellipsis;white-space:nowrap;padding:4px 0;overflow:hidden}.SD8qLW_fbBody{flex:1 1 0;min-height:0;padding:4px 8px;overflow:auto}.SD8qLW_fbCommit{font-size:var(--gs-t-meta);color:var(--gs-fg-dim);background:var(--gs-surface-2);border-bottom:1px solid var(--gs-border);flex:none;align-items:baseline;gap:8px;padding:4px 8px;display:flex}.SD8qLW_fbCommitLine{color:var(--gs-fg-faint);font-variant-numeric:tabular-nums;flex:none}.SD8qLW_fbCommitWho{color:var(--gs-fg);flex:none;font-weight:600}.SD8qLW_fbCommitWhen{font-variant-numeric:tabular-nums;flex:none}.SD8qLW_fbCommitHash{color:var(--gs-fg-faint);flex:none;font-family:inherit}.SD8qLW_fbCommitWhat{text-overflow:ellipsis;white-space:nowrap;min-width:0;color:var(--gs-fg);flex:auto;overflow:hidden}.SD8qLW_fbCommitHint{color:var(--gs-fg-faint)}.SD8qLW_fbMore{font-size:var(--gs-t-meta);color:var(--gs-fg-faint);padding:2px 6px 4px 4px;font-style:italic;display:block}.SD8qLW_imgPane{flex-direction:column;gap:8px;height:100%;min-height:0;display:flex}.SD8qLW_imgStage{border-radius:var(--gs-r-control);background-color:var(--gs-surface-2);background-image:linear-gradient(45deg, var(--gs-border-soft) 25%, transparent 25%, transparent 75%, var(--gs-border-soft) 75%), linear-gradient(45deg, var(--gs-border-soft) 25%, transparent 25%, transparent 75%, var(--gs-border-soft) 75%);background-position:0 0,8px 8px;background-size:16px 16px;flex:auto;justify-content:center;align-items:center;min-height:0;padding:12px;display:flex;overflow:auto}.SD8qLW_imgShot{object-fit:contain;image-rendering:auto;max-width:100%;max-height:100%}.SD8qLW_imgStage[data-actual]{justify-content:flex-start;align-items:flex-start}.SD8qLW_imgStage[data-actual] .SD8qLW_imgShot{object-fit:none;max-width:none;max-height:none}.SD8qLW_imgCaption{font-size:var(--gs-t-meta);color:var(--gs-fg-dim);font-variant-numeric:tabular-nums;flex:none;align-items:center;gap:8px;display:flex}";
462
+ 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_paneDividerY{cursor:row-resize;width:auto;height:7px}.SD8qLW_paneDividerY:before{width:auto;height:1px;inset:3px 0 auto}.SD8qLW_paneDividerY:after{width:28px;height:3px;transition:opacity .14s,width .14s}.SD8qLW_paneDividerY.SD8qLW_paneDividerActive:after{width:48px;height:3px}.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);border-radius:var(--gs-r-pill);background:var(--gs-accent-bg);color:var(--gs-accent);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{border-radius:var(--gs-r-pill);background:var(--gs-neutral-bg);color:var(--gs-fg-muted);font-size:var(--gs-t-meta);font-variant-numeric:tabular-nums;flex:none;padding:1px 8px}.SD8qLW_headerDetached{border-radius:var(--gs-r-pill);background:var(--gs-warn-bg);color:var(--gs-warn);font-size:var(--gs-t-meta);letter-spacing:.02em;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-del)}.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-control);background:var(--gs-bg);flex:none;gap:2px;margin:8px 8px 0;padding:2px;display:flex}.SD8qLW_funnelTab{box-sizing:border-box;min-width:0;height:var(--gs-h-compact);border-radius:calc(var(--gs-r-control) - 3px);color:var(--gs-fg-dim);font-family:inherit;font-size:var(--gs-t-dense);white-space:nowrap;cursor:pointer;background:0 0;border:1px solid #0000;flex:1;padding:0 8px;line-height:1;transition:background .12s,color .12s}.SD8qLW_funnelTab:hover{color:var(--gs-fg)}.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);font-size:var(--gs-t-meta);letter-spacing:.04em;text-transform:uppercase;flex:none;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{box-sizing:border-box;min-width:0;height:var(--gs-h-compact);border-radius:calc(var(--gs-r-control) - 3px);color:var(--gs-fg-dim);font-family:inherit;font-size:var(--gs-t-dense);cursor:pointer;background:0 0;border:1px solid #0000;flex:1;padding:0 6px;line-height:1;transition:background .12s,color .12s}.SD8qLW_funnelBoundBtn:hover{color:var(--gs-fg)}.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{width:var(--gs-h-compact);height:var(--gs-h-compact);border-radius:var(--gs-r-control);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:var(--gs-t-meta);line-height:14px}.SD8qLW_calGrid button{height:var(--gs-h-compact);border-radius:var(--gs-r-control);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);min-width:0;font-size:var(--gs-t-meta);align-items:center;gap:4px;padding:3px 6px;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-del)}.SD8qLW_funnelSearch{box-sizing:border-box;height:var(--gs-h-compact);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: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);border-radius:var(--gs-r-pill);background-clip:content-box;border:3px solid #0000}.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-size:var(--gs-t-meta);font-variant-numeric:tabular-nums;flex:none}.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_funnelFoot{border-top:1px solid var(--gs-border-soft);background:var(--gs-bg);font-size:var(--gs-t-meta);flex:none;justify-content:space-between;align-items:center;gap:8px;padding:6px 10px;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);font-family:inherit;font-size:var(--gs-t-meta);cursor:pointer;background:0 0;border:none;flex:none;padding:2px 6px;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-wrap:wrap;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{height:var(--gs-commit-row);flex:none;align-items:stretch;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:auto;align-items:center;gap:10px;margin:1px 0;padding:4px 8px;transition:background .12s,border-color .12s;display:flex;overflow:hidden}.SD8qLW_commitsPane[data-layout=columns] .SD8qLW_commit{flex-direction:column;justify-content:center;align-items:stretch;gap:2px}.SD8qLW_commitsPane[data-layout=columns] .SD8qLW_commitSubjectRow{flex:none}.SD8qLW_commitTop{justify-content:space-between;align-items:center;gap:8px;min-width:0;display:flex}.SD8qLW_commit:hover{background:var(--gs-raise)}.SD8qLW_commitMeta{flex:none;align-items:baseline;gap:8px;display:flex}.SD8qLW_commitHash{color:var(--gs-info);flex:none}.SD8qLW_commitSubjectRow{flex:auto;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:0 auto;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_body[data-stacked]{flex-direction:column}.SD8qLW_bodyRow{flex:1 1 0;min-width:0;min-height:0;display:flex}.SD8qLW_body[data-stacked] .SD8qLW_commitsPane{width:auto;min-width:0;max-width:none;height:34%;min-height:var(--gs-min-commits-tall);max-height:max(var(--gs-min-commits-tall), calc(100% - var(--gs-min-stacked-lower)));flex:none}.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;border-radius:var(--gs-r-control);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-raise);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{border-radius:var(--gs-r-pill);background:var(--gs-neutral-bg);color:var(--gs-fg-dim);font-size:var(--gs-t-meta);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;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;border-left:2px solid #0000;align-items:center;gap:8px;padding:5px 14px;display:flex}.SD8qLW_file:hover{background:var(--gs-raise)}.SD8qLW_fileStatus{border-radius:var(--gs-r-control);width:18px;height:18px;font-weight:700;font-size:var(--gs-t-meta);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{border-radius:var(--gs-r-control);background:var(--gs-neutral-bg);color:var(--gs-fg-dim);letter-spacing:.06em;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-direction:column;flex:1 1 0;display:flex;overflow:auto}.SD8qLW_diffWrap{flex-direction:column;flex:auto;width:100%;min-height:0;display:flex}.SD8qLW_diffScroll{outline:none;flex:1 1 0;min-height:0;overflow:auto}.SD8qLW_diffNav{border-bottom:1px solid var(--gs-border-soft);flex:none;justify-content:flex-end;align-items:center;gap:4px;padding:3px 12px 3px 0;display:flex}.SD8qLW_diffNav .SD8qLW_blockBtn{padding-inline:6px;line-height:0}.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);flex:none;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_diffPreWrap{width:100%;min-width:0}.SD8qLW_diffPreWrap .SD8qLW_line{align-items:flex-start}.SD8qLW_diffPreWrap .SD8qLW_code{white-space:pre-wrap;overflow-wrap:anywhere;flex:1 1 0;min-width:0}.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_crMark{color:var(--gs-fg-faint);user-select:none;font-size:.9em}.SD8qLW_sidePane{flex-direction:column;width:100%;min-width:0;height:100%;display:flex}.SD8qLW_sideScroll{flex:1 1 0;min-height:0;overflow:hidden auto}.SD8qLW_sideCols{align-items:stretch;min-height:100%;display:flex}.SD8qLW_sideCol{scrollbar-width:none;flex:none;min-width:0;overflow-x:auto}.SD8qLW_sideCol::-webkit-scrollbar{display:none}.SD8qLW_sideColRight{flex:1 1 0}.SD8qLW_sideColGrid{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;grid-template-columns:3.2em 1fr;align-content:start;padding:8px 0 16px;font-weight:400;line-height:20px;display:grid}.SD8qLW_sideColGridBlame{grid-template-columns:13em 3.2em 1fr}.SD8qLW_blameCell{box-sizing:border-box;border-right:1px solid var(--gs-border-soft);color:var(--gs-fg-faint);font-size:var(--gs-t-meta);white-space:pre;text-overflow:ellipsis;user-select:none;padding:0 8px 0 10px;overflow:hidden}.SD8qLW_sideTabs{z-index:5;border-bottom:1px solid var(--gs-border-soft);background:var(--gs-surface-2);flex-wrap:wrap;flex:none;align-items:flex-start;gap:0 16px;padding:6px 16px 0;display:flex}.SD8qLW_sideTab{font-family:inherit;font-size:var(--gs-t-meta);color:var(--gs-fg-dim);cursor:pointer;background:0 0;border:none;padding:2px 0 5px;font-weight:500;line-height:16px;position:relative}.SD8qLW_sideTab:after{content:\"\";background:0 0;border-radius:2px;height:2px;position:absolute;bottom:1px;left:0;right:0}.SD8qLW_sideTabActive{color:var(--gs-accent)}.SD8qLW_sideTabActive:after{background:var(--gs-accent)}.SD8qLW_sideNotice{border-bottom:1px solid var(--gs-border-soft);color:var(--gs-info);font-size:var(--gs-t-dense);flex:none;padding:8px 16px}.SD8qLW_sideNum{box-sizing:border-box;text-align:right;color:var(--gs-fg-fainter);user-select:none;font-variant-numeric:tabular-nums;padding:0 8px}.SD8qLW_sideNumAdd{background:var(--gs-add-num,var(--gs-add-line))}.SD8qLW_sideNumDel{background:var(--gs-del-num,var(--gs-del-line))}.SD8qLW_sideCode{white-space:pre;min-height:20px;padding:0 16px 0 10px}.SD8qLW_sideColWrap{overflow-x:hidden}.SD8qLW_sideColGridWrap{width:100%;min-width:0}.SD8qLW_sideColGridWrap .SD8qLW_sideCode{white-space:pre-wrap;overflow-wrap:anywhere;min-width:0}.SD8qLW_sideFlow{height:auto;display:block}.SD8qLW_sideSpacer{grid-column:1/-1}.SD8qLW_diffSpacer{flex:none}.SD8qLW_sideCodeSame{color:var(--gs-fg-muted)}.SD8qLW_sideCodeAdd{background:var(--gs-add-line)}.SD8qLW_sideCodeDel{background:var(--gs-del-line)}.SD8qLW_sideCellBlock{position:relative}.SD8qLW_sideNum.SD8qLW_sideBlockHot{box-shadow:inset 1px 0 var(--gs-accent)}.SD8qLW_sideCode.SD8qLW_sideBlockHot{box-shadow:inset -1px 0 var(--gs-accent)}.SD8qLW_sideNum.SD8qLW_sideBlockHot.SD8qLW_sideBlockHotFirst{box-shadow:inset 1px 0 var(--gs-accent), inset 0 1px var(--gs-accent)}.SD8qLW_sideCode.SD8qLW_sideBlockHot.SD8qLW_sideBlockHotFirst{box-shadow:inset -1px 0 var(--gs-accent), inset 0 1px var(--gs-accent)}.SD8qLW_sideNum.SD8qLW_sideBlockHot.SD8qLW_sideBlockHotLast{box-shadow:inset 1px 0 var(--gs-accent), inset 0 -1px var(--gs-accent)}.SD8qLW_sideCode.SD8qLW_sideBlockHot.SD8qLW_sideBlockHotLast{box-shadow:inset -1px 0 var(--gs-accent), inset 0 -1px var(--gs-accent)}.SD8qLW_sideNum.SD8qLW_sideBlockHot.SD8qLW_sideBlockHotFirst.SD8qLW_sideBlockHotLast{box-shadow:inset 1px 0 var(--gs-accent), inset 0 1px var(--gs-accent), inset 0 -1px var(--gs-accent)}.SD8qLW_sideCode.SD8qLW_sideBlockHot.SD8qLW_sideBlockHotFirst.SD8qLW_sideBlockHotLast{box-shadow:inset -1px 0 var(--gs-accent), inset 0 1px var(--gs-accent), inset 0 -1px var(--gs-accent)}.SD8qLW_blockBar{z-index:4;border:1px solid var(--gs-border);border-radius:var(--gs-r-control);background:var(--gs-surface-2,var(--gs-bg));box-shadow:0 4px 14px var(--gs-shadow);white-space:nowrap;gap:4px;padding:2px;display:flex;position:absolute;left:0}.SD8qLW_blockBtn{border:1px solid var(--gs-border);border-radius:var(--gs-r-pill);background:var(--gs-bg);color:var(--gs-fg-dim);font-family:inherit;font-size:var(--gs-t-meta);cursor:pointer;padding:0 8px;line-height:16px}.SD8qLW_blockBtn:hover:not(:disabled){color:var(--gs-fg);border-color:var(--gs-accent)}.SD8qLW_blockBtn:disabled{opacity:.45;cursor:default}.SD8qLW_blockBtnDanger{color:var(--gs-del);border-color:var(--gs-del-bg,var(--gs-border))}.SD8qLW_blockBtnDanger:hover:not(:disabled){color:var(--gs-del);border-color:var(--gs-del)}.SD8qLW_sideArmable{cursor:text}.SD8qLW_cmHost{position:relative}.SD8qLW_cmHost[data-editable]:before{content:\"\";background:var(--gs-accent);opacity:.32;pointer-events:none;z-index:201;border-radius:1px;width:2px;transition:opacity .14s;position:absolute;top:0;bottom:0;left:0}.SD8qLW_cmHost[data-editable]:focus-within:before{opacity:1}.SD8qLW_sideNav{align-items:center;gap:4px;margin-left:auto;padding:2px 0 5px;display:flex}.SD8qLW_sideNav .SD8qLW_blockBtn{padding-inline:6px;line-height:0}.SD8qLW_sideNavCount{color:var(--gs-fg-faint);font-size:var(--gs-t-meta);white-space:nowrap}.SD8qLW_sideCurrentBlockActions{flex-wrap:wrap;align-items:flex-start;gap:4px;margin-left:4px;padding:2px 0 5px;display:flex}.SD8qLW_sideActions{align-items:flex-start;gap:4px;margin-left:auto;padding:2px 0 5px;display:flex}.SD8qLW_sideActionsAdjacent{margin-left:0}.SD8qLW_sideSaveReady{color:var(--gs-accent);border-color:var(--gs-accent)}.SD8qLW_sideBanner{border-bottom:1px solid var(--gs-border-soft);background:var(--gs-del-bg,transparent);color:var(--gs-del);font-size:var(--gs-t-dense);flex-direction:column;flex:none;gap:6px;padding:8px 16px;display:flex}.SD8qLW_sideBannerTitle{font-weight:600}.SD8qLW_sideBannerActs{gap:4px;display:flex}.SD8qLW_sideEditCell{position:relative}.SD8qLW_empty{color:var(--gs-fg-faint);font-size:var(--gs-t-ui);padding:32px 24px}.SD8qLW_sideRails{z-index:6;align-items:stretch;height:0;display:flex;position:sticky;bottom:0;overflow:hidden}.SD8qLW_sideRailsOn{background:var(--gs-surface);height:12px;margin-top:-12px;overflow:visible}.SD8qLW_sideRail{flex:none;min-width:0;overflow:auto hidden}@supports ((-moz-appearance:none)){.SD8qLW_sideRail{scrollbar-width:thin;scrollbar-color:var(--gs-fg-fainter) transparent}}.SD8qLW_sideRail::-webkit-scrollbar{height:10px}.SD8qLW_sideRail::-webkit-scrollbar-track{background:0 0}.SD8qLW_sideRail::-webkit-scrollbar-thumb{background:var(--gs-fg-fainter);border-radius:var(--gs-r-pill);background-clip:padding-box;border:3px solid #0000}.SD8qLW_sideRail:hover::-webkit-scrollbar-thumb{background:var(--gs-fg-faint);background-clip:padding-box}.SD8qLW_sideRailRight{flex:1 1 0}.SD8qLW_sideRailGap{flex:none;width:7px}.SD8qLW_sideRailSpan{height:1px}.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,.SD8qLW_layoutButton{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,.SD8qLW_layoutButton: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,.SD8qLW_layoutButton: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,.SD8qLW_layoutButton{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_layoutSwitch{flex:none;gap:2px;margin-left:auto;display:inline-flex}.SD8qLW_layoutButton{width:var(--gs-h-compact);padding:0}.SD8qLW_layoutButton.SD8qLW_layoutButtonOn{color:var(--gs-accent);border-color:var(--gs-accent)}.SD8qLW_layoutGlyph{display:block}.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,.SD8qLW_fileActive,.SD8qLW_commitActive,.SD8qLW_funnelTabActive,.SD8qLW_funnelBoundBtnActive,.SD8qLW_funnelPresetActive,.SD8qLW_fbRow.SD8qLW_fbRowActive{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_btnIcon.SD8qLW_btnIconOn{color:var(--gs-accent);border-color:var(--gs-accent)}.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);font-weight:600}.SD8qLW_btnPrimary:hover:not(:disabled){background:var(--gs-accent);color:var(--gs-on-accent);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);border-radius:var(--gs-r-pill);background-clip:padding-box;border:3px solid #0000}.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);font-weight:600}.SD8qLW_btn.SD8qLW_btnDanger:hover:not(:disabled){border-color:var(--gs-del);background:var(--gs-del);color:var(--gs-on-accent)}.SD8qLW_fbTree{min-width:var(--gs-min-tree);background:var(--gs-surface-2);flex-direction:column;flex:none;gap:4px;padding:6px;display:flex;overflow:auto}.SD8qLW_fbSearch{box-sizing:border-box;width:100%;font:inherit;font-size:var(--gs-t-meta);color:var(--gs-fg);background:var(--gs-surface);border:1px solid var(--gs-border);border-radius:var(--gs-r-control);flex:none;padding:4px 6px}.SD8qLW_fbSearch:focus{border-color:var(--gs-accent);outline:none}.SD8qLW_fbNote{font-size:var(--gs-t-meta);color:var(--gs-fg-dim);flex:none;padding:2px 4px}.SD8qLW_fbList{margin:0;padding:0;list-style:none}.SD8qLW_fbRow{width:100%;font:inherit;font-size:var(--gs-t-dense);color:var(--gs-fg-dim);text-align:left;border-radius:var(--gs-r-control);cursor:pointer;white-space:nowrap;text-overflow:ellipsis;background:0 0;border:0;align-items:center;gap:4px;padding:2px 6px 2px 4px;display:flex;overflow:hidden}.SD8qLW_fbRow:hover{background:var(--gs-raise)}.SD8qLW_fbRow.SD8qLW_fbRowIgnored{opacity:.55}.SD8qLW_fbDirName,.SD8qLW_fbFileName{text-overflow:ellipsis;overflow:hidden}.SD8qLW_fbMain{min-width:var(--gs-min-diff);background:var(--gs-surface);flex-direction:column;flex:1 1 0;min-height:0;display:flex}.SD8qLW_fbHeader{border-bottom:1px solid var(--gs-border);flex:none;align-items:flex-start;gap:8px;padding:4px 8px 0;display:flex}.SD8qLW_fbPath{font-size:var(--gs-t-meta);color:var(--gs-fg-dim);text-overflow:ellipsis;white-space:nowrap;padding:4px 0;overflow:hidden}.SD8qLW_fbBody{flex:1 1 0;min-height:0;padding:4px 8px;overflow:auto}.SD8qLW_fbCommit{font-size:var(--gs-t-meta);color:var(--gs-fg-dim);background:var(--gs-surface-2);border-bottom:1px solid var(--gs-border);flex:none;align-items:baseline;gap:8px;padding:4px 8px;display:flex}.SD8qLW_fbCommitLine{color:var(--gs-fg-faint);font-variant-numeric:tabular-nums;flex:none}.SD8qLW_fbCommitWho{color:var(--gs-fg);flex:none;font-weight:600}.SD8qLW_fbCommitWhen{font-variant-numeric:tabular-nums;flex:none}.SD8qLW_fbCommitHash{color:var(--gs-fg-faint);flex:none;font-family:inherit}.SD8qLW_fbCommitWhat{text-overflow:ellipsis;white-space:nowrap;min-width:0;color:var(--gs-fg);flex:auto;overflow:hidden}.SD8qLW_fbCommitHint{color:var(--gs-fg-faint)}.SD8qLW_fbMore{font-size:var(--gs-t-meta);color:var(--gs-fg-faint);padding:2px 6px 4px 4px;font-style:italic;display:block}.SD8qLW_imgPane{flex-direction:column;gap:8px;height:100%;min-height:0;display:flex}.SD8qLW_imgStage{border-radius:var(--gs-r-control);background-color:var(--gs-surface-2);background-image:linear-gradient(45deg, var(--gs-border-soft) 25%, transparent 25%, transparent 75%, var(--gs-border-soft) 75%), linear-gradient(45deg, var(--gs-border-soft) 25%, transparent 25%, transparent 75%, var(--gs-border-soft) 75%);background-position:0 0,8px 8px;background-size:16px 16px;flex:auto;justify-content:center;align-items:center;min-height:0;padding:12px;display:flex;overflow:auto}.SD8qLW_imgShot{object-fit:contain;image-rendering:auto;max-width:100%;max-height:100%}.SD8qLW_imgStage[data-actual]{justify-content:flex-start;align-items:flex-start}.SD8qLW_imgStage[data-actual] .SD8qLW_imgShot{object-fit:none;max-width:none;max-height:none}.SD8qLW_imgCaption{font-size:var(--gs-t-meta);color:var(--gs-fg-dim);font-variant-numeric:tabular-nums;flex:none;align-items:center;gap:8px;display:flex}";
463
463
  const tagId = "@young1lin/dsh-ui-gitworkbench/GitWorkbenchPanel.module.css";
464
464
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
465
465
  const tag = document.createElement("style");
@@ -469,337 +469,349 @@ window.__ModuleLoader__.load({
469
469
  document.head.appendChild(tag);
470
470
  }
471
471
  var GitWorkbenchPanel_module_css_default = {
472
- "paneTitle": "SD8qLW_paneTitle",
473
- "funnelButton": "SD8qLW_funnelButton",
474
- "fbCommitWho": "SD8qLW_fbCommitWho",
475
- "funnelCount": "SD8qLW_funnelCount",
476
- "headerPathMain": "SD8qLW_headerPathMain",
477
- "fbPath": "SD8qLW_fbPath",
478
- "funnel": "SD8qLW_funnel",
479
- "funnelCaption": "SD8qLW_funnelCaption",
480
- "sideColRight": "SD8qLW_sideColRight",
481
- "treeIconDown": "SD8qLW_treeIconDown",
482
- "cal": "SD8qLW_cal",
483
- "funnelBoundClear": "SD8qLW_funnelBoundClear",
472
+ "theme": "SD8qLW_theme",
473
+ "paletteRow": "SD8qLW_paletteRow",
474
+ "lineHunk": "SD8qLW_lineHunk",
475
+ "sideBlockHotLast": "SD8qLW_sideBlockHotLast",
476
+ "commitsPane": "SD8qLW_commitsPane",
477
+ "funnelBoundBtnActive": "SD8qLW_funnelBoundBtnActive",
478
+ "commitSubject": "SD8qLW_commitSubject",
479
+ "commitPopBody": "SD8qLW_commitPopBody",
484
480
  "wordDel": "SD8qLW_wordDel",
485
- "calMark": "SD8qLW_calMark",
486
- "graphCell": "SD8qLW_graphCell",
487
- "calTitle": "SD8qLW_calTitle",
488
- "sideScroll": "SD8qLW_sideScroll",
489
- "funnelBoundVal": "SD8qLW_funnelBoundVal",
490
- "commitAmend": "SD8qLW_commitAmend",
491
- "fbFileName": "SD8qLW_fbFileName",
492
- "commitLine": "SD8qLW_commitLine",
481
+ "funnelTabActive": "SD8qLW_funnelTabActive",
482
+ "fbDirName": "SD8qLW_fbDirName",
493
483
  "imgPane": "SD8qLW_imgPane",
494
- "calIn": "SD8qLW_calIn",
495
- "diffNav": "SD8qLW_diffNav",
496
- "pathNode": "SD8qLW_pathNode",
497
- "calGrid": "SD8qLW_calGrid",
498
- "commitPopSubject": "SD8qLW_commitPopSubject",
499
- "commitRef": "SD8qLW_commitRef",
500
- "crMark": "SD8qLW_crMark",
501
- "sideColGrid": "SD8qLW_sideColGrid",
502
- "sideBannerTitle": "SD8qLW_sideBannerTitle",
503
- "paneDividerActive": "SD8qLW_paneDividerActive",
504
- "lineAdd": "SD8qLW_lineAdd",
505
- "sideNumAdd": "SD8qLW_sideNumAdd",
506
- "lnOld": "SD8qLW_lnOld",
507
- "commits": "SD8qLW_commits",
508
- "funnelFootClear": "SD8qLW_funnelFootClear",
509
- "themeLabel": "SD8qLW_themeLabel",
484
+ "paneDivider": "SD8qLW_paneDivider",
485
+ "segmented": "SD8qLW_segmented",
486
+ "scopeRow": "SD8qLW_scopeRow",
510
487
  "funnelTabs": "SD8qLW_funnelTabs",
511
- "tree": "SD8qLW_tree",
512
- "menuPop": "SD8qLW_menuPop",
513
- "bgPreview": "SD8qLW_bgPreview",
514
- "filterClear": "SD8qLW_filterClear",
515
- "overlay": "SD8qLW_overlay",
516
- "fbMore": "SD8qLW_fbMore",
517
- "pullGroup": "SD8qLW_pullGroup",
518
- "sideTab": "SD8qLW_sideTab",
519
- "fbCommitLine": "SD8qLW_fbCommitLine",
520
- "elide": "SD8qLW_elide",
521
- "commitPopTop": "SD8qLW_commitPopTop",
522
- "paneHead": "SD8qLW_paneHead",
523
- "commitMeta": "SD8qLW_commitMeta",
524
- "compareBar": "SD8qLW_compareBar",
525
- "headerLeft": "SD8qLW_headerLeft",
526
- "filterChipLabel": "SD8qLW_filterChipLabel",
527
- "funnelTab": "SD8qLW_funnelTab",
528
- "sliderValue": "SD8qLW_sliderValue",
529
- "refRow": "SD8qLW_refRow",
530
- "compareArrow": "SD8qLW_compareArrow",
531
- "line": "SD8qLW_line",
532
- "commitPopBody": "SD8qLW_commitPopBody",
533
- "filterChip": "SD8qLW_filterChip",
534
- "headerBranch": "SD8qLW_headerBranch",
535
- "commitAuthor": "SD8qLW_commitAuthor",
536
- "refFoot": "SD8qLW_refFoot",
537
- "sideNotice": "SD8qLW_sideNotice",
538
- "checkMark": "SD8qLW_checkMark",
539
- "funnelBoundKey": "SD8qLW_funnelBoundKey",
540
- "checkMarkPartial": "SD8qLW_checkMarkPartial",
541
- "commitStaged": "SD8qLW_commitStaged",
542
- "funnelPresetActive": "SD8qLW_funnelPresetActive",
543
- "fbRowActive": "SD8qLW_fbRowActive",
544
- "btnIcon": "SD8qLW_btnIcon",
545
- "btnCount": "SD8qLW_btnCount",
546
- "confirmBody": "SD8qLW_confirmBody",
547
- "treeTools": "SD8qLW_treeTools",
548
- "fbCommitWhat": "SD8qLW_fbCommitWhat",
549
- "treeEmpty": "SD8qLW_treeEmpty",
550
- "tabs": "SD8qLW_tabs",
551
- "commitSubject": "SD8qLW_commitSubject",
552
- "opBanner": "SD8qLW_opBanner",
553
- "fbMain": "SD8qLW_fbMain",
554
- "segment": "SD8qLW_segment",
555
- "refRowName": "SD8qLW_refRowName",
556
- "treeIconGlyph": "SD8qLW_treeIconGlyph",
557
- "fileDiscard": "SD8qLW_fileDiscard",
488
+ "sideActions": "SD8qLW_sideActions",
489
+ "gsSlide": "SD8qLW_gsSlide",
490
+ "commitLine": "SD8qLW_commitLine",
491
+ "lineDel": "SD8qLW_lineDel",
492
+ "funnelBounds": "SD8qLW_funnelBounds",
493
+ "sideNumDel": "SD8qLW_sideNumDel",
558
494
  "fbList": "SD8qLW_fbList",
495
+ "cardBranchName": "SD8qLW_cardBranchName",
496
+ "treeDirCount": "SD8qLW_treeDirCount",
497
+ "sideBlockHotFirst": "SD8qLW_sideBlockHotFirst",
498
+ "treeEmpty": "SD8qLW_treeEmpty",
499
+ "syncBar": "SD8qLW_syncBar",
500
+ "sliderValue": "SD8qLW_sliderValue",
501
+ "headerPicker": "SD8qLW_headerPicker",
559
502
  "headerRight": "SD8qLW_headerRight",
560
- "fileCounts": "SD8qLW_fileCounts",
561
- "layoutSwitch": "SD8qLW_layoutSwitch",
562
- "commitFilter": "SD8qLW_commitFilter",
563
- "treeWrap": "SD8qLW_treeWrap",
564
- "fileActive": "SD8qLW_fileActive",
565
- "funnelBoundBtnActive": "SD8qLW_funnelBoundBtnActive",
566
- "commitWhen": "SD8qLW_commitWhen",
567
- "empty": "SD8qLW_empty",
503
+ "funnelBoundValSet": "SD8qLW_funnelBoundValSet",
504
+ "sideNav": "SD8qLW_sideNav",
505
+ "treeDirActive": "SD8qLW_treeDirActive",
568
506
  "fileLi": "SD8qLW_fileLi",
569
- "calHead": "SD8qLW_calHead",
570
- "gsFade": "SD8qLW_gsFade",
571
- "funnelChevron": "SD8qLW_funnelChevron",
507
+ "treeIconOn": "SD8qLW_treeIconOn",
508
+ "treeLead": "SD8qLW_treeLead",
509
+ "funnelList": "SD8qLW_funnelList",
510
+ "headerDetached": "SD8qLW_headerDetached",
511
+ "fbTree": "SD8qLW_fbTree",
512
+ "pathFileGlyph": "SD8qLW_pathFileGlyph",
513
+ "treeSub": "SD8qLW_treeSub",
514
+ "filterChips": "SD8qLW_filterChips",
572
515
  "diffSpacer": "SD8qLW_diffSpacer",
573
- "fbCommit": "SD8qLW_fbCommit",
574
- "cardBehind": "SD8qLW_cardBehind",
575
- "headerPicker": "SD8qLW_headerPicker",
576
- "paneDividerY": "SD8qLW_paneDividerY",
577
- "cardWt": "SD8qLW_cardWt",
578
- "diffScroll": "SD8qLW_diffScroll",
579
- "refRowSpacer": "SD8qLW_refRowSpacer",
580
- "sideNavCount": "SD8qLW_sideNavCount",
581
- "commitSubjectRow": "SD8qLW_commitSubjectRow",
516
+ "miniBtn": "SD8qLW_miniBtn",
517
+ "btnIcon": "SD8qLW_btnIcon",
518
+ "sideBannerTitle": "SD8qLW_sideBannerTitle",
519
+ "refLabel": "SD8qLW_refLabel",
520
+ "treeDirLi": "SD8qLW_treeDirLi",
521
+ "calNav": "SD8qLW_calNav",
522
+ "commitMessage": "SD8qLW_commitMessage",
523
+ "fbNote": "SD8qLW_fbNote",
524
+ "sideTab": "SD8qLW_sideTab",
525
+ "lineContext": "SD8qLW_lineContext",
526
+ "sidePane": "SD8qLW_sidePane",
582
527
  "chipDark": "SD8qLW_chipDark",
583
- "cardFiles": "SD8qLW_cardFiles",
584
- "paneDivider": "SD8qLW_paneDivider",
585
- "themeRowSplit": "SD8qLW_themeRowSplit",
586
- "pathChildren": "SD8qLW_pathChildren",
587
- "fbCommitHash": "SD8qLW_fbCommitHash",
528
+ "graphCell": "SD8qLW_graphCell",
588
529
  "header": "SD8qLW_header",
589
- "sideColGridBlame": "SD8qLW_sideColGridBlame",
590
- "paletteRowActive": "SD8qLW_paletteRowActive",
591
- "swatch": "SD8qLW_swatch",
592
- "fbHeader": "SD8qLW_fbHeader",
593
- "commitTop": "SD8qLW_commitTop",
594
- "fbDirName": "SD8qLW_fbDirName",
595
- "chipSystem": "SD8qLW_chipSystem",
596
- "sliderRow": "SD8qLW_sliderRow",
597
- "miniBtn": "SD8qLW_miniBtn",
598
- "commitRow": "SD8qLW_commitRow",
599
- "funnelName": "SD8qLW_funnelName",
600
- "fbSearch": "SD8qLW_fbSearch",
601
- "fileBinary": "SD8qLW_fileBinary",
602
- "opBannerBad": "SD8qLW_opBannerBad",
603
- "sideEditCell": "SD8qLW_sideEditCell",
604
- "commitPopMeta": "SD8qLW_commitPopMeta",
605
- "layoutButton": "SD8qLW_layoutButton",
606
- "paletteRow": "SD8qLW_paletteRow",
530
+ "funnelTabCount": "SD8qLW_funnelTabCount",
531
+ "segmentChip": "SD8qLW_segmentChip",
532
+ "filterClear": "SD8qLW_filterClear",
533
+ "commitBox": "SD8qLW_commitBox",
534
+ "commitMeta": "SD8qLW_commitMeta",
535
+ "confirmBody": "SD8qLW_confirmBody",
536
+ "headerTotalsDel": "SD8qLW_headerTotalsDel",
537
+ "pathChildren": "SD8qLW_pathChildren",
538
+ "paneDividerActive": "SD8qLW_paneDividerActive",
539
+ "renameLine": "SD8qLW_renameLine",
607
540
  "calToday": "SD8qLW_calToday",
608
- "funnelPreset": "SD8qLW_funnelPreset",
609
- "blockBtn": "SD8qLW_blockBtn",
610
- "sideCol": "SD8qLW_sideCol",
611
- "btnPrimary": "SD8qLW_btnPrimary",
612
- "treeDir": "SD8qLW_treeDir",
613
- "treeFilterInput": "SD8qLW_treeFilterInput",
614
- "fileCountDel": "SD8qLW_fileCountDel",
615
- "btnBehind": "SD8qLW_btnBehind",
616
- "fbCommitWhen": "SD8qLW_fbCommitWhen",
617
- "funnelPresets": "SD8qLW_funnelPresets",
541
+ "sideRail": "SD8qLW_sideRail",
542
+ "fbRowIgnored": "SD8qLW_fbRowIgnored",
543
+ "resizer": "SD8qLW_resizer",
544
+ "sideNum": "SD8qLW_sideNum",
545
+ "treeIconDown": "SD8qLW_treeIconDown",
546
+ "funnelPresetActive": "SD8qLW_funnelPresetActive",
547
+ "fbMain": "SD8qLW_fbMain",
548
+ "funnelFootCountOn": "SD8qLW_funnelFootCountOn",
549
+ "commitCopy": "SD8qLW_commitCopy",
550
+ "pathDirGlyph": "SD8qLW_pathDirGlyph",
551
+ "calMark": "SD8qLW_calMark",
552
+ "file": "SD8qLW_file",
553
+ "cardGlyph": "SD8qLW_cardGlyph",
554
+ "filterChipLabel": "SD8qLW_filterChipLabel",
555
+ "funnelBoundRow": "SD8qLW_funnelBoundRow",
556
+ "sideColWrap": "SD8qLW_sideColWrap",
557
+ "cardBehind": "SD8qLW_cardBehind",
558
+ "fileCountAdd": "SD8qLW_fileCountAdd",
559
+ "sideBlockHot": "SD8qLW_sideBlockHot",
560
+ "stModified": "SD8qLW_stModified",
561
+ "cardAhead": "SD8qLW_cardAhead",
562
+ "commitPop": "SD8qLW_commitPop",
563
+ "funnelName": "SD8qLW_funnelName",
564
+ "commitWhen": "SD8qLW_commitWhen",
565
+ "funnelBoundBtn": "SD8qLW_funnelBoundBtn",
566
+ "funnelBoundVal": "SD8qLW_funnelBoundVal",
567
+ "refCaret": "SD8qLW_refCaret",
618
568
  "treeFilterClear": "SD8qLW_treeFilterClear",
619
- "refEmpty": "SD8qLW_refEmpty",
569
+ "sideTabs": "SD8qLW_sideTabs",
570
+ "btnIconOn": "SD8qLW_btnIconOn",
571
+ "commitHash": "SD8qLW_commitHash",
572
+ "cardAdded": "SD8qLW_cardAdded",
573
+ "confirmTitle": "SD8qLW_confirmTitle",
620
574
  "body": "SD8qLW_body",
621
- "miniBtnPrimary": "SD8qLW_miniBtnPrimary",
622
- "commit": "SD8qLW_commit",
623
- "blockBtnDanger": "SD8qLW_blockBtnDanger",
624
- "layoutGlyph": "SD8qLW_layoutGlyph",
575
+ "funnelPresets": "SD8qLW_funnelPresets",
576
+ "calTitle": "SD8qLW_calTitle",
577
+ "funnelCaption": "SD8qLW_funnelCaption",
578
+ "funnelRow": "SD8qLW_funnelRow",
579
+ "scopeBtnActive": "SD8qLW_scopeBtnActive",
580
+ "bgPreview": "SD8qLW_bgPreview",
581
+ "refButton": "SD8qLW_refButton",
582
+ "fileActive": "SD8qLW_fileActive",
583
+ "treeDirCounts": "SD8qLW_treeDirCounts",
584
+ "fileCounts": "SD8qLW_fileCounts",
625
585
  "refGroup": "SD8qLW_refGroup",
626
- "commitBtn": "SD8qLW_commitBtn",
627
- "syncBar": "SD8qLW_syncBar",
628
- "lineContext": "SD8qLW_lineContext",
629
- "commitsFoot": "SD8qLW_commitsFoot",
630
- "signAdd": "SD8qLW_signAdd",
631
- "cardDetached": "SD8qLW_cardDetached",
632
- "treeIconOn": "SD8qLW_treeIconOn",
633
- "confirmScrim": "SD8qLW_confirmScrim",
634
- "sideSaveReady": "SD8qLW_sideSaveReady",
635
- "headerTotalsAdd": "SD8qLW_headerTotalsAdd",
636
- "funnelFoot": "SD8qLW_funnelFoot",
637
- "wordAdd": "SD8qLW_wordAdd",
638
- "confirmBox": "SD8qLW_confirmBox",
639
- "layoutButtonOn": "SD8qLW_layoutButtonOn",
640
- "cssArea": "SD8qLW_cssArea",
641
- "tab": "SD8qLW_tab",
586
+ "overlay": "SD8qLW_overlay",
642
587
  "stDeleted": "SD8qLW_stDeleted",
643
- "commitMessage": "SD8qLW_commitMessage",
588
+ "line": "SD8qLW_line",
589
+ "sideNumAdd": "SD8qLW_sideNumAdd",
590
+ "bodyRow": "SD8qLW_bodyRow",
591
+ "sideColGridWrap": "SD8qLW_sideColGridWrap",
644
592
  "funnelPop": "SD8qLW_funnelPop",
645
- "sideNav": "SD8qLW_sideNav",
646
- "refPicker": "SD8qLW_refPicker",
647
- "headerTotals": "SD8qLW_headerTotals",
648
- "themeRail": "SD8qLW_themeRail",
649
- "renameLine": "SD8qLW_renameLine",
650
- "commitsPane": "SD8qLW_commitsPane",
651
- "headerTotalsDim": "SD8qLW_headerTotalsDim",
652
- "sideNum": "SD8qLW_sideNum",
653
- "fileCountAdd": "SD8qLW_fileCountAdd",
654
- "sideBlockHotLast": "SD8qLW_sideBlockHotLast",
655
- "fbBody": "SD8qLW_fbBody",
656
- "imgShot": "SD8qLW_imgShot",
657
- "funnelBounds": "SD8qLW_funnelBounds",
658
- "confirmActions": "SD8qLW_confirmActions",
659
- "cmHost": "SD8qLW_cmHost",
660
- "funnelTabActive": "SD8qLW_funnelTabActive",
661
- "tabActive": "SD8qLW_tabActive",
662
- "segmentChip": "SD8qLW_segmentChip",
663
- "wtCurrent": "SD8qLW_wtCurrent",
664
- "funnelSearch": "SD8qLW_funnelSearch",
665
- "funnelBoundRow": "SD8qLW_funnelBoundRow",
666
- "funnelFootCount": "SD8qLW_funnelFootCount",
667
- "syncSpacer": "SD8qLW_syncSpacer",
668
- "funnelRow": "SD8qLW_funnelRow",
669
- "chevronOpen": "SD8qLW_chevronOpen",
670
- "funnelBoundBtn": "SD8qLW_funnelBoundBtn",
671
- "treeDirName": "SD8qLW_treeDirName",
672
- "lnNew": "SD8qLW_lnNew",
673
- "signDel": "SD8qLW_signDel",
674
- "commitsSentinel": "SD8qLW_commitsSentinel",
675
593
  "sideCodeSame": "SD8qLW_sideCodeSame",
676
- "sideBlockHotFirst": "SD8qLW_sideBlockHotFirst",
677
594
  "sideActionsAdjacent": "SD8qLW_sideActionsAdjacent",
678
- "funnelBoundValSet": "SD8qLW_funnelBoundValSet",
679
- "headerViewRef": "SD8qLW_headerViewRef",
680
- "commitHasBody": "SD8qLW_commitHasBody",
681
- "gutter": "SD8qLW_gutter",
682
- "checkBox": "SD8qLW_checkBox",
683
- "elideHead": "SD8qLW_elideHead",
684
- "btnClose": "SD8qLW_btnClose",
685
- "btnAhead": "SD8qLW_btnAhead",
686
- "funnelFootCountOn": "SD8qLW_funnelFootCountOn",
687
- "funnelButtonActive": "SD8qLW_funnelButtonActive",
688
- "refLabel": "SD8qLW_refLabel",
689
- "treeDirCounts": "SD8qLW_treeDirCounts",
690
- "headerTotalsDel": "SD8qLW_headerTotalsDel",
691
- "scopeHint": "SD8qLW_scopeHint",
692
- "stUntracked": "SD8qLW_stUntracked",
693
- "confirmTitle": "SD8qLW_confirmTitle",
694
- "fbCommitHint": "SD8qLW_fbCommitHint",
695
- "imgCaption": "SD8qLW_imgCaption",
595
+ "headerTotalsAdd": "SD8qLW_headerTotalsAdd",
596
+ "filePath": "SD8qLW_filePath",
597
+ "tree": "SD8qLW_tree",
598
+ "sideRailSpan": "SD8qLW_sideRailSpan",
599
+ "checkMark": "SD8qLW_checkMark",
600
+ "commitLead": "SD8qLW_commitLead",
601
+ "fbRowActive": "SD8qLW_fbRowActive",
602
+ "refPicker": "SD8qLW_refPicker",
696
603
  "cardDeleted": "SD8qLW_cardDeleted",
697
- "treeCol": "SD8qLW_treeCol",
698
- "lineDel": "SD8qLW_lineDel",
699
- "sideArmable": "SD8qLW_sideArmable",
700
- "scopeBtn": "SD8qLW_scopeBtn",
701
- "imgStage": "SD8qLW_imgStage",
702
- "drawer": "SD8qLW_drawer",
703
- "card": "SD8qLW_card",
604
+ "cmHost": "SD8qLW_cmHost",
605
+ "blockBtnDanger": "SD8qLW_blockBtnDanger",
606
+ "confirmActions": "SD8qLW_confirmActions",
704
607
  "refSearch": "SD8qLW_refSearch",
705
- "treeLead": "SD8qLW_treeLead",
706
- "sideBanner": "SD8qLW_sideBanner",
707
- "filePath": "SD8qLW_filePath",
708
- "bodyRow": "SD8qLW_bodyRow",
709
- "diffWrap": "SD8qLW_diffWrap",
710
- "headerDetached": "SD8qLW_headerDetached",
711
- "stModified": "SD8qLW_stModified",
712
- "treeDirActive": "SD8qLW_treeDirActive",
713
- "themeDirty": "SD8qLW_themeDirty",
714
- "diffPane": "SD8qLW_diffPane",
715
- "filterChips": "SD8qLW_filterChips",
716
- "funnelPane": "SD8qLW_funnelPane",
717
- "fileStatus": "SD8qLW_fileStatus",
718
- "btnDanger": "SD8qLW_btnDanger",
608
+ "treeCol": "SD8qLW_treeCol",
609
+ "layoutGlyph": "SD8qLW_layoutGlyph",
610
+ "paneDividerY": "SD8qLW_paneDividerY",
611
+ "syncSpacer": "SD8qLW_syncSpacer",
719
612
  "treeIcon": "SD8qLW_treeIcon",
720
- "funnelBoundRows": "SD8qLW_funnelBoundRows",
721
- "scopeRow": "SD8qLW_scopeRow",
722
- "chipLight": "SD8qLW_chipLight",
723
- "stAdded": "SD8qLW_stAdded",
724
- "segmented": "SD8qLW_segmented",
725
- "sidePane": "SD8qLW_sidePane",
726
- "blameCell": "SD8qLW_blameCell",
727
- "sideActions": "SD8qLW_sideActions",
728
- "sideBannerActs": "SD8qLW_sideBannerActs",
729
- "themeGroup": "SD8qLW_themeGroup",
613
+ "commitPopSubject": "SD8qLW_commitPopSubject",
614
+ "empty": "SD8qLW_empty",
615
+ "fbPath": "SD8qLW_fbPath",
616
+ "cardWt": "SD8qLW_cardWt",
617
+ "sideArmable": "SD8qLW_sideArmable",
618
+ "commitSubjectRow": "SD8qLW_commitSubjectRow",
619
+ "checkBox": "SD8qLW_checkBox",
620
+ "opBanner": "SD8qLW_opBanner",
621
+ "btnBehind": "SD8qLW_btnBehind",
622
+ "scopeHint": "SD8qLW_scopeHint",
623
+ "calGrid": "SD8qLW_calGrid",
624
+ "headerPathMain": "SD8qLW_headerPathMain",
625
+ "compareBar": "SD8qLW_compareBar",
730
626
  "elideTail": "SD8qLW_elideTail",
731
- "funnelMore": "SD8qLW_funnelMore",
732
- "headerView": "SD8qLW_headerView",
627
+ "refFoot": "SD8qLW_refFoot",
628
+ "commitRef": "SD8qLW_commitRef",
629
+ "stUntracked": "SD8qLW_stUntracked",
630
+ "fileBinary": "SD8qLW_fileBinary",
631
+ "refEmpty": "SD8qLW_refEmpty",
632
+ "gutter": "SD8qLW_gutter",
633
+ "funnelPreset": "SD8qLW_funnelPreset",
733
634
  "sideTabActive": "SD8qLW_sideTabActive",
734
- "commitBox": "SD8qLW_commitBox",
735
- "commitLead": "SD8qLW_commitLead",
736
- "scopeBtnActive": "SD8qLW_scopeBtnActive",
635
+ "stRenamed": "SD8qLW_stRenamed",
636
+ "sideCode": "SD8qLW_sideCode",
637
+ "blockBar": "SD8qLW_blockBar",
638
+ "syncUpstream": "SD8qLW_syncUpstream",
639
+ "tabActive": "SD8qLW_tabActive",
640
+ "funnelBoundKey": "SD8qLW_funnelBoundKey",
641
+ "commitAmend": "SD8qLW_commitAmend",
642
+ "refList": "SD8qLW_refList",
643
+ "btnClose": "SD8qLW_btnClose",
737
644
  "commitRefMore": "SD8qLW_commitRefMore",
738
- "fbTree": "SD8qLW_fbTree",
739
- "calNav": "SD8qLW_calNav",
740
- "refValue": "SD8qLW_refValue",
741
- "commitActive": "SD8qLW_commitActive",
742
- "segmentActive": "SD8qLW_segmentActive",
645
+ "fbCommitLine": "SD8qLW_fbCommitLine",
646
+ "cardFiles": "SD8qLW_cardFiles",
647
+ "sideSaveReady": "SD8qLW_sideSaveReady",
648
+ "tab": "SD8qLW_tab",
649
+ "commitAuthor": "SD8qLW_commitAuthor",
650
+ "treeWrap": "SD8qLW_treeWrap",
743
651
  "code": "SD8qLW_code",
744
- "lineHunk": "SD8qLW_lineHunk",
745
- "treeFilter": "SD8qLW_treeFilter",
746
- "resizerActive": "SD8qLW_resizerActive",
747
- "blockBar": "SD8qLW_blockBar",
748
- "refCaret": "SD8qLW_refCaret",
749
- "syncLevel": "SD8qLW_syncLevel",
750
- "refButton": "SD8qLW_refButton",
751
- "bgEmpty": "SD8qLW_bgEmpty",
752
- "sideCols": "SD8qLW_sideCols",
753
- "refPop": "SD8qLW_refPop",
652
+ "sideFlow": "SD8qLW_sideFlow",
653
+ "imgCaption": "SD8qLW_imgCaption",
654
+ "btnPrimary": "SD8qLW_btnPrimary",
655
+ "commitsSentinel": "SD8qLW_commitsSentinel",
656
+ "sideRailRight": "SD8qLW_sideRailRight",
657
+ "btnDanger": "SD8qLW_btnDanger",
754
658
  "themeNote": "SD8qLW_themeNote",
755
659
  "treeActions": "SD8qLW_treeActions",
756
- "gsSlide": "SD8qLW_gsSlide",
757
- "stRenamed": "SD8qLW_stRenamed",
758
- "resizer": "SD8qLW_resizer",
759
- "fbNote": "SD8qLW_fbNote",
760
- "cardSep": "SD8qLW_cardSep",
761
- "checkMarkOn": "SD8qLW_checkMarkOn",
762
- "commitPop": "SD8qLW_commitPop",
763
- "sideCodeAdd": "SD8qLW_sideCodeAdd",
764
- "refRowActive": "SD8qLW_refRowActive",
765
- "calOut": "SD8qLW_calOut",
766
- "sideCodeDel": "SD8qLW_sideCodeDel",
767
- "sideNumDel": "SD8qLW_sideNumDel",
768
- "treeDirLi": "SD8qLW_treeDirLi",
769
- "overlayMax": "SD8qLW_overlayMax",
770
- "treeLabel": "SD8qLW_treeLabel",
771
- "diffPre": "SD8qLW_diffPre",
772
- "cardAhead": "SD8qLW_cardAhead",
773
- "sideSpacer": "SD8qLW_sideSpacer",
774
- "btn": "SD8qLW_btn",
775
- "opBannerOk": "SD8qLW_opBannerOk",
776
- "commitCopy": "SD8qLW_commitCopy",
777
- "sideCurrentBlockActions": "SD8qLW_sideCurrentBlockActions",
778
- "cardGlyph": "SD8qLW_cardGlyph",
779
- "sideBlockHot": "SD8qLW_sideBlockHot",
780
- "theme": "SD8qLW_theme",
781
- "pathDirGlyph": "SD8qLW_pathDirGlyph",
782
- "cardBranchName": "SD8qLW_cardBranchName",
783
660
  "cardBranch": "SD8qLW_cardBranch",
784
- "refList": "SD8qLW_refList",
785
- "sideTabs": "SD8qLW_sideTabs",
786
- "calWeek": "SD8qLW_calWeek",
787
- "settingsPop": "SD8qLW_settingsPop",
661
+ "imgStage": "SD8qLW_imgStage",
788
662
  "chevron": "SD8qLW_chevron",
789
- "treeDirCount": "SD8qLW_treeDirCount",
790
- "syncUpstream": "SD8qLW_syncUpstream",
791
- "funnelList": "SD8qLW_funnelList",
663
+ "gsFade": "SD8qLW_gsFade",
664
+ "overlayMax": "SD8qLW_overlayMax",
665
+ "resizerActive": "SD8qLW_resizerActive",
666
+ "refRow": "SD8qLW_refRow",
667
+ "commitHasBody": "SD8qLW_commitHasBody",
668
+ "refRowName": "SD8qLW_refRowName",
669
+ "sideRailGap": "SD8qLW_sideRailGap",
670
+ "fbBody": "SD8qLW_fbBody",
671
+ "btn": "SD8qLW_btn",
672
+ "tabs": "SD8qLW_tabs",
673
+ "calHead": "SD8qLW_calHead",
674
+ "funnelCount": "SD8qLW_funnelCount",
675
+ "signDel": "SD8qLW_signDel",
676
+ "sideColGrid": "SD8qLW_sideColGrid",
677
+ "commitStaged": "SD8qLW_commitStaged",
678
+ "layoutButton": "SD8qLW_layoutButton",
679
+ "fbRow": "SD8qLW_fbRow",
680
+ "confirmScrim": "SD8qLW_confirmScrim",
681
+ "confirmBox": "SD8qLW_confirmBox",
792
682
  "filterChipRemove": "SD8qLW_filterChipRemove",
683
+ "layoutButtonOn": "SD8qLW_layoutButtonOn",
684
+ "treeDirName": "SD8qLW_treeDirName",
793
685
  "treeRow": "SD8qLW_treeRow",
794
- "fbRow": "SD8qLW_fbRow",
795
- "treeSub": "SD8qLW_treeSub",
796
- "funnelTabCount": "SD8qLW_funnelTabCount",
797
- "sideCode": "SD8qLW_sideCode",
686
+ "sliderRow": "SD8qLW_sliderRow",
687
+ "calIn": "SD8qLW_calIn",
688
+ "headerTotals": "SD8qLW_headerTotals",
689
+ "funnelBoundRows": "SD8qLW_funnelBoundRows",
690
+ "sideSpacer": "SD8qLW_sideSpacer",
691
+ "sideNavCount": "SD8qLW_sideNavCount",
692
+ "sideRailsOn": "SD8qLW_sideRailsOn",
693
+ "checkMarkOn": "SD8qLW_checkMarkOn",
694
+ "scopeBtn": "SD8qLW_scopeBtn",
695
+ "fbCommitHint": "SD8qLW_fbCommitHint",
696
+ "fileStatus": "SD8qLW_fileStatus",
697
+ "fileCountDel": "SD8qLW_fileCountDel",
698
+ "fbCommitWhen": "SD8qLW_fbCommitWhen",
699
+ "elideHead": "SD8qLW_elideHead",
700
+ "paneHead": "SD8qLW_paneHead",
701
+ "blameCell": "SD8qLW_blameCell",
702
+ "calWeek": "SD8qLW_calWeek",
703
+ "fbMore": "SD8qLW_fbMore",
704
+ "calOut": "SD8qLW_calOut",
705
+ "btnAhead": "SD8qLW_btnAhead",
706
+ "cssArea": "SD8qLW_cssArea",
707
+ "opBannerBad": "SD8qLW_opBannerBad",
708
+ "refValue": "SD8qLW_refValue",
709
+ "treeDir": "SD8qLW_treeDir",
710
+ "themeRowSplit": "SD8qLW_themeRowSplit",
711
+ "diffPane": "SD8qLW_diffPane",
712
+ "opBannerOk": "SD8qLW_opBannerOk",
713
+ "lnNew": "SD8qLW_lnNew",
714
+ "headerBranch": "SD8qLW_headerBranch",
715
+ "treeTools": "SD8qLW_treeTools",
716
+ "diffScroll": "SD8qLW_diffScroll",
717
+ "sideRails": "SD8qLW_sideRails",
718
+ "pathNode": "SD8qLW_pathNode",
719
+ "treeIconGlyph": "SD8qLW_treeIconGlyph",
720
+ "diffPre": "SD8qLW_diffPre",
721
+ "bgEmpty": "SD8qLW_bgEmpty",
722
+ "chevronOpen": "SD8qLW_chevronOpen",
723
+ "miniBtnPrimary": "SD8qLW_miniBtnPrimary",
724
+ "chipLight": "SD8qLW_chipLight",
725
+ "funnelFoot": "SD8qLW_funnelFoot",
726
+ "funnelPane": "SD8qLW_funnelPane",
727
+ "blockBtn": "SD8qLW_blockBtn",
728
+ "diffPreWrap": "SD8qLW_diffPreWrap",
729
+ "commits": "SD8qLW_commits",
730
+ "stAdded": "SD8qLW_stAdded",
731
+ "lineAdd": "SD8qLW_lineAdd",
732
+ "sideColRight": "SD8qLW_sideColRight",
733
+ "commitBtn": "SD8qLW_commitBtn",
734
+ "fbCommit": "SD8qLW_fbCommit",
735
+ "treeFilter": "SD8qLW_treeFilter",
736
+ "settingsPop": "SD8qLW_settingsPop",
737
+ "funnel": "SD8qLW_funnel",
738
+ "funnelTab": "SD8qLW_funnelTab",
739
+ "chipSystem": "SD8qLW_chipSystem",
740
+ "commitActive": "SD8qLW_commitActive",
741
+ "fileDiscard": "SD8qLW_fileDiscard",
742
+ "sideColGridBlame": "SD8qLW_sideColGridBlame",
743
+ "commitPopMeta": "SD8qLW_commitPopMeta",
744
+ "fbCommitWho": "SD8qLW_fbCommitWho",
745
+ "funnelFootClear": "SD8qLW_funnelFootClear",
746
+ "diffWrap": "SD8qLW_diffWrap",
747
+ "headerView": "SD8qLW_headerView",
748
+ "refPop": "SD8qLW_refPop",
749
+ "funnelChevron": "SD8qLW_funnelChevron",
750
+ "cardDetached": "SD8qLW_cardDetached",
751
+ "menuPop": "SD8qLW_menuPop",
752
+ "sideBanner": "SD8qLW_sideBanner",
753
+ "themeGroup": "SD8qLW_themeGroup",
754
+ "funnelButton": "SD8qLW_funnelButton",
755
+ "fbSearch": "SD8qLW_fbSearch",
756
+ "sideEditCell": "SD8qLW_sideEditCell",
757
+ "cardSep": "SD8qLW_cardSep",
758
+ "funnelButtonActive": "SD8qLW_funnelButtonActive",
759
+ "funnelFootCount": "SD8qLW_funnelFootCount",
760
+ "card": "SD8qLW_card",
761
+ "refRowSpacer": "SD8qLW_refRowSpacer",
762
+ "fbHeader": "SD8qLW_fbHeader",
763
+ "themeLabel": "SD8qLW_themeLabel",
764
+ "commitTop": "SD8qLW_commitTop",
765
+ "btnCount": "SD8qLW_btnCount",
766
+ "checkMarkPartial": "SD8qLW_checkMarkPartial",
767
+ "sideCodeDel": "SD8qLW_sideCodeDel",
768
+ "sideCols": "SD8qLW_sideCols",
769
+ "paletteRowActive": "SD8qLW_paletteRowActive",
770
+ "themeRail": "SD8qLW_themeRail",
771
+ "commitsFoot": "SD8qLW_commitsFoot",
772
+ "fbCommitHash": "SD8qLW_fbCommitHash",
773
+ "sideBannerActs": "SD8qLW_sideBannerActs",
774
+ "sideCol": "SD8qLW_sideCol",
775
+ "themeDirty": "SD8qLW_themeDirty",
776
+ "diffNav": "SD8qLW_diffNav",
777
+ "signAdd": "SD8qLW_signAdd",
778
+ "elide": "SD8qLW_elide",
779
+ "funnelSearch": "SD8qLW_funnelSearch",
780
+ "syncLevel": "SD8qLW_syncLevel",
781
+ "sideScroll": "SD8qLW_sideScroll",
782
+ "wtCurrent": "SD8qLW_wtCurrent",
798
783
  "sideCellBlock": "SD8qLW_sideCellBlock",
799
- "cardAdded": "SD8qLW_cardAdded",
800
- "pathFileGlyph": "SD8qLW_pathFileGlyph",
801
- "file": "SD8qLW_file",
802
- "commitHash": "SD8qLW_commitHash"
784
+ "layoutSwitch": "SD8qLW_layoutSwitch",
785
+ "refRowActive": "SD8qLW_refRowActive",
786
+ "lnOld": "SD8qLW_lnOld",
787
+ "drawer": "SD8qLW_drawer",
788
+ "filterChip": "SD8qLW_filterChip",
789
+ "pullGroup": "SD8qLW_pullGroup",
790
+ "funnelBoundClear": "SD8qLW_funnelBoundClear",
791
+ "cal": "SD8qLW_cal",
792
+ "wordAdd": "SD8qLW_wordAdd",
793
+ "crMark": "SD8qLW_crMark",
794
+ "compareArrow": "SD8qLW_compareArrow",
795
+ "fbFileName": "SD8qLW_fbFileName",
796
+ "imgShot": "SD8qLW_imgShot",
797
+ "headerTotalsDim": "SD8qLW_headerTotalsDim",
798
+ "treeFilterInput": "SD8qLW_treeFilterInput",
799
+ "segmentActive": "SD8qLW_segmentActive",
800
+ "paneTitle": "SD8qLW_paneTitle",
801
+ "headerViewRef": "SD8qLW_headerViewRef",
802
+ "headerLeft": "SD8qLW_headerLeft",
803
+ "commitRow": "SD8qLW_commitRow",
804
+ "sideCurrentBlockActions": "SD8qLW_sideCurrentBlockActions",
805
+ "sideCodeAdd": "SD8qLW_sideCodeAdd",
806
+ "fbCommitWhat": "SD8qLW_fbCommitWhat",
807
+ "funnelMore": "SD8qLW_funnelMore",
808
+ "treeLabel": "SD8qLW_treeLabel",
809
+ "commitPopTop": "SD8qLW_commitPopTop",
810
+ "segment": "SD8qLW_segment",
811
+ "commitFilter": "SD8qLW_commitFilter",
812
+ "commit": "SD8qLW_commit",
813
+ "swatch": "SD8qLW_swatch",
814
+ "sideNotice": "SD8qLW_sideNotice"
803
815
  };
804
816
  //#endregion
805
817
  //#region node_modules/.pnpm/@marijn+find-cluster-break@1.0.3/node_modules/@marijn/find-cluster-break/src/index.js
@@ -19807,6 +19819,9 @@ window.__ModuleLoader__.load({
19807
19819
  * {@link paintCompartment} — when the file, the language or the theme moves. */
19808
19820
  const paintFacet = Facet.define({ combine: (values) => values.length > 0 ? values[0] : null });
19809
19821
  const paintCompartment = new Compartment();
19822
+ /** Soft wrap, in and out without rebuilding the view — the caret, the undo
19823
+ * stack and the selection all survive the toggle. */
19824
+ const wrapCompartment = new Compartment();
19810
19825
  /**
19811
19826
  * How long after the last keystroke the editor recomputes what it paints.
19812
19827
  *
@@ -20096,7 +20111,7 @@ window.__ModuleLoader__.load({
20096
20111
  },
20097
20112
  ...SEARCH_PANEL_THEME
20098
20113
  });
20099
- function CodeEditor({ value, original, onChange, paint, indent, ariaLabel, onSave, blame, notCommitted, readOnly, onBlameClick }) {
20114
+ function CodeEditor({ value, original, onChange, paint, indent, ariaLabel, onSave, blame, notCommitted, readOnly, onBlameClick, wrap }) {
20100
20115
  /**
20101
20116
  * Editable, and SAID to be editable, from one boolean.
20102
20117
  *
@@ -20137,6 +20152,7 @@ window.__ModuleLoader__.load({
20137
20152
  searchCount,
20138
20153
  highlightActiveLine(),
20139
20154
  paintCompartment.of(paintFacet.of(paint)),
20155
+ wrapCompartment.of(wrap === true ? EditorView.lineWrapping : []),
20140
20156
  painter,
20141
20157
  blameField,
20142
20158
  blameCompartment.of([]),
@@ -20211,6 +20227,11 @@ window.__ModuleLoader__.load({
20211
20227
  if (current === null) return;
20212
20228
  current.dispatch({ effects: paintCompartment.reconfigure(paintFacet.of(paint)) });
20213
20229
  }, [paint]);
20230
+ (0, react.useEffect)(() => {
20231
+ const current = view.current;
20232
+ if (current === null) return;
20233
+ current.dispatch({ effects: wrapCompartment.reconfigure(wrap === true ? EditorView.lineWrapping : []) });
20234
+ }, [wrap]);
20214
20235
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
20215
20236
  ref: host,
20216
20237
  className: GitWorkbenchPanel_module_css_default.cmHost,
@@ -20223,10 +20244,20 @@ window.__ModuleLoader__.load({
20223
20244
  * Fold a flat path list into a sorted directory tree carrying its files.
20224
20245
  * Root-level files live on no directory; the SEARCH ({@link searchPaths}) is
20225
20246
  * where they surface.
20247
+ *
20248
+ * `dirHints` names directories that exist with NO file under them yet — the
20249
+ * collapsed entries of the ignored listing (`node_modules/`), whose children
20250
+ * are only read when the reader expands them. A path list cannot express
20251
+ * that: a node with no files and no children is indistinguishable from a
20252
+ * path that was never mentioned. Hints only ever ADD an empty directory; a
20253
+ * hint that lands on a name the path list recorded as a file is skipped,
20254
+ * because the list is the primary source and a hint is hearsay next to it.
20255
+ *
20226
20256
  * @param paths - repo-relative file paths, any order, no duplicates assumed.
20257
+ * @param dirHints - repo-relative directory paths to show even while empty.
20227
20258
  * @returns the top-level directories, children and files sorted by name.
20228
20259
  */
20229
- function buildDirTree(paths) {
20260
+ function buildDirTree(paths, dirHints) {
20230
20261
  const rootNode = {
20231
20262
  name: "",
20232
20263
  path: "",
@@ -20253,6 +20284,37 @@ window.__ModuleLoader__.load({
20253
20284
  }
20254
20285
  node.files.push(parts[parts.length - 1]);
20255
20286
  }
20287
+ const addHint = (hint) => {
20288
+ if (hint.length === 0) return;
20289
+ const parts = hint.split("/");
20290
+ let node = rootNode;
20291
+ for (let i = 0; i < parts.length - 1; i += 1) {
20292
+ const name = parts[i];
20293
+ const child = node.children.get(name);
20294
+ if (child !== void 0) {
20295
+ node = child;
20296
+ continue;
20297
+ }
20298
+ if (node.files.includes(name)) return;
20299
+ const created = {
20300
+ name,
20301
+ path: parts.slice(0, i + 1).join("/"),
20302
+ files: [],
20303
+ children: /* @__PURE__ */ new Map()
20304
+ };
20305
+ node.children.set(name, created);
20306
+ node = created;
20307
+ }
20308
+ const last = parts[parts.length - 1];
20309
+ if (node.children.has(last) || node.files.includes(last)) return;
20310
+ node.children.set(last, {
20311
+ name: last,
20312
+ path: hint,
20313
+ files: [],
20314
+ children: /* @__PURE__ */ new Map()
20315
+ });
20316
+ };
20317
+ if (dirHints !== void 0) for (const hint of dirHints) addHint(hint);
20256
20318
  const freeze = (node) => {
20257
20319
  const children = [...node.children.values()].sort((a, b) => a.name.localeCompare(b.name)).map(freeze);
20258
20320
  const files = [...node.files].sort((a, b) => a.localeCompare(b));
@@ -20423,6 +20485,41 @@ window.__ModuleLoader__.load({
20423
20485
  }
20424
20486
  return out;
20425
20487
  }
20488
+ /** What `git ls-files --others --ignored --exclude-standard --directory`
20489
+ * returned, split by kind: every ignored FILE (any depth, as long as no rule
20490
+ * swallowed its whole directory) and every directory git collapsed to one
20491
+ * line because it is ignored as a whole — the trailing slash is the only
20492
+ * thing that tells them apart, so it is the whole test.
20493
+ * @param entries - the listing verbatim, as `repoTree` carried it.
20494
+ */
20495
+ function splitIgnored(entries) {
20496
+ const files = [];
20497
+ const dirs = [];
20498
+ for (const entry of entries) {
20499
+ if (entry.length === 0) continue;
20500
+ if (entry.endsWith("/")) dirs.push(entry.slice(0, -1));
20501
+ else files.push(entry);
20502
+ }
20503
+ return {
20504
+ files,
20505
+ dirs
20506
+ };
20507
+ }
20508
+ /**
20509
+ * Whether a row is gitignored territory: an ignored file itself, a directory
20510
+ * the listing collapsed, or anything living under one. Everything under a
20511
+ * collapsed directory is ignored by inheritance, which is what makes the
20512
+ * ancestor walk the whole rule.
20513
+ *
20514
+ * @param path - the row's repo-relative path.
20515
+ * @param files - ignored file paths, from {@link splitIgnored}.
20516
+ * @param dirs - collapsed directory paths, from {@link splitIgnored}.
20517
+ */
20518
+ function isIgnoredPath(path, files, dirs) {
20519
+ if (files.has(path) || dirs.has(path)) return true;
20520
+ for (const dir of ancestorsOf(path)) if (dirs.has(dir)) return true;
20521
+ return false;
20522
+ }
20426
20523
  /**
20427
20524
  * The browsable path list: everything git tracks, plus files that exist on
20428
20525
  * disk but not in HEAD.
@@ -20442,6 +20539,155 @@ window.__ModuleLoader__.load({
20442
20539
  return [...all].sort((a, b) => a.localeCompare(b));
20443
20540
  }
20444
20541
  //#endregion
20542
+ //#region src/client/ignored-cache.ts
20543
+ /** Nothing read yet. One instance, so an untouched worktree does not
20544
+ * re-render the browser on every pass. */
20545
+ const NO_IGNORED_READS = {
20546
+ reads: {},
20547
+ order: [],
20548
+ size: 0
20549
+ };
20550
+ /**
20551
+ * Most entries held across all directories at once. A real level is a few
20552
+ * hundred, so reaching this takes dozens of expansions of pathological
20553
+ * directories; it is the fuse, not a working number.
20554
+ */
20555
+ const IGNORED_CACHE_CAP = 2e4;
20556
+ /**
20557
+ * Record one directory's children, evicting the oldest reads that nobody is
20558
+ * looking at until the cache is back under the cap.
20559
+ *
20560
+ * @param cache - the cache as it stands.
20561
+ * @param dir - repo-relative directory the read belongs to.
20562
+ * @param read - what the host answered.
20563
+ * @param keep - directories that must survive eviction: the ones expanded on
20564
+ * screen, and the ancestors of the open file (whose path must
20565
+ * stay in the browsable list or the editor would report it
20566
+ * vanished). The directory being written is always kept.
20567
+ * @param cap - most entries to hold in total.
20568
+ */
20569
+ function rememberDir(cache, dir, read, keep, cap = IGNORED_CACHE_CAP) {
20570
+ const reads = {
20571
+ ...cache.reads,
20572
+ [dir]: read
20573
+ };
20574
+ const order = [...cache.order.filter((key) => key !== dir), dir];
20575
+ let size = cache.size - (cache.reads[dir]?.entries.length ?? 0) + read.entries.length;
20576
+ let index = 0;
20577
+ while (size > cap && index < order.length) {
20578
+ const victim = order[index];
20579
+ if (victim === dir || keep.has(victim)) {
20580
+ index += 1;
20581
+ continue;
20582
+ }
20583
+ size -= reads[victim]?.entries.length ?? 0;
20584
+ delete reads[victim];
20585
+ order.splice(index, 1);
20586
+ }
20587
+ return {
20588
+ reads,
20589
+ order,
20590
+ size
20591
+ };
20592
+ }
20593
+ /** Whether any directory still held was cut at the host's per-directory cap.
20594
+ * Derived rather than sticky: a cut directory that has been evicted, or a
20595
+ * refresh that replaced the cache, stops claiming it. */
20596
+ function anyDirTruncated(cache) {
20597
+ for (const read of Object.values(cache.reads)) if (read.truncated) return true;
20598
+ return false;
20599
+ }
20600
+ /** Files plus subtree counts — the invariant every {@link DirEntry} carries. */
20601
+ function countOf(files, children) {
20602
+ return files.length + children.reduce((sum, child) => sum + child.fileCount, 0);
20603
+ }
20604
+ /** Merge one directory's read into the node that stands for it. Names the
20605
+ * node already has win: the path list is the primary source, and a read that
20606
+ * disagrees with it must not split one name into two rows. */
20607
+ function withChildren(entry, read) {
20608
+ const haveDirs = new Set(entry.children.map((child) => child.name));
20609
+ const haveFiles = new Set(entry.files);
20610
+ const files = [...entry.files];
20611
+ const children = [...entry.children];
20612
+ for (const child of read.entries) {
20613
+ if (haveDirs.has(child.name) || haveFiles.has(child.name)) continue;
20614
+ if (child.dir) {
20615
+ haveDirs.add(child.name);
20616
+ children.push({
20617
+ name: child.name,
20618
+ path: `${entry.path}/${child.name}`,
20619
+ fileCount: 0,
20620
+ files: [],
20621
+ children: []
20622
+ });
20623
+ } else {
20624
+ haveFiles.add(child.name);
20625
+ files.push(child.name);
20626
+ }
20627
+ }
20628
+ files.sort((a, b) => a.localeCompare(b));
20629
+ children.sort((a, b) => a.name.localeCompare(b.name));
20630
+ return {
20631
+ ...entry,
20632
+ files,
20633
+ children,
20634
+ fileCount: countOf(files, children)
20635
+ };
20636
+ }
20637
+ /** Rebuild only the spine down to `parts`, leaving every other node — and
20638
+ * every other subtree's identity — exactly as it was. Null when the path is
20639
+ * not in the tree, which is how a read for a directory a refresh dropped
20640
+ * becomes a no-op instead of an error. */
20641
+ function replaceAt(dirs, parts, depth, apply) {
20642
+ const name = parts[depth];
20643
+ const index = dirs.findIndex((dir) => dir.name === name);
20644
+ if (index === -1) return null;
20645
+ const current = dirs[index];
20646
+ let next;
20647
+ if (depth === parts.length - 1) next = apply(current);
20648
+ else {
20649
+ const grafted = replaceAt(current.children, parts, depth + 1, apply);
20650
+ if (grafted === null) return null;
20651
+ next = {
20652
+ ...current,
20653
+ children: grafted,
20654
+ fileCount: countOf(current.files, grafted)
20655
+ };
20656
+ }
20657
+ const out = [...dirs];
20658
+ out[index] = next;
20659
+ return out;
20660
+ }
20661
+ /**
20662
+ * Graft every read directory's children onto the tree built from the path
20663
+ * list.
20664
+ *
20665
+ * This is the reason expanding an ignored directory is not O(repository):
20666
+ * rebuilding the tree from a merged path list meant a fresh `Set`, a fresh
20667
+ * `localeCompare` sort and a fresh walk over EVERY path on every click —
20668
+ * measured at 140ms on a 50,000-path repository, synchronously, on the click.
20669
+ * Grafting touches only the nodes on the path to each read directory, so the
20670
+ * cost is the depth of what was clicked plus the width of what came back.
20671
+ *
20672
+ * Shallow directories are grafted first, so the empty node a parent's read
20673
+ * creates exists by the time its own read is grafted onto it.
20674
+ *
20675
+ * @param tree - the tree from {@link buildDirTree} over the path list.
20676
+ * @param cache - the directories read so far.
20677
+ */
20678
+ function attachReads(tree, cache) {
20679
+ const dirs = Object.keys(cache.reads);
20680
+ if (dirs.length === 0) return tree;
20681
+ const shallowFirst = [...dirs].sort((a, b) => a.split("/").length - b.split("/").length);
20682
+ let out = tree;
20683
+ for (const dir of shallowFirst) {
20684
+ if (dir.length === 0) continue;
20685
+ const grafted = replaceAt(out, dir.split("/"), 0, (entry) => withChildren(entry, cache.reads[dir]));
20686
+ if (grafted !== null) out = grafted;
20687
+ }
20688
+ return out;
20689
+ }
20690
+ //#endregion
20445
20691
  //#region src/client/files-place.ts
20446
20692
  /**
20447
20693
  * Where the reader is in the Files tab — the file they have open, the folders
@@ -32883,9 +33129,9 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
32883
33129
  * The history filter's path picker has capped at this number since it
32884
33130
  * shipped; the search box is the way to a file in a crowded directory. */
32885
33131
  const FILES_PER_DIR = 100;
32886
- function FileBrowser({ t, palette, statsPath, extraPaths, gen, treeStyle, treeRef, divider, place, onPlace, cached, onTree, fetchRepoTree, fetchFileSides, writeChecked, fetchBlame, fetchFileImage, onSaved, onDirtyChange, onShowHistory }) {
33132
+ function FileBrowser({ t, palette, statsPath, extraPaths, gen, treeStyle, treeRef, divider, place, onPlace, cached, onTree, wrap, fetchRepoTree, fetchIgnoredDir, fetchFileSides, writeChecked, fetchBlame, fetchFileImage, onSaved, onDirtyChange, onShowHistory }) {
32887
33133
  const { open, query, blameOn } = place;
32888
- const { paths, truncated } = cached;
33134
+ const { paths, truncated, ignored, ignoredTruncated, ignoredError, children } = cached;
32889
33135
  const expanded = (0, react.useMemo)(() => new Set(place.expanded), [place.expanded]);
32890
33136
  /** A file that was open and is not in the repository any more. Reported
32891
33137
  * rather than silently applied: a selection that clears itself with no
@@ -32938,10 +33184,14 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
32938
33184
  let alive = true;
32939
33185
  fetchRepoTree(statsPath, ctrl.signal).then((answer) => {
32940
33186
  if (!alive || answer === null) return;
32941
- onTree({
33187
+ onTree(() => ({
32942
33188
  paths: answer.paths,
32943
- truncated: answer.truncated
32944
- });
33189
+ truncated: answer.truncated,
33190
+ ignored: answer.ignored ?? [],
33191
+ ignoredTruncated: answer.ignoredTruncated ?? false,
33192
+ ...answer.ignoredError !== void 0 ? { ignoredError: answer.ignoredError } : {},
33193
+ children: NO_IGNORED_READS
33194
+ }));
32945
33195
  }).catch(() => {});
32946
33196
  return () => {
32947
33197
  alive = false;
@@ -33066,9 +33316,48 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
33066
33316
  const extraHeld = (0, react.useRef)(extraPaths);
33067
33317
  if (!sameList(extraHeld.current, extraPaths)) extraHeld.current = extraPaths;
33068
33318
  const steadyExtra = extraHeld.current;
33069
- const all = (0, react.useMemo)(() => mergePaths(paths, steadyExtra), [paths, steadyExtra]);
33070
- const tree = (0, react.useMemo)(() => buildDirTree(all), [all]);
33071
- const roots = (0, react.useMemo)(() => rootFiles(all), [all]);
33319
+ /** The ignored half of the listing, split by the trailing slash that is its
33320
+ * only kind marker: ignored files join the browsable list (they open,
33321
+ * edit and save through the same path untracked files already take);
33322
+ * collapsed directories become tree hints the rows below render as
33323
+ * expandable folders. */
33324
+ const { files: ignoredFiles, dirs: collapsedDirs } = (0, react.useMemo)(() => splitIgnored(ignored), [ignored]);
33325
+ const ignoredFilesOf = (0, react.useMemo)(() => new Set(ignoredFiles), [ignoredFiles]);
33326
+ const collapsedOf = (0, react.useMemo)(() => new Set(collapsedDirs), [collapsedDirs]);
33327
+ /**
33328
+ * The list git can speak for, and the tree over it. Both change only when
33329
+ * the REPOSITORY is re-read — never when a folder is expanded, which is the
33330
+ * whole point of the split below: merging the lazy children back into this
33331
+ * list meant a fresh `Set`, a fresh `localeCompare` sort and a fresh tree
33332
+ * walk over every path in the repository on every click (140ms at 50,000
33333
+ * paths, measured, synchronously on the click).
33334
+ */
33335
+ const base = (0, react.useMemo)(() => mergePaths(mergePaths(paths, steadyExtra), ignoredFiles), [
33336
+ paths,
33337
+ steadyExtra,
33338
+ ignoredFiles
33339
+ ]);
33340
+ /** Collapsed directories exist with nothing under them YET, and a path list
33341
+ * cannot express that; tracked directories are all implied by their files
33342
+ * and never need a hint. */
33343
+ const baseTree = (0, react.useMemo)(() => buildDirTree(base, collapsedOf), [base, collapsedOf]);
33344
+ /** Everything read since, grafted onto the nodes it belongs to — work
33345
+ * proportional to what was clicked, not to the repository. */
33346
+ const tree = (0, react.useMemo)(() => attachReads(baseTree, children), [baseTree, children]);
33347
+ const roots = (0, react.useMemo)(() => rootFiles(base), [base]);
33348
+ /** Lazy children as ordinary openable paths. The search and the
33349
+ * open-file reconciliation both read a flat list, so they get one — a
33350
+ * CONCATENATION, deliberately: `base` is already sorted and deduped, and
33351
+ * nothing under a collapsed directory can appear in it (git lists neither
33352
+ * ignored files nor untracked ones from inside a collapsed directory). */
33353
+ const lazyFiles = (0, react.useMemo)(() => {
33354
+ const out = [];
33355
+ for (const [dir, read] of Object.entries(children.reads)) for (const child of read.entries) if (!child.dir) out.push(`${dir}/${child.name}`);
33356
+ return out.sort((a, b) => a.localeCompare(b));
33357
+ }, [children]);
33358
+ const all = (0, react.useMemo)(() => lazyFiles.length === 0 ? base : [...base, ...lazyFiles], [base, lazyFiles]);
33359
+ /** Whether a directory the reader can still see was cut at the host's cap. */
33360
+ const dirCut = (0, react.useMemo)(() => anyDirTruncated(children), [children]);
33072
33361
  const rows = (0, react.useMemo)(() => query.trim().length > 0 ? searchRows(all, query, SEARCH_CAP) : treeRows(tree, roots, expanded, FILES_PER_DIR), [
33073
33362
  query,
33074
33363
  all,
@@ -33184,9 +33473,30 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
33184
33473
  setVanished(null);
33185
33474
  onPlace(openAt(place, path));
33186
33475
  };
33476
+ /** Directories whose children are in flight, so the effect further down
33477
+ * does not ask twice for the same one. */
33478
+ const pendingDirs = (0, react.useRef)(/* @__PURE__ */ new Set());
33479
+ /**
33480
+ * How many times each directory has been expanded. A read belongs to ONE
33481
+ * attempt: fold a directory while its read is in flight, expand it again,
33482
+ * and the first read's answer — including its decision to fold the row back
33483
+ * on failure — would otherwise land on the second attempt and undo the
33484
+ * reader's click.
33485
+ */
33486
+ const attempts = (0, react.useRef)(/* @__PURE__ */ new Map());
33187
33487
  const foldDir = (path) => {
33488
+ if (place.expanded.includes(path)) {
33489
+ attempts.current.set(path, (attempts.current.get(path) ?? 0) + 1);
33490
+ pendingDirs.current.delete(path);
33491
+ }
33188
33492
  onPlace(toggleDir(place, path));
33189
33493
  };
33494
+ /** Collapse a directory only if it stands open — the failure path of a
33495
+ * lazy read, where a plain toggle would re-open what the reader already
33496
+ * folded while the read was in flight. */
33497
+ const foldIfOpen = (path) => {
33498
+ if (place.expanded.includes(path)) onPlace(toggleDir(place, path));
33499
+ };
33190
33500
  const idRef = (0, react.useRef)(0);
33191
33501
  (0, react.useEffect)(() => {
33192
33502
  idRef.current += 1;
@@ -33230,12 +33540,65 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
33230
33540
  */
33231
33541
  const acts = (0, react.useRef)({
33232
33542
  foldDir,
33233
- openFile
33543
+ openFile,
33544
+ foldIfOpen
33234
33545
  });
33235
33546
  acts.current = {
33236
33547
  foldDir,
33237
- openFile
33238
- };
33548
+ openFile,
33549
+ foldIfOpen
33550
+ };
33551
+ /** What eviction must not touch: the directories standing open, and the
33552
+ * ancestors of the open file — whose path has to stay in the browsable
33553
+ * list, or the reconciliation below would report the open file vanished. */
33554
+ const keepRef = (0, react.useRef)(/* @__PURE__ */ new Set());
33555
+ keepRef.current = (0, react.useMemo)(() => /* @__PURE__ */ new Set([...place.expanded, ...open === null ? [] : ancestorsOf(open)]), [place.expanded, open]);
33556
+ /** Read one ignored directory's children into the tree cache. A no-op when
33557
+ * they are already there or in flight. */
33558
+ const loadDirChildren = (0, react.useCallback)((dir) => {
33559
+ if (pendingDirs.current.has(dir)) return;
33560
+ pendingDirs.current.add(dir);
33561
+ const attempt = (attempts.current.get(dir) ?? 0) + 1;
33562
+ attempts.current.set(dir, attempt);
33563
+ const settle = () => {
33564
+ pendingDirs.current.delete(dir);
33565
+ return attempts.current.get(dir) === attempt;
33566
+ };
33567
+ fetchIgnoredDir(statsPath, dir, new AbortController().signal).then((answer) => {
33568
+ if (!settle()) return;
33569
+ if (answer === null) {
33570
+ acts.current.foldIfOpen(dir);
33571
+ return;
33572
+ }
33573
+ onTree((prev) => ({
33574
+ ...prev,
33575
+ children: rememberDir(prev.children, dir, answer, keepRef.current)
33576
+ }));
33577
+ }).catch(() => {
33578
+ settle();
33579
+ });
33580
+ }, [
33581
+ statsPath,
33582
+ fetchIgnoredDir,
33583
+ onTree
33584
+ ]);
33585
+ (0, react.useEffect)(() => {
33586
+ for (const dir of place.expanded) if (children.reads[dir] === void 0 && isIgnoredPath(dir, ignoredFilesOf, collapsedOf)) loadDirChildren(dir);
33587
+ }, [
33588
+ place.expanded,
33589
+ children,
33590
+ ignoredFilesOf,
33591
+ collapsedOf,
33592
+ loadDirChildren
33593
+ ]);
33594
+ /**
33595
+ * `t` arrives in the host's slot props and its identity is not ours to rely
33596
+ * on, so the memo watches the STRING it produces instead: that is what has
33597
+ * to change when the language does, and it costs one lookup per render.
33598
+ */
33599
+ const langKey = t("filesMore", { count: 0 });
33600
+ /** Same trick for the ignored rows' title suffix. */
33601
+ const ignoredLabel = t("filesIgnored");
33239
33602
  /**
33240
33603
  * The rendered rows.
33241
33604
  *
@@ -33246,33 +33609,42 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
33246
33609
  * the reader opens — the shape of "it gets laggy once there are a lot of
33247
33610
  * files".
33248
33611
  */
33249
- const list = (0, react.useMemo)(() => rows.map((row) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: row.kind === "more" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
33250
- className: GitWorkbenchPanel_module_css_default.fbMore,
33251
- style: { paddingLeft: `${.4 + row.depth * INDENT_EM}em` },
33252
- children: t("filesMore", { count: row.hidden ?? 0 })
33253
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
33254
- type: "button",
33255
- title: row.path,
33256
- "aria-expanded": row.kind === "dir" ? row.open : void 0,
33257
- className: row.kind === "file" && row.path === open ? `${GitWorkbenchPanel_module_css_default.fbRow} ${GitWorkbenchPanel_module_css_default.fbRowActive}` : GitWorkbenchPanel_module_css_default.fbRow,
33258
- style: { paddingLeft: `${.4 + row.depth * INDENT_EM}em` },
33259
- onClick: () => {
33260
- row.kind === "dir" ? acts.current.foldDir(row.path) : acts.current.openFile(row.path);
33261
- },
33262
- children: [row.kind === "dir" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
33263
- className: `${GitWorkbenchPanel_module_css_default.chevron} ${row.open ? GitWorkbenchPanel_module_css_default.chevronOpen : ""}`,
33264
- children: "▸"
33265
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PathDirGlyph, {})] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
33266
- className: GitWorkbenchPanel_module_css_default.chevron,
33267
- "aria-hidden": "true"
33268
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PathFileGlyph, { path: row.path })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
33269
- className: row.kind === "dir" ? GitWorkbenchPanel_module_css_default.fbDirName : GitWorkbenchPanel_module_css_default.fbFileName,
33270
- children: row.name
33271
- })]
33272
- }) }, rowKey(row))), [
33612
+ const list = (0, react.useMemo)(() => rows.map((row) => {
33613
+ /** Ignored territory, by file or by descent from a collapsed directory.
33614
+ * The active row is exempt in the className below: being the file on
33615
+ * screen is the louder state, and the two must not fight. */
33616
+ const ignored = row.kind !== "more" && isIgnoredPath(row.path, ignoredFilesOf, collapsedOf);
33617
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: row.kind === "more" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
33618
+ className: GitWorkbenchPanel_module_css_default.fbMore,
33619
+ style: { paddingLeft: `${.4 + row.depth * INDENT_EM}em` },
33620
+ children: t("filesMore", { count: row.hidden ?? 0 })
33621
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
33622
+ type: "button",
33623
+ title: ignored ? `${row.path} — ${ignoredLabel}` : row.path,
33624
+ "aria-expanded": row.kind === "dir" ? row.open : void 0,
33625
+ className: row.kind === "file" && row.path === open ? `${GitWorkbenchPanel_module_css_default.fbRow} ${GitWorkbenchPanel_module_css_default.fbRowActive}` : ignored ? `${GitWorkbenchPanel_module_css_default.fbRow} ${GitWorkbenchPanel_module_css_default.fbRowIgnored}` : GitWorkbenchPanel_module_css_default.fbRow,
33626
+ style: { paddingLeft: `${.4 + row.depth * INDENT_EM}em` },
33627
+ onClick: () => {
33628
+ row.kind === "dir" ? acts.current.foldDir(row.path) : acts.current.openFile(row.path);
33629
+ },
33630
+ children: [row.kind === "dir" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
33631
+ className: `${GitWorkbenchPanel_module_css_default.chevron} ${row.open ? GitWorkbenchPanel_module_css_default.chevronOpen : ""}`,
33632
+ children: ""
33633
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PathDirGlyph, {})] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
33634
+ className: GitWorkbenchPanel_module_css_default.chevron,
33635
+ "aria-hidden": "true"
33636
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PathFileGlyph, { path: row.path })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
33637
+ className: row.kind === "dir" ? GitWorkbenchPanel_module_css_default.fbDirName : GitWorkbenchPanel_module_css_default.fbFileName,
33638
+ children: row.name
33639
+ })]
33640
+ }) }, rowKey(row));
33641
+ }), [
33273
33642
  rows,
33274
33643
  open,
33275
- t("filesMore", { count: 0 })
33644
+ langKey,
33645
+ ignoredLabel,
33646
+ ignoredFilesOf,
33647
+ collapsedOf
33276
33648
  ]);
33277
33649
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
33278
33650
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
@@ -33294,10 +33666,18 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
33294
33666
  });
33295
33667
  }
33296
33668
  }),
33297
- truncated ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
33669
+ truncated || ignoredTruncated ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
33298
33670
  className: GitWorkbenchPanel_module_css_default.fbNote,
33299
33671
  children: t("filesTruncated")
33300
33672
  }) : null,
33673
+ dirCut ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
33674
+ className: GitWorkbenchPanel_module_css_default.fbNote,
33675
+ children: t("filesIgnoredCut")
33676
+ }) : null,
33677
+ ignoredError !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
33678
+ className: GitWorkbenchPanel_module_css_default.fbNote,
33679
+ children: t("filesIgnoredFailed")
33680
+ }) : null,
33301
33681
  rows.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
33302
33682
  className: GitWorkbenchPanel_module_css_default.empty,
33303
33683
  children: all.length === 0 ? t("filesEmpty") : t("filesNoMatch")
@@ -33517,7 +33897,8 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
33517
33897
  setPicked(line);
33518
33898
  },
33519
33899
  notCommitted: t("blameUncommitted"),
33520
- readOnly
33900
+ readOnly,
33901
+ wrap
33521
33902
  }, open)
33522
33903
  })
33523
33904
  ] })
@@ -36294,7 +36675,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
36294
36675
  * @param offset - px above the first row, e.g. the grid's top padding.
36295
36676
  * @returns the first row of each block, in the order the blocks appear.
36296
36677
  */
36297
- function blockTopsFromRows(blocks, rowH, offset = 0) {
36678
+ function blockTopsFromRows(blocks, rowH, offset = 0, topOf) {
36298
36679
  const tops = [];
36299
36680
  const seen = /* @__PURE__ */ new Set();
36300
36681
  for (let i = 0; i < blocks.length; i += 1) {
@@ -36303,7 +36684,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
36303
36684
  seen.add(block);
36304
36685
  tops.push({
36305
36686
  block,
36306
- top: offset + i * rowH
36687
+ top: offset + (topOf === void 0 ? i * rowH : topOf(i))
36307
36688
  });
36308
36689
  }
36309
36690
  return tops;
@@ -36349,7 +36730,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
36349
36730
  * until the height is known would flash an empty pane; a screenful is both
36350
36731
  * safe and close.
36351
36732
  */
36352
- const ASSUMED_VIEWPORT_PX = 1200;
36733
+ const ASSUMED_VIEWPORT_PX$1 = 1200;
36353
36734
  /** Whether two windows paint the same rows AND reserve the same total height.
36354
36735
  * Comparing only start/end leaves a previous file's bottom spacer behind when
36355
36736
  * two long files happen to expose the same viewport-sized row range. */
@@ -36365,7 +36746,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
36365
36746
  * @param fallback - used when it is neither finite nor usable.
36366
36747
  * @returns a finite, non-negative number.
36367
36748
  */
36368
- function sane(value, fallback) {
36749
+ function sane$1(value, fallback) {
36369
36750
  return Number.isFinite(value) && value > 0 ? value : fallback;
36370
36751
  }
36371
36752
  /**
@@ -36380,7 +36761,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
36380
36761
  */
36381
36762
  function rowWindow(scrollTop, viewportH, rowCount, rowH = 20, overscan = 40) {
36382
36763
  const rows = Math.max(0, Math.trunc(rowCount));
36383
- const height = sane(rowH, 20);
36764
+ const height = sane$1(rowH, 20);
36384
36765
  if (rows <= 400) return {
36385
36766
  start: 0,
36386
36767
  end: rows,
@@ -36388,7 +36769,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
36388
36769
  padBottom: 0
36389
36770
  };
36390
36771
  const top = Number.isFinite(scrollTop) && scrollTop > 0 ? scrollTop : 0;
36391
- const view = sane(viewportH, ASSUMED_VIEWPORT_PX);
36772
+ const view = sane$1(viewportH, ASSUMED_VIEWPORT_PX$1);
36392
36773
  const pad = Math.max(0, Math.trunc(overscan));
36393
36774
  const first = Math.max(0, Math.floor(top / height) - pad);
36394
36775
  const last = Math.min(rows, Math.ceil((top + view) / height) + pad);
@@ -36540,6 +36921,154 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
36540
36921
  });
36541
36922
  }
36542
36923
  //#endregion
36924
+ //#region src/client/h-rail.ts
36925
+ /** Before anything has been measured, and for a column that is not mounted. */
36926
+ const NO_RAIL = {
36927
+ view: 0,
36928
+ content: 0
36929
+ };
36930
+ /**
36931
+ * Whether a column overflows by enough to be worth a scrollbar.
36932
+ *
36933
+ * The one-pixel slack is not tidiness: a grid track rounds to a fraction, so a
36934
+ * column whose longest line exactly fits still reports a scroll width a third
36935
+ * of a pixel wider than its client width. Without the slack every file gets a
36936
+ * rail, and every one of those rails scrolls nowhere.
36937
+ *
36938
+ * @param metric - the column's measured widths.
36939
+ * @returns true when a rail should be shown for it.
36940
+ */
36941
+ function railNeeded(metric) {
36942
+ const { view, content } = metric;
36943
+ if (!Number.isFinite(view) || !Number.isFinite(content)) return false;
36944
+ if (view <= 0) return false;
36945
+ return content > view + 1;
36946
+ }
36947
+ /** Whether the rail row is shown at all — either column overflowing is enough,
36948
+ * since the row is one sticky strip and a lone rail still needs its space. */
36949
+ function railsShown(left, right) {
36950
+ return railNeeded(left) || railNeeded(right);
36951
+ }
36952
+ /**
36953
+ * Whether two measurements are the same.
36954
+ *
36955
+ * The measuring effect runs on every render, so this is what keeps it from
36956
+ * setting state on every render and rendering again forever.
36957
+ *
36958
+ * @param a - the measurement held in state.
36959
+ * @param b - the one just read from the DOM.
36960
+ */
36961
+ function sameMetric(a, b) {
36962
+ return a.view === b.view && a.content === b.content;
36963
+ }
36964
+ //#endregion
36965
+ //#region src/client/SideRails.tsx
36966
+ /**
36967
+ * Measure one column and keep its rail's scroll position tied to it.
36968
+ *
36969
+ * The measurement runs on EVERY render rather than on a dependency list: the
36970
+ * content width changes for reasons no list can name — a window scrolled to
36971
+ * longer rows, syntax spans replacing plain text, the editor mounting into the
36972
+ * right column, wrap turning on. Two integer reads is a cheap price for never
36973
+ * being stale, and {@link sameMetric} is what keeps setting state on every
36974
+ * render from becoming a render on every render.
36975
+ *
36976
+ * @param colRef - the column, which clips and scrolls its own content.
36977
+ * @param railRef - the strip at the bottom of the pane that stands in for it.
36978
+ * @returns the column's measured widths.
36979
+ */
36980
+ function useRail(colRef, railRef) {
36981
+ const [metric, setMetric] = (0, react.useState)(NO_RAIL);
36982
+ const measure = (col) => {
36983
+ const next = {
36984
+ view: col.clientWidth,
36985
+ content: col.scrollWidth
36986
+ };
36987
+ setMetric((prev) => sameMetric(prev, next) ? prev : next);
36988
+ };
36989
+ (0, react.useLayoutEffect)(() => {
36990
+ const col = colRef.current;
36991
+ if (col !== null) measure(col);
36992
+ });
36993
+ (0, react.useEffect)(() => {
36994
+ const col = colRef.current;
36995
+ if (col === null) return;
36996
+ const observer = new ResizeObserver(() => {
36997
+ measure(col);
36998
+ });
36999
+ observer.observe(col);
37000
+ return () => {
37001
+ observer.disconnect();
37002
+ };
37003
+ }, [colRef]);
37004
+ (0, react.useEffect)(() => {
37005
+ const col = colRef.current;
37006
+ const rail = railRef.current;
37007
+ if (col === null || rail === null) return;
37008
+ const toRail = () => {
37009
+ if (rail.scrollLeft !== col.scrollLeft) rail.scrollLeft = col.scrollLeft;
37010
+ };
37011
+ const toCol = () => {
37012
+ if (col.scrollLeft !== rail.scrollLeft) col.scrollLeft = rail.scrollLeft;
37013
+ };
37014
+ col.addEventListener("scroll", toRail, { passive: true });
37015
+ rail.addEventListener("scroll", toCol, { passive: true });
37016
+ toRail();
37017
+ return () => {
37018
+ col.removeEventListener("scroll", toRail);
37019
+ rail.removeEventListener("scroll", toCol);
37020
+ };
37021
+ }, [colRef, railRef]);
37022
+ return metric;
37023
+ }
37024
+ /**
37025
+ * The side-by-side pane's horizontal scrollbars, stuck to the bottom of the
37026
+ * pane instead of the bottom of the file — see `h-rail.ts` for why they are
37027
+ * here at all.
37028
+ *
37029
+ * Always rendered, never conditionally: the strip collapses to nothing through
37030
+ * a class when neither column overflows, so both rails keep their elements and
37031
+ * the effects above keep stable dependencies. A rail that mounted and
37032
+ * unmounted with the measurement would re-attach its listeners on every file.
37033
+ *
37034
+ * @param leftRef - the left column.
37035
+ * @param rightRef - the right column.
37036
+ * @param split - the divider's position, as a fraction of the pane's width;
37037
+ * the rails carry the same flex sizing so each sits under the
37038
+ * column it scrolls.
37039
+ * @returns the sticky rail strip.
37040
+ */
37041
+ function SideRails({ leftRef, rightRef, split }) {
37042
+ const leftRailRef = (0, react.useRef)(null);
37043
+ const rightRailRef = (0, react.useRef)(null);
37044
+ const left = useRail(leftRef, leftRailRef);
37045
+ const right = useRail(rightRef, rightRailRef);
37046
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
37047
+ className: railsShown(left, right) ? `${GitWorkbenchPanel_module_css_default.sideRails} ${GitWorkbenchPanel_module_css_default.sideRailsOn}` : GitWorkbenchPanel_module_css_default.sideRails,
37048
+ "aria-hidden": "true",
37049
+ children: [
37050
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
37051
+ ref: leftRailRef,
37052
+ className: GitWorkbenchPanel_module_css_default.sideRail,
37053
+ style: { flexBasis: `${split * 100}%` },
37054
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
37055
+ className: GitWorkbenchPanel_module_css_default.sideRailSpan,
37056
+ style: { width: left.content }
37057
+ })
37058
+ }),
37059
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: GitWorkbenchPanel_module_css_default.sideRailGap }),
37060
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
37061
+ ref: rightRailRef,
37062
+ className: `${GitWorkbenchPanel_module_css_default.sideRail} ${GitWorkbenchPanel_module_css_default.sideRailRight}`,
37063
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
37064
+ className: GitWorkbenchPanel_module_css_default.sideRailSpan,
37065
+ style: { width: right.content }
37066
+ })
37067
+ })
37068
+ ]
37069
+ });
37070
+ }
37071
+ //#endregion
36543
37072
  //#region src/client/use-row-window.ts
36544
37073
  /**
36545
37074
  * The rows a diff actually has to put in the DOM, tracked against its scroller.
@@ -36555,9 +37084,13 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
36555
37084
  *
36556
37085
  * @param scrollRef - the element that scrolls the rows.
36557
37086
  * @param rowCount - how many rows the diff has.
37087
+ * @param enabled - false while soft wrap is on, when the rows are no longer a
37088
+ * fixed height and `use-variable-row-window.ts` answers
37089
+ * instead. Attaching both panes' listeners at once would put
37090
+ * two readers on the same scroll.
36558
37091
  * @returns the rows to render and the spacer heights standing in for the rest.
36559
37092
  */
36560
- function useRowWindow(scrollRef, rowCount, mountKey) {
37093
+ function useRowWindow(scrollRef, rowCount, mountKey, enabled = true) {
36561
37094
  const count = Number.isFinite(rowCount) ? Math.max(0, Math.trunc(rowCount)) : 0;
36562
37095
  const [held, setHeld] = (0, react.useState)(() => ({
36563
37096
  mountKey,
@@ -36567,7 +37100,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
36567
37100
  const visible = rowWindowForMount(held, count, mountKey);
36568
37101
  (0, react.useEffect)(() => {
36569
37102
  const el = scrollRef.current;
36570
- if (el === null) return;
37103
+ if (el === null || !enabled) return;
36571
37104
  const read = () => {
36572
37105
  const next = rowWindow(el.scrollTop, el.clientHeight, count);
36573
37106
  setHeld((prev) => prev.mountKey === mountKey && prev.rowCount === count && sameRowWindow(prev.win, next) ? prev : {
@@ -36587,11 +37120,475 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
36587
37120
  }, [
36588
37121
  scrollRef,
36589
37122
  count,
36590
- mountKey
37123
+ mountKey,
37124
+ enabled
36591
37125
  ]);
36592
37126
  return visible;
36593
37127
  }
36594
37128
  //#endregion
37129
+ //#region src/client/row-heights.ts
37130
+ /**
37131
+ * Row offsets when the rows are NOT all the same height — what soft wrap makes
37132
+ * of a diff pane.
37133
+ *
37134
+ * `row-window.ts` places row `i` at `i * 20px` with no measuring at all, and
37135
+ * says so: the panes guarantee it by writing `white-space: pre`, so nothing
37136
+ * ever wraps. Turn wrapping on and that guarantee is gone — one row can be
37137
+ * five lines tall — and every number the window computes from it (which rows
37138
+ * are visible, how tall the spacers are, where a change sits) is wrong.
37139
+ *
37140
+ * The replacement keeps the same shape and the same rule: work proportional to
37141
+ * the VIEWPORT. Heights are MEASURED, but only for rows that are actually in
37142
+ * the DOM, which is the window plus its overscan; every other row carries an
37143
+ * estimate computed from its text. So a 20,000-row file costs one array and
37144
+ * some arithmetic, not 20,000 measurements.
37145
+ *
37146
+ * The prefix sums are a Fenwick tree rather than a running array, because both
37147
+ * things this is asked for happen on the scroll path and must not be linear:
37148
+ * "where does row i start" is a prefix query, "which row is at y" is a search
37149
+ * for a prefix, and a measurement that lands changes one row's height. All
37150
+ * three are O(log n) here; as a plain array of running totals, the third would
37151
+ * rewrite every entry after the row that moved.
37152
+ *
37153
+ * Pure: no React, no DOM. `tests/row-heights.test.ts` loads it directly.
37154
+ *
37155
+ * @module @young1lin/dsh-ui-gitworkbench/client/row-heights
37156
+ */
37157
+ /** A viewport height to assume before the scroller has been measured, matching
37158
+ * `row-window.ts`: rendering nothing until the height is known flashes an
37159
+ * empty pane, and a screenful is both safe and close. */
37160
+ const ASSUMED_VIEWPORT_PX = 1200;
37161
+ /**
37162
+ * How many display columns a line occupies.
37163
+ *
37164
+ * Only for the ESTIMATE of rows nobody has measured, so it does not have to
37165
+ * agree with the browser to the character — it has to be close enough that
37166
+ * the scrollbar is the right length and a jump to the middle of the file lands
37167
+ * near where it should. Tabs advance to the next stop (`tab-size: 4` in the
37168
+ * panes' grid) and full-width characters take two columns, which are the two
37169
+ * ways a naive `text.length` is badly wrong rather than slightly wrong.
37170
+ *
37171
+ * @param text - one row's text.
37172
+ * @param tabSize - columns a tab advances to the next multiple of.
37173
+ */
37174
+ function displayColumns(text, tabSize = 4) {
37175
+ let columns = 0;
37176
+ for (const char of text) {
37177
+ if (char === " ") {
37178
+ columns += tabSize - columns % tabSize;
37179
+ continue;
37180
+ }
37181
+ columns += isWide(char) ? 2 : 1;
37182
+ }
37183
+ return columns;
37184
+ }
37185
+ /**
37186
+ * Whether a character takes two columns in a monospace font: CJK, Hangul,
37187
+ * kana, the full-width forms, and the emoji blocks. The ranges rather than a
37188
+ * table because this runs over a file's worth of text and the answer only
37189
+ * feeds an estimate.
37190
+ */
37191
+ function isWide(char) {
37192
+ const code = char.codePointAt(0) ?? 0;
37193
+ return code >= 4352 && code <= 4447 || code >= 11904 && code <= 42191 || code >= 44032 && code <= 55203 || code >= 63744 && code <= 64255 || code >= 65072 && code <= 65135 || code >= 65280 && code <= 65376 || code >= 65504 && code <= 65510 || code >= 127744 && code <= 129535;
37194
+ }
37195
+ /**
37196
+ * The height a row is guessed to have before it has been measured.
37197
+ *
37198
+ * @param text - the row's text.
37199
+ * @param columns - how many columns fit across the pane; 0 or less means the
37200
+ * pane has not been measured yet, and one line is assumed.
37201
+ * @param rowH - one line's height in px.
37202
+ */
37203
+ function estimateRowHeight(text, columns, rowH) {
37204
+ if (!Number.isFinite(columns) || columns <= 0) return rowH;
37205
+ return Math.max(1, Math.ceil(displayColumns(text) / columns)) * rowH;
37206
+ }
37207
+ /**
37208
+ * Per-row heights with O(log n) offsets, backed by a Fenwick tree.
37209
+ *
37210
+ * Bounded by construction: two arrays of `rowCount`, which the panes already
37211
+ * cap. {@link measured} reports how much of it is real rather than guessed, so
37212
+ * a test can prove the measuring stays viewport-sized instead of trusting it.
37213
+ */
37214
+ var RowHeights = class {
37215
+ /** Each row's height. */
37216
+ heights;
37217
+ /** Fenwick sums, 1-based; `tree[i]` covers a block ending at row `i - 1`. */
37218
+ tree;
37219
+ /** Rows whose height came from the DOM rather than from an estimate. */
37220
+ real;
37221
+ realCount = 0;
37222
+ /**
37223
+ * @param initial - each row's starting height, normally an estimate.
37224
+ */
37225
+ constructor(initial) {
37226
+ const n = initial.length;
37227
+ this.heights = new Float64Array(n);
37228
+ this.tree = new Float64Array(n + 1);
37229
+ this.real = new Uint8Array(n);
37230
+ for (let i = 0; i < n; i += 1) this.heights[i] = sane(initial[i]);
37231
+ for (let i = 1; i <= n; i += 1) {
37232
+ this.tree[i] += this.heights[i - 1];
37233
+ const parent = i + (i & -i);
37234
+ if (parent <= n) this.tree[parent] += this.tree[i];
37235
+ }
37236
+ }
37237
+ /** How many rows there are. */
37238
+ get count() {
37239
+ return this.heights.length;
37240
+ }
37241
+ /** How many rows carry a measured height rather than an estimate. */
37242
+ measured() {
37243
+ return this.realCount;
37244
+ }
37245
+ /**
37246
+ * Record one row's real height.
37247
+ * @returns whether anything moved, so a caller can avoid a re-render.
37248
+ */
37249
+ set(index, px) {
37250
+ if (!Number.isInteger(index) || index < 0 || index >= this.count) return false;
37251
+ const next = sane(px);
37252
+ if (this.real[index] === 0) {
37253
+ this.real[index] = 1;
37254
+ this.realCount += 1;
37255
+ }
37256
+ const delta = next - this.heights[index];
37257
+ if (delta === 0) return false;
37258
+ this.heights[index] = next;
37259
+ for (let i = index + 1; i <= this.count; i += i & -i) this.tree[i] += delta;
37260
+ return true;
37261
+ }
37262
+ /** One row's height. */
37263
+ heightAt(index) {
37264
+ if (index < 0 || index >= this.count) return 0;
37265
+ return this.heights[index];
37266
+ }
37267
+ /** Where a row starts, measured from the top of the scrolled content. */
37268
+ topAt(index) {
37269
+ let sum = 0;
37270
+ for (let i = Math.max(0, Math.min(index, this.count)); i > 0; i -= i & -i) sum += this.tree[i];
37271
+ return sum;
37272
+ }
37273
+ /** Every row's height together. */
37274
+ total() {
37275
+ return this.topAt(this.count);
37276
+ }
37277
+ /**
37278
+ * The row that contains `y`, by binary lifting over the tree — the same
37279
+ * search a running-totals array would do, without the array.
37280
+ */
37281
+ rowAt(y) {
37282
+ if (this.count === 0) return 0;
37283
+ let remaining = Number.isFinite(y) && y > 0 ? y : 0;
37284
+ let index = 0;
37285
+ let step = 1;
37286
+ while (step * 2 <= this.count) step *= 2;
37287
+ for (; step > 0; step = Math.floor(step / 2)) {
37288
+ const probe = index + step;
37289
+ if (probe <= this.count && this.tree[probe] <= remaining) {
37290
+ remaining -= this.tree[probe];
37291
+ index = probe;
37292
+ }
37293
+ }
37294
+ return Math.min(index, this.count - 1);
37295
+ }
37296
+ };
37297
+ /** @param value - a number from the DOM, which can be NaN or negative. */
37298
+ function sane(value) {
37299
+ return Number.isFinite(value) && value > 0 ? value : 0;
37300
+ }
37301
+ /**
37302
+ * The window of rows to render when the rows are not all one height.
37303
+ *
37304
+ * Same answer shape as {@link rowWindow}, and the same short-circuit below
37305
+ * {@link WINDOW_WHOLE_BELOW}: windowing has costs of its own, and a file that
37306
+ * was never slow should render exactly as it did before.
37307
+ *
37308
+ * @param scrollTop - the scroller's current offset in px.
37309
+ * @param viewportH - the scroller's visible height in px; 0 before measuring.
37310
+ * @param heights - the rows' heights.
37311
+ * @param overscan - rows to keep beyond each edge.
37312
+ */
37313
+ function variableRowWindow(scrollTop, viewportH, heights, overscan = 40) {
37314
+ const rows = heights.count;
37315
+ if (rows <= 400) return {
37316
+ start: 0,
37317
+ end: rows,
37318
+ padTop: 0,
37319
+ padBottom: 0
37320
+ };
37321
+ const top = Number.isFinite(scrollTop) && scrollTop > 0 ? scrollTop : 0;
37322
+ const view = Number.isFinite(viewportH) && viewportH > 0 ? viewportH : ASSUMED_VIEWPORT_PX;
37323
+ const pad = Math.max(0, Math.trunc(overscan));
37324
+ const start = Math.max(0, heights.rowAt(top) - pad);
37325
+ const end = Math.min(rows, heights.rowAt(top + view) + 1 + pad);
37326
+ const safeEnd = Math.max(start, end);
37327
+ return {
37328
+ start,
37329
+ end: safeEnd,
37330
+ padTop: heights.topAt(start),
37331
+ padBottom: Math.max(0, heights.total() - heights.topAt(safeEnd))
37332
+ };
37333
+ }
37334
+ //#endregion
37335
+ //#region src/client/use-variable-row-window.ts
37336
+ /**
37337
+ * The windowing hook for panes whose rows are not all one height — a diff
37338
+ * pane with soft wrap on.
37339
+ *
37340
+ * The fixed-height hook beside this one (`use-row-window.ts`) needs nothing
37341
+ * from the DOM but the scroll offset, because `i * 20px` is the answer. Once
37342
+ * lines wrap, only the DOM knows how tall a row came out, so this one measures
37343
+ * — and measures ONLY the rows it just rendered, which is the window plus its
37344
+ * overscan. Every other row carries an estimate computed from its text, so the
37345
+ * scrollbar is close from the first paint instead of growing as the reader
37346
+ * scrolls.
37347
+ *
37348
+ * A measurement that replaces an estimate for a row ABOVE the viewport moves
37349
+ * everything below it, including what the reader is looking at. The scroll
37350
+ * offset is corrected by the same delta in the same layout pass, so the rows
37351
+ * on screen stay under the eye rather than sliding.
37352
+ *
37353
+ * @module @young1lin/dsh-ui-gitworkbench/client/use-variable-row-window
37354
+ */
37355
+ /** Attribute a pane puts on each measurable element, as `scope:index`. */
37356
+ const ROW_INDEX_ATTR = "data-gw-row";
37357
+ /**
37358
+ * What a pane writes on an element it wants measured.
37359
+ *
37360
+ * The scope is half the value rather than a second attribute because two
37361
+ * height models can be mounted inside one scroller — the aligned diff and the
37362
+ * dense index column beside the editor — and a bare index would let each read
37363
+ * the other's rows as its own.
37364
+ *
37365
+ * @param scope - which height model the element belongs to.
37366
+ * @param index - the row's index WITHIN that model.
37367
+ */
37368
+ function rowMark(scope, index) {
37369
+ return { [ROW_INDEX_ATTR]: `${scope}:${index}` };
37370
+ }
37371
+ /**
37372
+ * Columns that fit across a pane, from its width and one character's advance.
37373
+ *
37374
+ * Measured off a probe rather than assumed: the panes' font size is a theme
37375
+ * variable a reader can change, so a hard-coded character width would put the
37376
+ * estimate out by a third on the smallest setting.
37377
+ *
37378
+ * @param grid - the element the rows are laid out in.
37379
+ * @returns columns across, or 0 when the element is not laid out yet.
37380
+ */
37381
+ function columnsAcross(grid) {
37382
+ if (grid === null) return 0;
37383
+ const width = grid.clientWidth;
37384
+ if (!Number.isFinite(width) || width <= 0) return 0;
37385
+ const probe = document.createElement("span");
37386
+ probe.textContent = "0".repeat(100);
37387
+ probe.style.cssText = "position:absolute;visibility:hidden;white-space:pre;pointer-events:none";
37388
+ grid.appendChild(probe);
37389
+ const advance = probe.getBoundingClientRect().width / 100;
37390
+ probe.remove();
37391
+ if (!Number.isFinite(advance) || advance <= 0) return 0;
37392
+ return Math.max(1, Math.floor(width / advance));
37393
+ }
37394
+ function useVariableRowWindow({ scrollRef, rowsRef, widthRef, texts, mountKey, scope, enabled, rowH = 20 }) {
37395
+ /** Columns across the pane. State, because the estimate depends on it and a
37396
+ * dragged divider changes it. */
37397
+ const [columns, setColumns] = (0, react.useState)(0);
37398
+ /**
37399
+ * Bumped when a measurement moved a row.
37400
+ *
37401
+ * The heights live in a mutable structure, so changing one changes no
37402
+ * identity React watches — and the side-by-side pane reads them DURING
37403
+ * render, to impose each row's height on both of its cells. Without this
37404
+ * the first pass would measure correctly and then never re-render to apply
37405
+ * what it measured, which is a row that is one line taller on one side than
37406
+ * on the other. Bumped only when something actually moved, and imposing a
37407
+ * height does not change what is measured (the measured box is inside the
37408
+ * cell), so it settles in one extra pass.
37409
+ */
37410
+ const [, bumpHeights] = (0, react.useState)(0);
37411
+ const [win, setWin] = (0, react.useState)(() => ({
37412
+ start: 0,
37413
+ end: texts.length,
37414
+ padTop: 0,
37415
+ padBottom: 0
37416
+ }));
37417
+ /** Rebuilt only when the diff or the width really changes — never on scroll. */
37418
+ const heights = (0, react.useMemo)(() => new RowHeights(enabled ? texts.map((text) => estimateRowHeight(text, columns, rowH)) : []), [
37419
+ texts,
37420
+ columns,
37421
+ rowH,
37422
+ mountKey,
37423
+ enabled
37424
+ ]);
37425
+ const read = (0, react.useCallback)(() => {
37426
+ const el = scrollRef.current;
37427
+ if (el === null || heights.count === 0) return;
37428
+ const next = variableRowWindow(el.scrollTop, el.clientHeight, heights);
37429
+ setWin((prev) => sameRowWindow(prev, next) ? prev : next);
37430
+ }, [scrollRef, heights]);
37431
+ (0, react.useEffect)(() => {
37432
+ const el = scrollRef.current;
37433
+ if (el === null || !enabled) return;
37434
+ read();
37435
+ const observer = new ResizeObserver(() => {
37436
+ read();
37437
+ setColumns((prev) => {
37438
+ const measured = columnsAcross((widthRef ?? rowsRef).current);
37439
+ return measured === 0 || measured === prev ? prev : measured;
37440
+ });
37441
+ });
37442
+ el.addEventListener("scroll", read, { passive: true });
37443
+ observer.observe(el);
37444
+ return () => {
37445
+ el.removeEventListener("scroll", read);
37446
+ observer.disconnect();
37447
+ };
37448
+ }, [
37449
+ scrollRef,
37450
+ rowsRef,
37451
+ widthRef,
37452
+ read,
37453
+ enabled
37454
+ ]);
37455
+ (0, react.useLayoutEffect)(() => {
37456
+ const root = rowsRef.current;
37457
+ const scroller = scrollRef.current;
37458
+ if (!enabled || root === null || scroller === null) return;
37459
+ const before = heights.topAt(win.start);
37460
+ const tallest = /* @__PURE__ */ new Map();
37461
+ for (const node of root.querySelectorAll(`[${ROW_INDEX_ATTR}^="${scope}:"]`)) {
37462
+ const index = Number(node.getAttribute(ROW_INDEX_ATTR)?.slice(scope.length + 1));
37463
+ if (!Number.isInteger(index)) continue;
37464
+ tallest.set(index, Math.max(tallest.get(index) ?? 0, node.getBoundingClientRect().height));
37465
+ }
37466
+ let moved = false;
37467
+ for (const [index, height] of tallest) if (heights.set(index, height)) moved = true;
37468
+ if (!moved) return;
37469
+ const after = heights.topAt(win.start);
37470
+ if (after !== before) scroller.scrollTop += after - before;
37471
+ bumpHeights((version) => version + 1);
37472
+ read();
37473
+ });
37474
+ return {
37475
+ win,
37476
+ rowTop: (0, react.useCallback)((index) => enabled ? heights.topAt(index) : Math.max(0, Math.trunc(index)) * rowH, [
37477
+ enabled,
37478
+ heights,
37479
+ rowH
37480
+ ]),
37481
+ rowHeight: (0, react.useCallback)((index) => enabled ? heights.heightAt(index) : rowH, [
37482
+ enabled,
37483
+ heights,
37484
+ rowH
37485
+ ])
37486
+ };
37487
+ }
37488
+ //#endregion
37489
+ //#region src/client/diff-cells.tsx
37490
+ /** Classes that paint only a block's OUTER perimeter. Internal rows carry the
37491
+ * vertical edges but no top/bottom line, avoiding the blue ladder a large
37492
+ * addition block used to draw. Absent side-cells return no class at all. */
37493
+ function blockHotClass(rows, index, side, hot) {
37494
+ if (!hot) return "";
37495
+ const edge = blockEdge(rows, index, side);
37496
+ if (edge === null) return "";
37497
+ const first = edge === "first" || edge === "single" ? ` ${GitWorkbenchPanel_module_css_default.sideBlockHotFirst}` : "";
37498
+ const last = edge === "last" || edge === "single" ? ` ${GitWorkbenchPanel_module_css_default.sideBlockHotLast}` : "";
37499
+ return ` ${GitWorkbenchPanel_module_css_default.sideBlockHot}${first}${last}`;
37500
+ }
37501
+ /** Line-number cell class: a PRESENT cell of a changed row carries its side's
37502
+ * tint into the gutter; an absent one stays blank, the way a split diff shows
37503
+ * a one-sided change with an empty opposite pane rather than a tinted void. */
37504
+ function sideNumClass(row, side) {
37505
+ if ((side === "left" ? row.left : row.right) === null || row.kind === "same") return GitWorkbenchPanel_module_css_default.sideNum;
37506
+ return `${GitWorkbenchPanel_module_css_default.sideNum} ${side === "left" ? GitWorkbenchPanel_module_css_default.sideNumDel : GitWorkbenchPanel_module_css_default.sideNumAdd}`;
37507
+ }
37508
+ /** Code cell class: deletions tint left, additions right, context stays quiet. */
37509
+ function sideCodeClass(row, side) {
37510
+ if ((side === "left" ? row.left : row.right) === null || row.kind === "same") return GitWorkbenchPanel_module_css_default.sideCodeSame;
37511
+ return `${side === "left" ? GitWorkbenchPanel_module_css_default.sideCodeDel : GitWorkbenchPanel_module_css_default.sideCodeAdd} ${GitWorkbenchPanel_module_css_default.sideCellBlock}`;
37512
+ }
37513
+ /** One text with every carriage return drawn as the CR glyph. No CR means
37514
+ * the text comes back untouched — the common line, on both sides, costs one
37515
+ * `includes`. The glyph spans are aria-hidden and unselectable, so copying a
37516
+ * line copies code, not markers. */
37517
+ function renderWithCrMarks(text) {
37518
+ const parts = splitOnCr(text);
37519
+ if (parts.length === 1) return text;
37520
+ const out = [parts[0]];
37521
+ for (let i = 1; i < parts.length; i += 1) {
37522
+ out.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
37523
+ className: GitWorkbenchPanel_module_css_default.crMark,
37524
+ "aria-hidden": "true",
37525
+ children: "␍"
37526
+ }, `cr${i}`));
37527
+ out.push(parts[i]);
37528
+ }
37529
+ return out;
37530
+ }
37531
+ /** One cell's Shiki runs, or its plain text when no tokens exist; either way
37532
+ * each carriage return in the cell is drawn, so a line whose only change is
37533
+ * its ending shows the difference instead of two identical-looking cells. */
37534
+ function renderSideCode(cell, tokens) {
37535
+ if (cell === null) return "";
37536
+ if (tokens === void 0 || tokens.length === 0) return renderWithCrMarks(cell.text);
37537
+ if (tokens.length === 1 && tokens[0].color === void 0 && !tokens[0].italic) return renderWithCrMarks(cell.text);
37538
+ return tokens.map((tok, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
37539
+ style: tok.color === void 0 && !tok.italic ? void 0 : {
37540
+ color: tok.color,
37541
+ fontStyle: tok.italic ? "italic" : void 0
37542
+ },
37543
+ children: renderWithCrMarks(tok.text)
37544
+ }, i));
37545
+ }
37546
+ /**
37547
+ * The spacer standing in for the rows above or below the window.
37548
+ *
37549
+ * It spans every column of the grid, so a blame gutter does not change it.
37550
+ * @param height - px of rows it stands in for; nothing is rendered for 0.
37551
+ */
37552
+ function RowSpacer({ height }) {
37553
+ if (height <= 0) return null;
37554
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
37555
+ className: GitWorkbenchPanel_module_css_default.sideSpacer,
37556
+ style: { height: `${height}px` },
37557
+ "aria-hidden": "true"
37558
+ });
37559
+ }
37560
+ /**
37561
+ * One side of one aligned row: its line-number cell and its code cell.
37562
+ *
37563
+ * Written once and used by all three columns the pane draws — the left side of
37564
+ * a diff, the right side, and the dense index column beside the editor —
37565
+ * because the three differ only in which cell they read, what hangs the block
37566
+ * bar and whether a click arms the editor. Everything else about them, from
37567
+ * the block outline to the CR markers, is the same in all three and was
37568
+ * previously the same three times.
37569
+ */
37570
+ function SideCells({ row, side, index, rows, current, tokens, mark, minHeight, bar, armable, onArm }) {
37571
+ const hot = blockHotClass(rows, index, side, current);
37572
+ const cell = side === "left" ? row.left : row.right;
37573
+ const code = renderSideCode(cell, tokens);
37574
+ const box = minHeight !== void 0 && minHeight > 0 ? { minHeight: `${minHeight}px` } : void 0;
37575
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
37576
+ className: `${sideNumClass(row, side)}${hot}`,
37577
+ style: box,
37578
+ children: cell === null ? "" : cell.line
37579
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
37580
+ className: `${GitWorkbenchPanel_module_css_default.sideCode} ${sideCodeClass(row, side)}${hot}${armable === true ? ` ${GitWorkbenchPanel_module_css_default.sideArmable}` : ""}`,
37581
+ style: box,
37582
+ "data-block": cell !== null && row.block >= 0 ? row.block : void 0,
37583
+ onClick: onArm,
37584
+ children: [mark === void 0 ? code : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
37585
+ className: GitWorkbenchPanel_module_css_default.sideFlow,
37586
+ ...mark,
37587
+ children: code
37588
+ }), bar]
37589
+ })] });
37590
+ }
37591
+ //#endregion
36595
37592
  //#region src/client/DiffViews.tsx
36596
37593
  const SPLIT_MIN = .15;
36597
37594
  const SPLIT_MAX = .85;
@@ -36632,14 +37629,24 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
36632
37629
  * a block child of a scroller is only ever as wide as the scrollport. A header
36633
37630
  * outside the scrolled box has neither problem.
36634
37631
  */
36635
- function DiffView({ segment, path, palette, t }) {
37632
+ function DiffView({ segment, path, palette, t, wrap }) {
36636
37633
  const lang = shikiLangOf(path);
36637
37634
  const shikiTheme = shikiThemeOf(palette);
36638
37635
  const grammarGen = (0, react.useSyncExternalStore)(subscribeGrammarLoaded, grammarLoadCount);
36639
37636
  const rowsWithWords = (0, react.useMemo)(() => attachWordRanges(parseRows(segment)), [segment]);
36640
37637
  const sides = (0, react.useMemo)(() => gutterSides(rowsWithWords), [rowsWithWords]);
36641
37638
  const scrollRef = (0, react.useRef)(null);
36642
- const win = useRowWindow(scrollRef, rowsWithWords.length, path);
37639
+ const preRef = (0, react.useRef)(null);
37640
+ const fixed = useRowWindow(scrollRef, rowsWithWords.length, path, !wrap);
37641
+ const flow = useVariableRowWindow({
37642
+ scrollRef,
37643
+ rowsRef: preRef,
37644
+ texts: (0, react.useMemo)(() => rowsWithWords.map((row) => row.text), [rowsWithWords]),
37645
+ mountKey: path,
37646
+ scope: "u",
37647
+ enabled: wrap
37648
+ });
37649
+ const win = wrap ? flow.win : fixed;
36643
37650
  const syntax = (0, react.useMemo)(() => highlightForRowsWindow(rowsWithWords, lang, shikiTheme, win.start, win.end), [
36644
37651
  rowsWithWords,
36645
37652
  lang,
@@ -36652,7 +37659,9 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
36652
37659
  const changes = (0, react.useMemo)(() => countBlocks(blocks), [blocks]);
36653
37660
  const blocksForNav = (0, react.useRef)(blocks);
36654
37661
  blocksForNav.current = blocks;
36655
- const { goToChange } = useChangeNav(scrollRef, (0, react.useCallback)(() => blockTopsFromRows(blocksForNav.current, 20, 8), []));
37662
+ const placeRow = (0, react.useRef)(void 0);
37663
+ placeRow.current = wrap ? flow.rowTop : void 0;
37664
+ const { goToChange } = useChangeNav(scrollRef, (0, react.useCallback)(() => blockTopsFromRows(blocksForNav.current, 20, 8, placeRow.current), []));
36656
37665
  const walk = (0, react.useRef)(goToChange);
36657
37666
  walk.current = goToChange;
36658
37667
  (0, react.useEffect)(() => {
@@ -36703,7 +37712,8 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
36703
37712
  className: GitWorkbenchPanel_module_css_default.diffScroll,
36704
37713
  tabIndex: -1,
36705
37714
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("pre", {
36706
- className: GitWorkbenchPanel_module_css_default.diffPre,
37715
+ ref: preRef,
37716
+ className: wrap ? `${GitWorkbenchPanel_module_css_default.diffPre} ${GitWorkbenchPanel_module_css_default.diffPreWrap}` : GitWorkbenchPanel_module_css_default.diffPre,
36707
37717
  children: [
36708
37718
  win.padTop > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
36709
37719
  className: GitWorkbenchPanel_module_css_default.diffSpacer,
@@ -36715,6 +37725,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
36715
37725
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
36716
37726
  className: `${GitWorkbenchPanel_module_css_default.line} ${rowClass(row.kind)}`,
36717
37727
  "data-block": blocks[i] >= 0 ? blocks[i] : void 0,
37728
+ ...rowMark("u", i),
36718
37729
  children: [
36719
37730
  sides.old ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
36720
37731
  className: GitWorkbenchPanel_module_css_default.lnOld,
@@ -36799,7 +37810,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
36799
37810
  * (history and compare keep it unconditionally), with a notice — a silently
36800
37811
  * different view reads as a broken one, not a guarded one.
36801
37812
  */
36802
- function SideBySideView({ t, path, palette, statsPath, fetchSides, writeChecked, scopeKey, gen, fallbackSegment, fallbackLoading, onBlockAction, onSaved, onDirtyChange }) {
37813
+ function SideBySideView({ t, path, palette, wrap, statsPath, fetchSides, writeChecked, scopeKey, gen, fallbackSegment, fallbackLoading, onBlockAction, onSaved, onDirtyChange }) {
36803
37814
  const [layer, setLayer] = (0, react.useState)("unstaged");
36804
37815
  const [split, setSplit] = (0, react.useState)(.5);
36805
37816
  const colsRef = (0, react.useRef)(null);
@@ -36898,7 +37909,25 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
36898
37909
  const alignedBlockTops = (0, react.useMemo)(() => blockTopsFromRows(rows.map((row) => row.block), 20, navOffset), [rows, navOffset]);
36899
37910
  const editorBlockTops = (0, react.useMemo)(() => blockTopsFromSideRows(rows, "right", 20, navOffset), [rows, navOffset]);
36900
37911
  const rowWindowKey = `${scopeKey}\x1f${path}\x1f${layer}\x1f${sides?.diffSha ?? ""}`;
36901
- const win = useRowWindow(scrollRef, rows.length, rowWindowKey);
37912
+ const alignedGridRef = (0, react.useRef)(null);
37913
+ const denseGridRef = (0, react.useRef)(null);
37914
+ const leftColRef = (0, react.useRef)(null);
37915
+ const rightColRef = (0, react.useRef)(null);
37916
+ const fixedWin = useRowWindow(scrollRef, rows.length, rowWindowKey, !wrap);
37917
+ const flow = useVariableRowWindow({
37918
+ scrollRef,
37919
+ rowsRef: colsRef,
37920
+ widthRef: alignedGridRef,
37921
+ texts: (0, react.useMemo)(() => rows.map((row) => {
37922
+ const left = row.left?.text ?? "";
37923
+ const right = row.right?.text ?? "";
37924
+ return left.length >= right.length ? left : right;
37925
+ }), [rows]),
37926
+ mountKey: rowWindowKey,
37927
+ scope: "a",
37928
+ enabled: wrap
37929
+ });
37930
+ const win = wrap ? flow.win : fixedWin;
36902
37931
  const leftLines = (0, react.useMemo)(() => rows.map((row) => row.left === null ? "" : row.left.text), [rows]);
36903
37932
  const rightLines = (0, react.useMemo)(() => rows.map((row) => row.right === null ? "" : row.right.text), [rows]);
36904
37933
  const leftSyntax = (0, react.useMemo)(() => highlightWindow(leftLines, lang, shikiTheme, win.start, win.end), [
@@ -36949,7 +37978,16 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
36949
37978
  row,
36950
37979
  i
36951
37980
  })).filter((entry) => entry.row.left !== null), [rows]);
36952
- const leftWin = useRowWindow(scrollRef, leftRows.length, rowWindowKey);
37981
+ const fixedLeftWin = useRowWindow(scrollRef, leftRows.length, rowWindowKey, !wrap);
37982
+ const leftFlow = useVariableRowWindow({
37983
+ scrollRef,
37984
+ rowsRef: denseGridRef,
37985
+ texts: (0, react.useMemo)(() => leftRows.map((entry) => entry.row.left?.text ?? ""), [leftRows]),
37986
+ mountKey: rowWindowKey,
37987
+ scope: "d",
37988
+ enabled: wrap
37989
+ });
37990
+ const leftWin = wrap ? leftFlow.win : fixedLeftWin;
36953
37991
  /** Arm the editor from the payload on screen; the unstaged tab, and only
36954
37992
  * for a payload `editableSides` accepts — armEdit itself refuses the rest,
36955
37993
  * so even a stray call cannot put CRLF text into the buffer. */
@@ -37144,7 +38182,8 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
37144
38182
  segment: fallbackSegment,
37145
38183
  path,
37146
38184
  palette,
37147
- t
38185
+ t,
38186
+ wrap
37148
38187
  }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
37149
38188
  className: GitWorkbenchPanel_module_css_default.empty,
37150
38189
  children: fallbackLoading ? t("loadingDiff") : t("noTextDiff")
@@ -37370,7 +38409,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
37370
38409
  children: bodyState.kind === "empty" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
37371
38410
  className: GitWorkbenchPanel_module_css_default.empty,
37372
38411
  children: t("noTextDiff")
37373
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
38412
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
37374
38413
  ref: colsRef,
37375
38414
  className: GitWorkbenchPanel_module_css_default.sideCols,
37376
38415
  onMouseOver: onBodyHover,
@@ -37380,29 +38419,31 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
37380
38419
  },
37381
38420
  children: [
37382
38421
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
37383
- className: GitWorkbenchPanel_module_css_default.sideCol,
38422
+ ref: leftColRef,
38423
+ className: wrap ? `${GitWorkbenchPanel_module_css_default.sideCol} ${GitWorkbenchPanel_module_css_default.sideColWrap}` : GitWorkbenchPanel_module_css_default.sideCol,
37384
38424
  style: {
37385
38425
  flexBasis: `${split * 100}%`,
37386
38426
  paddingTop: blockBarClearance
37387
38427
  },
37388
38428
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
37389
- className: GitWorkbenchPanel_module_css_default.sideColGrid,
38429
+ ref: bodyState.kind === "editor" ? denseGridRef : alignedGridRef,
38430
+ className: wrap ? `${GitWorkbenchPanel_module_css_default.sideColGrid} ${GitWorkbenchPanel_module_css_default.sideColGridWrap}` : GitWorkbenchPanel_module_css_default.sideColGrid,
37390
38431
  children: bodyState.kind === "editor" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
37391
38432
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RowSpacer, { height: leftWin.padTop }),
37392
38433
  leftRows.slice(leftWin.start, leftWin.end).map((entry, kk) => {
37393
38434
  const k = leftWin.start + kk;
37394
38435
  const { row, i } = entry;
37395
38436
  const hot = hotBlock !== null && row.block === hotBlock;
37396
- const current = row.block >= 0 && row.block === currentBlock;
37397
- const hotClass = blockHotClass(rows, i, "left", current);
37398
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
37399
- className: `${sideNumClass(row, "left")}${hotClass}`,
37400
- children: row.left.line
37401
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
37402
- className: `${GitWorkbenchPanel_module_css_default.sideCode} ${sideCodeClass(row, "left")}${hotClass}`,
37403
- "data-block": row.left !== null && row.block >= 0 ? row.block : void 0,
37404
- children: [renderSideCode(row.left, leftSyntax?.[i]), hot && k === hotFirstLeft ? blockBar(row.block) : null]
37405
- })] }, `l${i}`);
38437
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SideCells, {
38438
+ row,
38439
+ side: "left",
38440
+ index: i,
38441
+ rows,
38442
+ current: row.block >= 0 && row.block === currentBlock,
38443
+ tokens: leftSyntax?.[i],
38444
+ mark: wrap ? rowMark("d", k) : void 0,
38445
+ bar: hot && k === hotFirstLeft ? blockBar(row.block) : null
38446
+ }, `l${i}`);
37406
38447
  }),
37407
38448
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RowSpacer, { height: leftWin.padBottom })
37408
38449
  ] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
@@ -37410,17 +38451,17 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
37410
38451
  rows.slice(win.start, win.end).map((row, k) => {
37411
38452
  const i = win.start + k;
37412
38453
  const hot = hotBlock !== null && row.block === hotBlock;
37413
- const current = row.block >= 0 && row.block === currentBlock;
37414
- const hotClass = blockHotClass(rows, i, "left", current);
37415
- const bar = hot && i === hotFirst && row.right === null ? blockBar(row.block) : null;
37416
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
37417
- className: `${sideNumClass(row, "left")}${hotClass}`,
37418
- children: row.left === null ? "" : row.left.line
37419
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
37420
- className: `${GitWorkbenchPanel_module_css_default.sideCode} ${sideCodeClass(row, "left")}${hotClass}`,
37421
- "data-block": row.left !== null && row.block >= 0 ? row.block : void 0,
37422
- children: [renderSideCode(row.left, leftSyntax?.[i]), bar]
37423
- })] }, i);
38454
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SideCells, {
38455
+ row,
38456
+ side: "left",
38457
+ index: i,
38458
+ rows,
38459
+ current: row.block >= 0 && row.block === currentBlock,
38460
+ tokens: leftSyntax?.[i],
38461
+ mark: wrap ? rowMark("a", i) : void 0,
38462
+ minHeight: wrap ? flow.rowHeight(i) : void 0,
38463
+ bar: hot && i === hotFirst && row.right === null ? blockBar(row.block) : null
38464
+ }, i);
37424
38465
  }),
37425
38466
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RowSpacer, { height: win.padBottom })
37426
38467
  ] })
@@ -37431,7 +38472,8 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
37431
38472
  onDrag: onSplitDrag
37432
38473
  }),
37433
38474
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
37434
- className: `${GitWorkbenchPanel_module_css_default.sideCol} ${GitWorkbenchPanel_module_css_default.sideColRight}`,
38475
+ ref: rightColRef,
38476
+ className: wrap ? `${GitWorkbenchPanel_module_css_default.sideCol} ${GitWorkbenchPanel_module_css_default.sideColRight} ${GitWorkbenchPanel_module_css_default.sideColWrap}` : `${GitWorkbenchPanel_module_css_default.sideCol} ${GitWorkbenchPanel_module_css_default.sideColRight}`,
37435
38477
  style: { paddingTop: blockBarClearance },
37436
38478
  children: bodyState.kind === "editor" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CodeEditor, {
37437
38479
  value: edit.buffer,
@@ -37447,33 +38489,39 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
37447
38489
  ariaLabel: path,
37448
38490
  onSave: () => {
37449
38491
  if (dirty && !saving) runSave(edit.baseSha);
37450
- }
38492
+ },
38493
+ wrap
37451
38494
  }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
37452
- className: GitWorkbenchPanel_module_css_default.sideColGrid,
38495
+ className: wrap ? `${GitWorkbenchPanel_module_css_default.sideColGrid} ${GitWorkbenchPanel_module_css_default.sideColGridWrap}` : GitWorkbenchPanel_module_css_default.sideColGrid,
37453
38496
  children: [
37454
38497
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RowSpacer, { height: win.padTop }),
37455
38498
  rows.slice(win.start, win.end).map((row, k) => {
37456
38499
  const i = win.start + k;
37457
38500
  const hot = hotBlock !== null && row.block === hotBlock;
37458
- const current = row.block >= 0 && row.block === currentBlock;
37459
- const hotClass = blockHotClass(rows, i, "right", current);
37460
- const bar = hot && i === hotFirst && row.right !== null ? blockBar(row.block) : null;
37461
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
37462
- className: `${sideNumClass(row, "right")}${hotClass}`,
37463
- children: row.right === null ? "" : row.right.line
37464
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
37465
- className: `${GitWorkbenchPanel_module_css_default.sideCode} ${sideCodeClass(row, "right")}${hotClass}${layer === "unstaged" && armable ? ` ${GitWorkbenchPanel_module_css_default.sideArmable}` : ""}`,
37466
- "data-block": row.right !== null && row.block >= 0 ? row.block : void 0,
37467
- onClick: layer === "unstaged" && armable ? armFromCell : void 0,
37468
- children: [renderSideCode(row.right, rightSyntax?.[i]), bar]
37469
- })] }, i);
38501
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SideCells, {
38502
+ row,
38503
+ side: "right",
38504
+ index: i,
38505
+ rows,
38506
+ current: row.block >= 0 && row.block === currentBlock,
38507
+ tokens: rightSyntax?.[i],
38508
+ mark: wrap ? rowMark("a", i) : void 0,
38509
+ minHeight: wrap ? flow.rowHeight(i) : void 0,
38510
+ bar: hot && i === hotFirst && row.right !== null ? blockBar(row.block) : null,
38511
+ armable: layer === "unstaged" && armable,
38512
+ onArm: layer === "unstaged" && armable ? armFromCell : void 0
38513
+ }, i);
37470
38514
  }),
37471
38515
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RowSpacer, { height: win.padBottom })
37472
38516
  ]
37473
38517
  })
37474
38518
  })
37475
38519
  ]
37476
- })
38520
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SideRails, {
38521
+ leftRef: leftColRef,
38522
+ rightRef: rightColRef,
38523
+ split
38524
+ })] })
37477
38525
  }),
37478
38526
  pendingLayer !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LeaveEditsConfirm, {
37479
38527
  t,
@@ -37547,76 +38595,6 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
37547
38595
  })
37548
38596
  });
37549
38597
  }
37550
- /** Classes that paint only a block's OUTER perimeter. Internal rows carry the
37551
- * vertical edges but no top/bottom line, avoiding the blue ladder a large
37552
- * addition block used to draw. Absent side-cells return no class at all. */
37553
- function blockHotClass(rows, index, side, hot) {
37554
- if (!hot) return "";
37555
- const edge = blockEdge(rows, index, side);
37556
- if (edge === null) return "";
37557
- const first = edge === "first" || edge === "single" ? ` ${GitWorkbenchPanel_module_css_default.sideBlockHotFirst}` : "";
37558
- const last = edge === "last" || edge === "single" ? ` ${GitWorkbenchPanel_module_css_default.sideBlockHotLast}` : "";
37559
- return ` ${GitWorkbenchPanel_module_css_default.sideBlockHot}${first}${last}`;
37560
- }
37561
- /** Line-number cell class: a PRESENT cell of a changed row carries its side's
37562
- * tint into the gutter; an absent one stays blank, the way a split diff shows
37563
- * a one-sided change with an empty opposite pane rather than a tinted void. */
37564
- function sideNumClass(row, side) {
37565
- if ((side === "left" ? row.left : row.right) === null || row.kind === "same") return GitWorkbenchPanel_module_css_default.sideNum;
37566
- return `${GitWorkbenchPanel_module_css_default.sideNum} ${side === "left" ? GitWorkbenchPanel_module_css_default.sideNumDel : GitWorkbenchPanel_module_css_default.sideNumAdd}`;
37567
- }
37568
- /** Code cell class: deletions tint left, additions right, context stays quiet. */
37569
- function sideCodeClass(row, side) {
37570
- if ((side === "left" ? row.left : row.right) === null || row.kind === "same") return GitWorkbenchPanel_module_css_default.sideCodeSame;
37571
- return `${side === "left" ? GitWorkbenchPanel_module_css_default.sideCodeDel : GitWorkbenchPanel_module_css_default.sideCodeAdd} ${GitWorkbenchPanel_module_css_default.sideCellBlock}`;
37572
- }
37573
- /** One text with every carriage return drawn as the CR glyph. No CR means
37574
- * the text comes back untouched — the common line, on both sides, costs one
37575
- * `includes`. The glyph spans are aria-hidden and unselectable, so copying a
37576
- * line copies code, not markers. */
37577
- function renderWithCrMarks(text) {
37578
- const parts = splitOnCr(text);
37579
- if (parts.length === 1) return text;
37580
- const out = [parts[0]];
37581
- for (let i = 1; i < parts.length; i += 1) {
37582
- out.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
37583
- className: GitWorkbenchPanel_module_css_default.crMark,
37584
- "aria-hidden": "true",
37585
- children: "␍"
37586
- }, `cr${i}`));
37587
- out.push(parts[i]);
37588
- }
37589
- return out;
37590
- }
37591
- /** One cell's Shiki runs, or its plain text when no tokens exist; either way
37592
- * each carriage return in the cell is drawn, so a line whose only change is
37593
- * its ending shows the difference instead of two identical-looking cells. */
37594
- function renderSideCode(cell, tokens) {
37595
- if (cell === null) return "";
37596
- if (tokens === void 0 || tokens.length === 0) return renderWithCrMarks(cell.text);
37597
- if (tokens.length === 1 && tokens[0].color === void 0 && !tokens[0].italic) return renderWithCrMarks(cell.text);
37598
- return tokens.map((tok, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
37599
- style: tok.color === void 0 && !tok.italic ? void 0 : {
37600
- color: tok.color,
37601
- fontStyle: tok.italic ? "italic" : void 0
37602
- },
37603
- children: renderWithCrMarks(tok.text)
37604
- }, i));
37605
- }
37606
- /**
37607
- * The spacer standing in for the rows above or below the window.
37608
- *
37609
- * It spans every column of the grid, so a blame gutter does not change it.
37610
- * @param height - px of rows it stands in for; nothing is rendered for 0.
37611
- */
37612
- function RowSpacer({ height }) {
37613
- if (height <= 0) return null;
37614
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
37615
- className: GitWorkbenchPanel_module_css_default.sideSpacer,
37616
- style: { height: `${height}px` },
37617
- "aria-hidden": "true"
37618
- });
37619
- }
37620
38598
  //#endregion
37621
38599
  //#region src/client/ChromeGlyph.tsx
37622
38600
  /**
@@ -37632,7 +38610,8 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
37632
38610
  maximize: "M1.5 1a.5.5 0 0 0-.5.5v4a.5.5 0 0 1-1 0v-4A1.5 1.5 0 0 1 1.5 0h4a.5.5 0 0 1 0 1h-4zM10 .5a.5.5 0 0 1 .5-.5h4A1.5 1.5 0 0 1 16 1.5v4a.5.5 0 0 1-1 0v-4a.5.5 0 0 0-.5-.5h-4a.5.5 0 0 1-.5-.5zM.5 10a.5.5 0 0 1 .5.5v4a.5.5 0 0 0 .5.5h4a.5.5 0 0 1 0 1h-4A1.5 1.5 0 0 1 0 14.5v-4a.5.5 0 0 1 .5-.5zm15 0a.5.5 0 0 1 .5.5v4a1.5 1.5 0 0 1-1.5 1.5h-4a.5.5 0 0 1 0-1h4a.5.5 0 0 0 .5-.5v-4a.5.5 0 0 1 .5-.5z",
37633
38611
  restore: "M5.5 0a.5.5 0 0 1 .5.5v4A1.5 1.5 0 0 1 4.5 6h-4a.5.5 0 0 1 0-1h4a.5.5 0 0 0 .5-.5v-4a.5.5 0 0 1 .5-.5zm5 0a.5.5 0 0 1 .5.5v4a.5.5 0 0 0 .5.5h4a.5.5 0 0 1 0 1h-4A1.5 1.5 0 0 1 10 4.5v-4a.5.5 0 0 1 .5-.5zM0 10.5a.5.5 0 0 1 .5-.5h4A1.5 1.5 0 0 1 6 11.5v4a.5.5 0 0 1-1 0v-4a.5.5 0 0 0-.5-.5h-4a.5.5 0 0 1-.5-.5zm10 1a1.5 1.5 0 0 1 1.5-1.5h4a.5.5 0 0 1 0 1h-4a.5.5 0 0 0-.5.5v4a.5.5 0 0 1-1 0v-4z",
37634
38612
  refresh: "M8 3a5 5 0 1 0 4.546 2.914.5.5 0 0 1 .908-.417A6 6 0 1 1 8 2v1z M8 4.466V.534a.25.25 0 0 1 .41-.192l2.36 1.966c.12.1.12.284 0 .384L8.41 4.658A.25.25 0 0 1 8 4.466z",
37635
- close: "M2.146 2.854a.5.5 0 1 1 .708-.708L8 7.293l5.146-5.147a.5.5 0 0 1 .708.708L8.707 8l5.147 5.146a.5.5 0 0 1-.708.708L8 8.707l-5.146 5.147a.5.5 0 0 1-.708-.708L7.293 8 2.146 2.854Z"
38613
+ close: "M2.146 2.854a.5.5 0 1 1 .708-.708L8 7.293l5.146-5.147a.5.5 0 0 1 .708.708L8.707 8l5.147 5.146a.5.5 0 0 1-.708.708L8 8.707l-5.146 5.147a.5.5 0 0 1-.708-.708L7.293 8 2.146 2.854Z",
38614
+ wrap: "M1 3.5a.5.5 0 0 1 .5-.5h13a.5.5 0 0 1 0 1h-13a.5.5 0 0 1-.5-.5zM1 7.5a.5.5 0 0 1 .5-.5h11a2.5 2.5 0 0 1 0 5h-2.293l1.147 1.146a.5.5 0 0 1-.708.708l-2-2a.5.5 0 0 1 0-.708l2-2a.5.5 0 0 1 .708.708L10.207 11H12.5a1.5 1.5 0 0 0 0-3h-11a.5.5 0 0 1-.5-.5zM1 12.5a.5.5 0 0 1 .5-.5h4a.5.5 0 0 1 0 1h-4a.5.5 0 0 1-.5-.5z"
37636
38615
  };
37637
38616
  function ChromeGlyph({ of }) {
37638
38617
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
@@ -39251,6 +40230,10 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
39251
40230
  * field of the appearance object: that one is about colour, and this choice
39252
40231
  * has to survive a build that adds a palette. */
39253
40232
  const STORE_HISTORY_LAYOUT = "dsh-ui-gitworkbench:history-layout";
40233
+ /** Soft wrap, on or off. A reading preference rather than a project setting:
40234
+ * it says how THIS person wants long lines shown, so it lives beside the
40235
+ * pane sizes in localStorage rather than in the shared style store. */
40236
+ const STORE_WRAP = "dsh-ui-gitworkbench:wrap";
39254
40237
  const DEFAULT_PANES = {
39255
40238
  commits: null,
39256
40239
  tree: null,
@@ -39320,7 +40303,10 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
39320
40303
  * unvisited worktree does not re-render the browser on every pass. */
39321
40304
  const EMPTY_TREE = {
39322
40305
  paths: [],
39323
- truncated: false
40306
+ truncated: false,
40307
+ ignored: [],
40308
+ ignoredTruncated: false,
40309
+ children: NO_IGNORED_READS
39324
40310
  };
39325
40311
  /** The overlay with nothing on it — one instance, so an empty overlay never
39326
40312
  * re-renders the tree that receives it. */
@@ -39346,7 +40332,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
39346
40332
  for (const preferred of ["main", "master"]) if (branches.includes(preferred) && preferred !== head) return preferred;
39347
40333
  return branches.find((branch) => branch !== head) ?? "";
39348
40334
  }
39349
- function GitWorkbenchPanel({ sessionId, useSessions, t, fetchStats, fetchFileDiff, fetchFileSides, writeChecked, fetchBlame, fetchFileImage, fetchWorktreeStatus, fetchSessionBinding, fetchCommitStats, fetchCommits, fetchAuthors, fetchRepoTree, fetchCompare, fetchStyle, saveStyle, fetchSync, runGitOp, fetchDiscardPlan }) {
40335
+ function GitWorkbenchPanel({ sessionId, useSessions, t, fetchStats, fetchFileDiff, fetchFileSides, writeChecked, fetchBlame, fetchFileImage, fetchWorktreeStatus, fetchSessionBinding, fetchCommitStats, fetchCommits, fetchAuthors, fetchRepoTree, fetchIgnoredDir, fetchCompare, fetchStyle, saveStyle, fetchSync, runGitOp, fetchDiscardPlan }) {
39350
40336
  const worktreePath = useSessions((state) => state?.byId?.[sessionId]?.cwd);
39351
40337
  /** Whether the session's agent has a turn in flight — the store mirrors it
39352
40338
  * live, so it is the signal for polling faster while there is something to
@@ -39367,10 +40353,10 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
39367
40353
  const rememberPlace = (0, react.useCallback)((key, next) => {
39368
40354
  setFilesPlaces((prev) => withPlace(prev, key, next));
39369
40355
  }, []);
39370
- const rememberTree = (0, react.useCallback)((key, next) => {
40356
+ const rememberTree = (0, react.useCallback)((key, update) => {
39371
40357
  setFilesTrees((prev) => {
39372
40358
  const map = new Map(prev);
39373
- map.set(key, next);
40359
+ map.set(key, update(prev.get(key) ?? EMPTY_TREE));
39374
40360
  return map;
39375
40361
  });
39376
40362
  }, []);
@@ -39443,6 +40429,10 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
39443
40429
  * the row height it implies is published from.
39444
40430
  */
39445
40431
  const [historyLayout, setHistoryLayout] = (0, react.useState)(() => readStored(STORE_HISTORY_LAYOUT, isHistoryLayout, DEFAULT_HISTORY_LAYOUT));
40432
+ /** Soft wrap. Default OFF, which is what the panes have always done: code is
40433
+ * written in columns, and wrapping it is a choice about one long file, not
40434
+ * a better default for every file. */
40435
+ const [wrap, setWrap] = (0, react.useState)(() => readStored(STORE_WRAP, (value) => typeof value === "boolean", false));
39446
40436
  /** Per-project and global styling; both scopes, unresolved. */
39447
40437
  const [style, setStyle] = (0, react.useState)(EMPTY_SETTINGS);
39448
40438
  /** Whether dsh's resolved palette is currently dark. */
@@ -40113,6 +41103,21 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
40113
41103
  setHistoryLayout(next);
40114
41104
  writeStored(STORE_HISTORY_LAYOUT, next);
40115
41105
  };
41106
+ /**
41107
+ * Turn soft wrap on or off, and remember it.
41108
+ *
41109
+ * A plain function, like every other handler below the two guards above:
41110
+ * those `return null`s are the reason nothing past this point may be a HOOK.
41111
+ * A `useCallback` here rendered fewer hooks than the previous pass on the
41112
+ * frame the first stats arrived, which React reports as error #310 and the
41113
+ * shell reports as "slot entry crashed" — the chip simply vanishes.
41114
+ */
41115
+ const toggleWrap = () => {
41116
+ setWrap((prev) => {
41117
+ writeStored(STORE_WRAP, !prev);
41118
+ return !prev;
41119
+ });
41120
+ };
40116
41121
  /** Tab switch. No direction refetches the working tree: `viewKey` already
40117
41122
  * separates the tabs' per-file diff caches, so bumping `gen` here only cost a
40118
41123
  * redundant round trip. */
@@ -40212,6 +41217,8 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
40212
41217
  onHistoryLayout: applyHistoryLayout,
40213
41218
  onClose: () => setOpen(false),
40214
41219
  onRefresh: refresh,
41220
+ wrap,
41221
+ onToggleWrap: toggleWrap,
40215
41222
  commitDraft,
40216
41223
  onCommitDraft: setCommitDraft,
40217
41224
  commitAmend,
@@ -40229,6 +41236,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
40229
41236
  writeChecked,
40230
41237
  fetchBlame,
40231
41238
  fetchFileImage,
41239
+ fetchIgnoredDir,
40232
41240
  viewKey,
40233
41241
  gen,
40234
41242
  collapsed,
@@ -40290,7 +41298,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
40290
41298
  ]
40291
41299
  });
40292
41300
  }
40293
- 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, onCommitsTall, historyLayout, onHistoryLayout, onClose, onRefresh, commitDraft, onCommitDraft, commitAmend, onCommitAmend, sync, treeLoading, historyLoading, busy, opResult, runOp, fetchDiscardPlan, onOpError, pendingTicks, onTick, fetchFileDiff, fetchFileSides, writeChecked, fetchBlame, fetchFileImage, viewKey, gen, collapsed, onCollapsedChange, filesPlaces, onFilesPlace, filesTrees, onFilesTree }) {
41301
+ function Drawer({ stats, shown, tab, onSwitchTab, commits, commitHash, onSelectCommit, hasMoreCommits, loadingMore, onLoadMoreCommits, historyRef, onHistoryRef, historyQuery, onHistoryQuery, historyError, fetchAuthors, fetchRepoTree, fetchIgnoredDir, 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, onCommitsTall, historyLayout, onHistoryLayout, onClose, onRefresh, wrap, onToggleWrap, commitDraft, onCommitDraft, commitAmend, onCommitAmend, sync, treeLoading, historyLoading, busy, opResult, runOp, fetchDiscardPlan, onOpError, pendingTicks, onTick, fetchFileDiff, fetchFileSides, writeChecked, fetchBlame, fetchFileImage, viewKey, gen, collapsed, onCollapsedChange, filesPlaces, onFilesPlace, filesTrees, onFilesTree }) {
40294
41302
  const body = shown ?? EMPTY_STATS;
40295
41303
  /** The file list with ticks still awaiting git laid over them. The tree and
40296
41304
  * the commit box read this, so a click moves its box and the "N ticked"
@@ -40315,8 +41323,8 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
40315
41323
  const rememberPlaceHere = (0, react.useCallback)((next) => {
40316
41324
  onFilesPlace(filesKey, next);
40317
41325
  }, [onFilesPlace, filesKey]);
40318
- const rememberTreeHere = (0, react.useCallback)((next) => {
40319
- onFilesTree(filesKey, next);
41326
+ const rememberTreeHere = (0, react.useCallback)((update) => {
41327
+ onFilesTree(filesKey, update);
40320
41328
  }, [onFilesTree, filesKey]);
40321
41329
  const browsablePaths = (0, react.useMemo)(() => stats.files.filter((file) => file.status !== "deleted").map((file) => file.path), [stats.files]);
40322
41330
  const active = preferredFile(body.files, activeFilterPaths, selected);
@@ -40683,6 +41691,15 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
40683
41691
  onClick: onToggleMaximized,
40684
41692
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChromeGlyph, { of: maximized ? "restore" : "maximize" })
40685
41693
  }),
41694
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
41695
+ type: "button",
41696
+ className: wrap ? `${GitWorkbenchPanel_module_css_default.btn} ${GitWorkbenchPanel_module_css_default.btnIcon} ${GitWorkbenchPanel_module_css_default.btnIconOn}` : `${GitWorkbenchPanel_module_css_default.btn} ${GitWorkbenchPanel_module_css_default.btnIcon}`,
41697
+ "aria-pressed": wrap,
41698
+ "aria-label": wrap ? t("wrapLinesOff") : t("wrapLines"),
41699
+ title: wrap ? t("wrapLinesOff") : t("wrapLines"),
41700
+ onClick: onToggleWrap,
41701
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChromeGlyph, { of: "wrap" })
41702
+ }),
40686
41703
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
40687
41704
  type: "button",
40688
41705
  className: `${GitWorkbenchPanel_module_css_default.btn} ${GitWorkbenchPanel_module_css_default.btnIcon}`,
@@ -40841,7 +41858,9 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
40841
41858
  onPlace: rememberPlaceHere,
40842
41859
  cached: filesTrees.get(filesKey) ?? EMPTY_TREE,
40843
41860
  onTree: rememberTreeHere,
41861
+ wrap,
40844
41862
  fetchRepoTree,
41863
+ fetchIgnoredDir,
40845
41864
  fetchFileSides,
40846
41865
  writeChecked,
40847
41866
  fetchBlame,
@@ -40913,6 +41932,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
40913
41932
  t,
40914
41933
  path: active,
40915
41934
  palette: theme,
41935
+ wrap,
40916
41936
  statsPath,
40917
41937
  fetchSides: fetchFileSides,
40918
41938
  writeChecked,
@@ -40930,7 +41950,8 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
40930
41950
  segment,
40931
41951
  path: active ?? "",
40932
41952
  palette: theme,
40933
- t
41953
+ t,
41954
+ wrap
40934
41955
  }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
40935
41956
  className: GitWorkbenchPanel_module_css_default.empty,
40936
41957
  children: t("noTextDiff")
@@ -40998,6 +42019,11 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
40998
42019
  tabFiles: "文件",
40999
42020
  fileSearchPlaceholder: "搜索文件…",
41000
42021
  filesTruncated: "文件太多,列表已截断;用搜索找剩下的。",
42022
+ filesIgnored: "已被 gitignore",
42023
+ wrapLines: "自动换行",
42024
+ wrapLinesOff: "取消自动换行",
42025
+ filesIgnoredCut: "这个目录条目太多,已截断;搜索找不到没列出的部分。",
42026
+ filesIgnoredFailed: "无法列出被忽略的文件。",
41001
42027
  filesEmpty: "这个仓库还没有文件",
41002
42028
  filesNoMatch: "没有匹配的文件",
41003
42029
  filesMore: "还有 {count} 个,用上面的搜索找",
@@ -41212,6 +42238,11 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
41212
42238
  tabFiles: "Files",
41213
42239
  fileSearchPlaceholder: "Search files…",
41214
42240
  filesTruncated: "Too many files to list; use the search for the rest.",
42241
+ filesIgnored: "Ignored (gitignore)",
42242
+ wrapLines: "Wrap long lines",
42243
+ wrapLinesOff: "Stop wrapping lines",
42244
+ filesIgnoredCut: "This directory has too many entries; the list is cut, and the search cannot reach the rest.",
42245
+ filesIgnoredFailed: "Could not list the ignored files.",
41215
42246
  filesEmpty: "This repository has no files yet",
41216
42247
  filesNoMatch: "No matching files",
41217
42248
  filesMore: "and {count} more — use the search above",
@@ -41516,6 +42547,17 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
41516
42547
  const result = await connection.rpc.call("/api", "gitWorkbench/repoTree", { args: { worktreePath: worktreePath ?? "" } }, signal);
41517
42548
  return result.ok ? result.value : null;
41518
42549
  },
42550
+ fetchIgnoredDir: async (worktreePath, dir, signal) => {
42551
+ try {
42552
+ const result = await connection.rpc.call("/api", "gitWorkbench/ignoredDir", { args: {
42553
+ worktreePath: worktreePath ?? "",
42554
+ dir
42555
+ } }, signal);
42556
+ return result.ok ? result.value : null;
42557
+ } catch {
42558
+ return null;
42559
+ }
42560
+ },
41519
42561
  fetchCompare: async (worktreePath, base, head, signal) => {
41520
42562
  const result = await connection.rpc.call("/api", "gitWorkbench/compareRefs", { args: {
41521
42563
  worktreePath: worktreePath ?? "",