@young1lin/dsh-ui-gitworkbench 0.1.15 → 0.1.17

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 (42) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/CHANGELOG_EN.md +26 -0
  3. package/README.md +30 -5
  4. package/README_EN.md +1 -1
  5. package/lib/client.js +1600 -519
  6. package/lib/dir-listing.js +34 -0
  7. package/lib/fs-remove.js +5 -36
  8. package/lib/index.js +233 -52
  9. package/lib/path-lock.js +54 -0
  10. package/lib/worktree.js +133 -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 +122 -123
  16. package/src/client/FileBrowser.tsx +196 -23
  17. package/src/client/GitWorkbenchPanel.module.css +1 -0
  18. package/src/client/GitWorkbenchPanel.tsx +114 -12
  19. package/src/client/SideRails.tsx +106 -0
  20. package/src/client/diff-cells.tsx +147 -0
  21. package/src/client/diff-model.ts +20 -0
  22. package/src/client/diff-nav.ts +4 -1
  23. package/src/client/dir-tree.ts +31 -1
  24. package/src/client/file-rows.ts +40 -0
  25. package/src/client/h-rail.ts +70 -0
  26. package/src/client/ignored-cache.ts +193 -0
  27. package/src/client/index.ts +22 -3
  28. package/src/client/locales.ts +18 -4
  29. package/src/client/row-heights.ts +225 -0
  30. package/src/client/styles/changes.css +33 -2
  31. package/src/client/styles/controls.css +5 -0
  32. package/src/client/styles/files.css +5 -0
  33. package/src/client/styles/rails.css +72 -0
  34. package/src/client/use-row-window.ts +7 -3
  35. package/src/client/use-variable-row-window.ts +210 -0
  36. package/src/dir-listing.ts +47 -0
  37. package/src/fs-remove.ts +5 -36
  38. package/src/index.ts +257 -55
  39. package/src/path-lock.ts +56 -0
  40. package/src/types/dsh-shim.d.ts +12 -2
  41. package/src/worktree.ts +153 -0
  42. 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",
472
+ "themeRowSplit": "SD8qLW_themeRowSplit",
473
+ "scopeBtnActive": "SD8qLW_scopeBtnActive",
477
474
  "fbPath": "SD8qLW_fbPath",
478
- "funnel": "SD8qLW_funnel",
479
- "funnelCaption": "SD8qLW_funnelCaption",
480
- "sideColRight": "SD8qLW_sideColRight",
481
- "treeIconDown": "SD8qLW_treeIconDown",
482
- "cal": "SD8qLW_cal",
475
+ "commitActive": "SD8qLW_commitActive",
476
+ "fbRow": "SD8qLW_fbRow",
477
+ "lineDel": "SD8qLW_lineDel",
478
+ "cmHost": "SD8qLW_cmHost",
483
479
  "funnelBoundClear": "SD8qLW_funnelBoundClear",
484
- "wordDel": "SD8qLW_wordDel",
485
- "calMark": "SD8qLW_calMark",
480
+ "chevron": "SD8qLW_chevron",
481
+ "headerViewRef": "SD8qLW_headerViewRef",
482
+ "cardDeleted": "SD8qLW_cardDeleted",
483
+ "headerTotalsAdd": "SD8qLW_headerTotalsAdd",
484
+ "commitHasBody": "SD8qLW_commitHasBody",
485
+ "funnelPresetActive": "SD8qLW_funnelPresetActive",
486
+ "blameCell": "SD8qLW_blameCell",
487
+ "sideCodeAdd": "SD8qLW_sideCodeAdd",
488
+ "fileCounts": "SD8qLW_fileCounts",
489
+ "lineHunk": "SD8qLW_lineHunk",
486
490
  "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",
493
- "imgPane": "SD8qLW_imgPane",
494
- "calIn": "SD8qLW_calIn",
495
- "diffNav": "SD8qLW_diffNav",
496
- "pathNode": "SD8qLW_pathNode",
497
- "calGrid": "SD8qLW_calGrid",
498
- "commitPopSubject": "SD8qLW_commitPopSubject",
491
+ "headerView": "SD8qLW_headerView",
492
+ "signAdd": "SD8qLW_signAdd",
493
+ "sideTabs": "SD8qLW_sideTabs",
494
+ "funnelBounds": "SD8qLW_funnelBounds",
499
495
  "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",
510
- "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
496
  "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",
497
+ "cardGlyph": "SD8qLW_cardGlyph",
498
+ "commitPopMeta": "SD8qLW_commitPopMeta",
499
+ "cal": "SD8qLW_cal",
500
+ "imgCaption": "SD8qLW_imgCaption",
558
501
  "fbList": "SD8qLW_fbList",
559
- "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",
568
- "fileLi": "SD8qLW_fileLi",
569
- "calHead": "SD8qLW_calHead",
570
- "gsFade": "SD8qLW_gsFade",
571
- "funnelChevron": "SD8qLW_funnelChevron",
572
- "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",
582
- "chipDark": "SD8qLW_chipDark",
583
- "cardFiles": "SD8qLW_cardFiles",
584
- "paneDivider": "SD8qLW_paneDivider",
585
- "themeRowSplit": "SD8qLW_themeRowSplit",
586
- "pathChildren": "SD8qLW_pathChildren",
587
- "fbCommitHash": "SD8qLW_fbCommitHash",
588
- "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",
502
+ "sidePane": "SD8qLW_sidePane",
503
+ "treeActions": "SD8qLW_treeActions",
504
+ "overlay": "SD8qLW_overlay",
505
+ "funnel": "SD8qLW_funnel",
506
+ "funnelTabCount": "SD8qLW_funnelTabCount",
507
+ "compareArrow": "SD8qLW_compareArrow",
599
508
  "funnelName": "SD8qLW_funnelName",
600
- "fbSearch": "SD8qLW_fbSearch",
601
- "fileBinary": "SD8qLW_fileBinary",
602
- "opBannerBad": "SD8qLW_opBannerBad",
509
+ "sideCols": "SD8qLW_sideCols",
510
+ "funnelSearch": "SD8qLW_funnelSearch",
511
+ "treeIcon": "SD8qLW_treeIcon",
512
+ "btnClose": "SD8qLW_btnClose",
513
+ "bgEmpty": "SD8qLW_bgEmpty",
514
+ "treeDirCounts": "SD8qLW_treeDirCounts",
515
+ "layoutGlyph": "SD8qLW_layoutGlyph",
516
+ "menuPop": "SD8qLW_menuPop",
517
+ "refButton": "SD8qLW_refButton",
518
+ "funnelPresets": "SD8qLW_funnelPresets",
519
+ "bodyRow": "SD8qLW_bodyRow",
520
+ "sideNavCount": "SD8qLW_sideNavCount",
521
+ "treeIconDown": "SD8qLW_treeIconDown",
522
+ "layoutSwitch": "SD8qLW_layoutSwitch",
523
+ "chevronOpen": "SD8qLW_chevronOpen",
524
+ "lineContext": "SD8qLW_lineContext",
525
+ "calOut": "SD8qLW_calOut",
603
526
  "sideEditCell": "SD8qLW_sideEditCell",
604
- "commitPopMeta": "SD8qLW_commitPopMeta",
527
+ "syncLevel": "SD8qLW_syncLevel",
528
+ "fbCommitHash": "SD8qLW_fbCommitHash",
605
529
  "layoutButton": "SD8qLW_layoutButton",
530
+ "swatch": "SD8qLW_swatch",
531
+ "diffPre": "SD8qLW_diffPre",
532
+ "lnOld": "SD8qLW_lnOld",
533
+ "lnNew": "SD8qLW_lnNew",
534
+ "sideBanner": "SD8qLW_sideBanner",
535
+ "elideHead": "SD8qLW_elideHead",
536
+ "headerLeft": "SD8qLW_headerLeft",
537
+ "paneDividerY": "SD8qLW_paneDividerY",
538
+ "sideNum": "SD8qLW_sideNum",
539
+ "pathDirGlyph": "SD8qLW_pathDirGlyph",
540
+ "filterChips": "SD8qLW_filterChips",
541
+ "diffScroll": "SD8qLW_diffScroll",
606
542
  "paletteRow": "SD8qLW_paletteRow",
607
- "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",
618
- "treeFilterClear": "SD8qLW_treeFilterClear",
619
- "refEmpty": "SD8qLW_refEmpty",
620
- "body": "SD8qLW_body",
621
- "miniBtnPrimary": "SD8qLW_miniBtnPrimary",
622
- "commit": "SD8qLW_commit",
623
- "blockBtnDanger": "SD8qLW_blockBtnDanger",
624
- "layoutGlyph": "SD8qLW_layoutGlyph",
625
- "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",
543
+ "sideCodeSame": "SD8qLW_sideCodeSame",
544
+ "commitsPane": "SD8qLW_commitsPane",
545
+ "resizer": "SD8qLW_resizer",
546
+ "btn": "SD8qLW_btn",
547
+ "chipSystem": "SD8qLW_chipSystem",
548
+ "sliderValue": "SD8qLW_sliderValue",
549
+ "themeDirty": "SD8qLW_themeDirty",
639
550
  "layoutButtonOn": "SD8qLW_layoutButtonOn",
640
- "cssArea": "SD8qLW_cssArea",
641
- "tab": "SD8qLW_tab",
642
- "stDeleted": "SD8qLW_stDeleted",
643
- "commitMessage": "SD8qLW_commitMessage",
644
- "funnelPop": "SD8qLW_funnelPop",
551
+ "fileStatus": "SD8qLW_fileStatus",
552
+ "commitBox": "SD8qLW_commitBox",
645
553
  "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",
554
+ "fbMain": "SD8qLW_fbMain",
555
+ "chipDark": "SD8qLW_chipDark",
556
+ "sideColGrid": "SD8qLW_sideColGrid",
557
+ "sideColGridWrap": "SD8qLW_sideColGridWrap",
558
+ "segmentActive": "SD8qLW_segmentActive",
559
+ "syncUpstream": "SD8qLW_syncUpstream",
560
+ "treeDir": "SD8qLW_treeDir",
655
561
  "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",
562
+ "commitMessage": "SD8qLW_commitMessage",
563
+ "diffPane": "SD8qLW_diffPane",
564
+ "miniBtnPrimary": "SD8qLW_miniBtnPrimary",
565
+ "pathNode": "SD8qLW_pathNode",
566
+ "compareBar": "SD8qLW_compareBar",
663
567
  "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",
568
+ "calTitle": "SD8qLW_calTitle",
569
+ "sideColRight": "SD8qLW_sideColRight",
570
+ "cardAhead": "SD8qLW_cardAhead",
571
+ "fbCommitWho": "SD8qLW_fbCommitWho",
572
+ "cardWt": "SD8qLW_cardWt",
573
+ "sideActions": "SD8qLW_sideActions",
574
+ "fileDiscard": "SD8qLW_fileDiscard",
575
+ "sideColGridBlame": "SD8qLW_sideColGridBlame",
576
+ "resizerActive": "SD8qLW_resizerActive",
577
+ "sideRails": "SD8qLW_sideRails",
578
+ "sideNotice": "SD8qLW_sideNotice",
579
+ "fbNote": "SD8qLW_fbNote",
580
+ "headerBranch": "SD8qLW_headerBranch",
581
+ "treeFilter": "SD8qLW_treeFilter",
582
+ "funnelBoundBtnActive": "SD8qLW_funnelBoundBtnActive",
583
+ "funnelButton": "SD8qLW_funnelButton",
584
+ "body": "SD8qLW_body",
585
+ "scopeBtn": "SD8qLW_scopeBtn",
586
+ "commits": "SD8qLW_commits",
587
+ "funnelBoundVal": "SD8qLW_funnelBoundVal",
588
+ "sideRail": "SD8qLW_sideRail",
673
589
  "signDel": "SD8qLW_signDel",
674
- "commitsSentinel": "SD8qLW_commitsSentinel",
675
- "sideCodeSame": "SD8qLW_sideCodeSame",
676
- "sideBlockHotFirst": "SD8qLW_sideBlockHotFirst",
677
- "sideActionsAdjacent": "SD8qLW_sideActionsAdjacent",
678
- "funnelBoundValSet": "SD8qLW_funnelBoundValSet",
679
- "headerViewRef": "SD8qLW_headerViewRef",
680
- "commitHasBody": "SD8qLW_commitHasBody",
681
- "gutter": "SD8qLW_gutter",
682
590
  "checkBox": "SD8qLW_checkBox",
683
- "elideHead": "SD8qLW_elideHead",
684
- "btnClose": "SD8qLW_btnClose",
685
- "btnAhead": "SD8qLW_btnAhead",
686
- "funnelFootCountOn": "SD8qLW_funnelFootCountOn",
591
+ "sideRailSpan": "SD8qLW_sideRailSpan",
592
+ "gsFade": "SD8qLW_gsFade",
593
+ "funnelCount": "SD8qLW_funnelCount",
594
+ "funnelRow": "SD8qLW_funnelRow",
595
+ "treeIconGlyph": "SD8qLW_treeIconGlyph",
596
+ "fbMore": "SD8qLW_fbMore",
597
+ "treeFilterInput": "SD8qLW_treeFilterInput",
598
+ "paneDivider": "SD8qLW_paneDivider",
599
+ "funnelBoundBtn": "SD8qLW_funnelBoundBtn",
600
+ "commitPopBody": "SD8qLW_commitPopBody",
601
+ "diffPreWrap": "SD8qLW_diffPreWrap",
602
+ "treeLabel": "SD8qLW_treeLabel",
603
+ "miniBtn": "SD8qLW_miniBtn",
604
+ "themeLabel": "SD8qLW_themeLabel",
605
+ "themeRail": "SD8qLW_themeRail",
606
+ "sideArmable": "SD8qLW_sideArmable",
607
+ "refEmpty": "SD8qLW_refEmpty",
608
+ "file": "SD8qLW_file",
687
609
  "funnelButtonActive": "SD8qLW_funnelButtonActive",
688
- "refLabel": "SD8qLW_refLabel",
689
- "treeDirCounts": "SD8qLW_treeDirCounts",
610
+ "opBannerOk": "SD8qLW_opBannerOk",
611
+ "header": "SD8qLW_header",
612
+ "sideCode": "SD8qLW_sideCode",
690
613
  "headerTotalsDel": "SD8qLW_headerTotalsDel",
691
- "scopeHint": "SD8qLW_scopeHint",
692
- "stUntracked": "SD8qLW_stUntracked",
693
- "confirmTitle": "SD8qLW_confirmTitle",
694
- "fbCommitHint": "SD8qLW_fbCommitHint",
695
- "imgCaption": "SD8qLW_imgCaption",
696
- "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",
704
- "refSearch": "SD8qLW_refSearch",
614
+ "fbCommitWhat": "SD8qLW_fbCommitWhat",
615
+ "sideRailsOn": "SD8qLW_sideRailsOn",
616
+ "calWeek": "SD8qLW_calWeek",
705
617
  "treeLead": "SD8qLW_treeLead",
706
- "sideBanner": "SD8qLW_sideBanner",
618
+ "refRowSpacer": "SD8qLW_refRowSpacer",
619
+ "code": "SD8qLW_code",
620
+ "fbCommitHint": "SD8qLW_fbCommitHint",
621
+ "settingsPop": "SD8qLW_settingsPop",
622
+ "treeRow": "SD8qLW_treeRow",
623
+ "blockBtnDanger": "SD8qLW_blockBtnDanger",
624
+ "funnelTabs": "SD8qLW_funnelTabs",
625
+ "fileCountAdd": "SD8qLW_fileCountAdd",
626
+ "sideBlockHotFirst": "SD8qLW_sideBlockHotFirst",
627
+ "sideBlockHotLast": "SD8qLW_sideBlockHotLast",
628
+ "paneDividerActive": "SD8qLW_paneDividerActive",
629
+ "filterChipLabel": "SD8qLW_filterChipLabel",
630
+ "checkMark": "SD8qLW_checkMark",
631
+ "filterChipRemove": "SD8qLW_filterChipRemove",
632
+ "imgPane": "SD8qLW_imgPane",
633
+ "sideCellBlock": "SD8qLW_sideCellBlock",
634
+ "sideCol": "SD8qLW_sideCol",
635
+ "commitSubjectRow": "SD8qLW_commitSubjectRow",
636
+ "fbFileName": "SD8qLW_fbFileName",
637
+ "refList": "SD8qLW_refList",
638
+ "cardDetached": "SD8qLW_cardDetached",
639
+ "cardSep": "SD8qLW_cardSep",
640
+ "calMark": "SD8qLW_calMark",
641
+ "stRenamed": "SD8qLW_stRenamed",
707
642
  "filePath": "SD8qLW_filePath",
708
- "bodyRow": "SD8qLW_bodyRow",
709
- "diffWrap": "SD8qLW_diffWrap",
710
- "headerDetached": "SD8qLW_headerDetached",
643
+ "funnelFoot": "SD8qLW_funnelFoot",
644
+ "fileLi": "SD8qLW_fileLi",
645
+ "headerTotalsDim": "SD8qLW_headerTotalsDim",
646
+ "sideColWrap": "SD8qLW_sideColWrap",
711
647
  "stModified": "SD8qLW_stModified",
712
- "treeDirActive": "SD8qLW_treeDirActive",
713
- "themeDirty": "SD8qLW_themeDirty",
714
- "diffPane": "SD8qLW_diffPane",
715
- "filterChips": "SD8qLW_filterChips",
648
+ "fbHeader": "SD8qLW_fbHeader",
649
+ "cardFiles": "SD8qLW_cardFiles",
650
+ "treeEmpty": "SD8qLW_treeEmpty",
651
+ "commitWhen": "SD8qLW_commitWhen",
652
+ "fileBinary": "SD8qLW_fileBinary",
653
+ "commitBtn": "SD8qLW_commitBtn",
654
+ "refValue": "SD8qLW_refValue",
716
655
  "funnelPane": "SD8qLW_funnelPane",
717
- "fileStatus": "SD8qLW_fileStatus",
656
+ "commitFilter": "SD8qLW_commitFilter",
657
+ "commitPopSubject": "SD8qLW_commitPopSubject",
658
+ "commitAuthor": "SD8qLW_commitAuthor",
659
+ "fileCountDel": "SD8qLW_fileCountDel",
660
+ "confirmBox": "SD8qLW_confirmBox",
661
+ "confirmBody": "SD8qLW_confirmBody",
662
+ "refSearch": "SD8qLW_refSearch",
663
+ "fbSearch": "SD8qLW_fbSearch",
664
+ "opBannerBad": "SD8qLW_opBannerBad",
665
+ "treeIconOn": "SD8qLW_treeIconOn",
666
+ "elide": "SD8qLW_elide",
667
+ "headerRight": "SD8qLW_headerRight",
668
+ "sideScroll": "SD8qLW_sideScroll",
669
+ "tabActive": "SD8qLW_tabActive",
670
+ "themeNote": "SD8qLW_themeNote",
718
671
  "btnDanger": "SD8qLW_btnDanger",
719
- "treeIcon": "SD8qLW_treeIcon",
720
- "funnelBoundRows": "SD8qLW_funnelBoundRows",
721
- "scopeRow": "SD8qLW_scopeRow",
722
- "chipLight": "SD8qLW_chipLight",
672
+ "drawer": "SD8qLW_drawer",
673
+ "fbCommitLine": "SD8qLW_fbCommitLine",
674
+ "funnelFootCount": "SD8qLW_funnelFootCount",
675
+ "refFoot": "SD8qLW_refFoot",
676
+ "paletteRowActive": "SD8qLW_paletteRowActive",
677
+ "tabs": "SD8qLW_tabs",
723
678
  "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",
730
- "elideTail": "SD8qLW_elideTail",
731
- "funnelMore": "SD8qLW_funnelMore",
732
- "headerView": "SD8qLW_headerView",
733
- "sideTabActive": "SD8qLW_sideTabActive",
734
- "commitBox": "SD8qLW_commitBox",
735
- "commitLead": "SD8qLW_commitLead",
736
- "scopeBtnActive": "SD8qLW_scopeBtnActive",
679
+ "refRowName": "SD8qLW_refRowName",
680
+ "theme": "SD8qLW_theme",
681
+ "commitRow": "SD8qLW_commitRow",
682
+ "syncSpacer": "SD8qLW_syncSpacer",
683
+ "commitHash": "SD8qLW_commitHash",
684
+ "treeDirLi": "SD8qLW_treeDirLi",
685
+ "btnCount": "SD8qLW_btnCount",
737
686
  "commitRefMore": "SD8qLW_commitRefMore",
738
- "fbTree": "SD8qLW_fbTree",
739
- "calNav": "SD8qLW_calNav",
740
- "refValue": "SD8qLW_refValue",
741
- "commitActive": "SD8qLW_commitActive",
742
- "segmentActive": "SD8qLW_segmentActive",
743
- "code": "SD8qLW_code",
744
- "lineHunk": "SD8qLW_lineHunk",
745
- "treeFilter": "SD8qLW_treeFilter",
746
- "resizerActive": "SD8qLW_resizerActive",
687
+ "elideTail": "SD8qLW_elideTail",
688
+ "tree": "SD8qLW_tree",
689
+ "headerTotals": "SD8qLW_headerTotals",
690
+ "treeSub": "SD8qLW_treeSub",
747
691
  "blockBar": "SD8qLW_blockBar",
692
+ "treeTools": "SD8qLW_treeTools",
693
+ "diffNav": "SD8qLW_diffNav",
694
+ "commitsFoot": "SD8qLW_commitsFoot",
695
+ "sideNumDel": "SD8qLW_sideNumDel",
696
+ "sideBannerTitle": "SD8qLW_sideBannerTitle",
697
+ "fileActive": "SD8qLW_fileActive",
698
+ "card": "SD8qLW_card",
699
+ "commitsSentinel": "SD8qLW_commitsSentinel",
700
+ "refGroup": "SD8qLW_refGroup",
701
+ "calIn": "SD8qLW_calIn",
702
+ "sideRailRight": "SD8qLW_sideRailRight",
703
+ "funnelBoundRow": "SD8qLW_funnelBoundRow",
704
+ "refRowActive": "SD8qLW_refRowActive",
705
+ "calHead": "SD8qLW_calHead",
706
+ "funnelBoundKey": "SD8qLW_funnelBoundKey",
707
+ "filterChip": "SD8qLW_filterChip",
708
+ "fbCommitWhen": "SD8qLW_fbCommitWhen",
709
+ "treeWrap": "SD8qLW_treeWrap",
710
+ "cardBranchName": "SD8qLW_cardBranchName",
711
+ "calNav": "SD8qLW_calNav",
712
+ "calToday": "SD8qLW_calToday",
713
+ "tab": "SD8qLW_tab",
714
+ "segmented": "SD8qLW_segmented",
715
+ "sideNumAdd": "SD8qLW_sideNumAdd",
716
+ "opBanner": "SD8qLW_opBanner",
717
+ "commitStaged": "SD8qLW_commitStaged",
718
+ "lineAdd": "SD8qLW_lineAdd",
719
+ "calGrid": "SD8qLW_calGrid",
720
+ "paneHead": "SD8qLW_paneHead",
721
+ "btnBehind": "SD8qLW_btnBehind",
722
+ "fbRowIgnored": "SD8qLW_fbRowIgnored",
723
+ "commitPopTop": "SD8qLW_commitPopTop",
748
724
  "refCaret": "SD8qLW_refCaret",
749
- "syncLevel": "SD8qLW_syncLevel",
750
- "refButton": "SD8qLW_refButton",
751
- "bgEmpty": "SD8qLW_bgEmpty",
752
- "sideCols": "SD8qLW_sideCols",
753
- "refPop": "SD8qLW_refPop",
754
- "themeNote": "SD8qLW_themeNote",
755
- "treeActions": "SD8qLW_treeActions",
756
- "gsSlide": "SD8qLW_gsSlide",
757
- "stRenamed": "SD8qLW_stRenamed",
758
- "resizer": "SD8qLW_resizer",
759
- "fbNote": "SD8qLW_fbNote",
760
- "cardSep": "SD8qLW_cardSep",
725
+ "fbDirName": "SD8qLW_fbDirName",
726
+ "cardBehind": "SD8qLW_cardBehind",
727
+ "commitLine": "SD8qLW_commitLine",
728
+ "scopeHint": "SD8qLW_scopeHint",
729
+ "fbTree": "SD8qLW_fbTree",
730
+ "funnelList": "SD8qLW_funnelList",
731
+ "commitTop": "SD8qLW_commitTop",
732
+ "themeGroup": "SD8qLW_themeGroup",
733
+ "funnelPop": "SD8qLW_funnelPop",
734
+ "wordDel": "SD8qLW_wordDel",
735
+ "sliderRow": "SD8qLW_sliderRow",
736
+ "commitSubject": "SD8qLW_commitSubject",
737
+ "sideBlockHot": "SD8qLW_sideBlockHot",
761
738
  "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",
739
+ "pathFileGlyph": "SD8qLW_pathFileGlyph",
740
+ "checkMarkPartial": "SD8qLW_checkMarkPartial",
741
+ "treeDirActive": "SD8qLW_treeDirActive",
742
+ "gutter": "SD8qLW_gutter",
743
+ "fbCommit": "SD8qLW_fbCommit",
744
+ "sideTab": "SD8qLW_sideTab",
745
+ "pathChildren": "SD8qLW_pathChildren",
746
+ "cssArea": "SD8qLW_cssArea",
747
+ "stDeleted": "SD8qLW_stDeleted",
748
+ "empty": "SD8qLW_empty",
749
+ "btnIconOn": "SD8qLW_btnIconOn",
750
+ "imgStage": "SD8qLW_imgStage",
751
+ "btnAhead": "SD8qLW_btnAhead",
752
+ "chipLight": "SD8qLW_chipLight",
753
+ "commit": "SD8qLW_commit",
754
+ "scopeRow": "SD8qLW_scopeRow",
755
+ "funnelTab": "SD8qLW_funnelTab",
769
756
  "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",
757
+ "crMark": "SD8qLW_crMark",
758
+ "fbRowActive": "SD8qLW_fbRowActive",
759
+ "treeFilterClear": "SD8qLW_treeFilterClear",
783
760
  "cardBranch": "SD8qLW_cardBranch",
784
- "refList": "SD8qLW_refList",
785
- "sideTabs": "SD8qLW_sideTabs",
786
- "calWeek": "SD8qLW_calWeek",
787
- "settingsPop": "SD8qLW_settingsPop",
788
- "chevron": "SD8qLW_chevron",
761
+ "syncBar": "SD8qLW_syncBar",
762
+ "segment": "SD8qLW_segment",
763
+ "refPicker": "SD8qLW_refPicker",
764
+ "refPop": "SD8qLW_refPop",
765
+ "paneTitle": "SD8qLW_paneTitle",
766
+ "commitLead": "SD8qLW_commitLead",
767
+ "refLabel": "SD8qLW_refLabel",
789
768
  "treeDirCount": "SD8qLW_treeDirCount",
790
- "syncUpstream": "SD8qLW_syncUpstream",
791
- "funnelList": "SD8qLW_funnelList",
792
- "filterChipRemove": "SD8qLW_filterChipRemove",
793
- "treeRow": "SD8qLW_treeRow",
794
- "fbRow": "SD8qLW_fbRow",
795
- "treeSub": "SD8qLW_treeSub",
796
- "funnelTabCount": "SD8qLW_funnelTabCount",
797
- "sideCode": "SD8qLW_sideCode",
798
- "sideCellBlock": "SD8qLW_sideCellBlock",
769
+ "sideSaveReady": "SD8qLW_sideSaveReady",
770
+ "confirmScrim": "SD8qLW_confirmScrim",
771
+ "sideFlow": "SD8qLW_sideFlow",
772
+ "imgShot": "SD8qLW_imgShot",
799
773
  "cardAdded": "SD8qLW_cardAdded",
800
- "pathFileGlyph": "SD8qLW_pathFileGlyph",
801
- "file": "SD8qLW_file",
802
- "commitHash": "SD8qLW_commitHash"
774
+ "headerPathMain": "SD8qLW_headerPathMain",
775
+ "treeDirName": "SD8qLW_treeDirName",
776
+ "funnelChevron": "SD8qLW_funnelChevron",
777
+ "commitMeta": "SD8qLW_commitMeta",
778
+ "confirmTitle": "SD8qLW_confirmTitle",
779
+ "filterClear": "SD8qLW_filterClear",
780
+ "stUntracked": "SD8qLW_stUntracked",
781
+ "wordAdd": "SD8qLW_wordAdd",
782
+ "headerPicker": "SD8qLW_headerPicker",
783
+ "bgPreview": "SD8qLW_bgPreview",
784
+ "funnelMore": "SD8qLW_funnelMore",
785
+ "renameLine": "SD8qLW_renameLine",
786
+ "sideActionsAdjacent": "SD8qLW_sideActionsAdjacent",
787
+ "sideBannerActs": "SD8qLW_sideBannerActs",
788
+ "funnelFootClear": "SD8qLW_funnelFootClear",
789
+ "commitPop": "SD8qLW_commitPop",
790
+ "sideTabActive": "SD8qLW_sideTabActive",
791
+ "pullGroup": "SD8qLW_pullGroup",
792
+ "diffSpacer": "SD8qLW_diffSpacer",
793
+ "headerDetached": "SD8qLW_headerDetached",
794
+ "funnelCaption": "SD8qLW_funnelCaption",
795
+ "refRow": "SD8qLW_refRow",
796
+ "funnelTabActive": "SD8qLW_funnelTabActive",
797
+ "confirmActions": "SD8qLW_confirmActions",
798
+ "commitAmend": "SD8qLW_commitAmend",
799
+ "sideCodeDel": "SD8qLW_sideCodeDel",
800
+ "sideCurrentBlockActions": "SD8qLW_sideCurrentBlockActions",
801
+ "line": "SD8qLW_line",
802
+ "funnelPreset": "SD8qLW_funnelPreset",
803
+ "funnelBoundRows": "SD8qLW_funnelBoundRows",
804
+ "funnelFootCountOn": "SD8qLW_funnelFootCountOn",
805
+ "blockBtn": "SD8qLW_blockBtn",
806
+ "funnelBoundValSet": "SD8qLW_funnelBoundValSet",
807
+ "sideSpacer": "SD8qLW_sideSpacer",
808
+ "segmentChip": "SD8qLW_segmentChip",
809
+ "gsSlide": "SD8qLW_gsSlide",
810
+ "commitCopy": "SD8qLW_commitCopy",
811
+ "treeCol": "SD8qLW_treeCol",
812
+ "sideRailGap": "SD8qLW_sideRailGap",
813
+ "btnPrimary": "SD8qLW_btnPrimary",
814
+ "diffWrap": "SD8qLW_diffWrap"
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
  ] })
@@ -35586,6 +35967,25 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
35586
35967
  //#endregion
35587
35968
  //#region src/client/diff-model.ts
35588
35969
  /**
35970
+ * Whether an empty per-file diff is the line-ending phantom.
35971
+ *
35972
+ * autocrlf / `eol` attributes make git's stat check and its clean filter
35973
+ * disagree, so a file can sit in the tree as modified forever while every
35974
+ * content diff comes back empty — the classic always-modified file of a
35975
+ * Windows checkout. Opening it must explain that, not fall to the generic
35976
+ * "no text changes" a broken pane also shows.
35977
+ *
35978
+ * Only `modified` + an empty WHOLE-FILE segment is the phantom: a fully
35979
+ * staged file has an empty unstaged layer but a non-empty HEAD-diff (the
35980
+ * reader is one click from the content), and every other status always has
35981
+ * a diff or a synthesized one.
35982
+ * @param status - the tree row's status for the open file.
35983
+ * @param segment - the whole-file diff text (HEAD vs worktree).
35984
+ */
35985
+ function isPhantomModified(status, segment) {
35986
+ return status === "modified" && segment.length === 0;
35987
+ }
35988
+ /**
35589
35989
  * Parse a unified diff segment into typed rows, tracking line numbers.
35590
35990
  * @param segment - one file's `diff --git` text (headers are skipped).
35591
35991
  */
@@ -36294,7 +36694,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
36294
36694
  * @param offset - px above the first row, e.g. the grid's top padding.
36295
36695
  * @returns the first row of each block, in the order the blocks appear.
36296
36696
  */
36297
- function blockTopsFromRows(blocks, rowH, offset = 0) {
36697
+ function blockTopsFromRows(blocks, rowH, offset = 0, topOf) {
36298
36698
  const tops = [];
36299
36699
  const seen = /* @__PURE__ */ new Set();
36300
36700
  for (let i = 0; i < blocks.length; i += 1) {
@@ -36303,7 +36703,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
36303
36703
  seen.add(block);
36304
36704
  tops.push({
36305
36705
  block,
36306
- top: offset + i * rowH
36706
+ top: offset + (topOf === void 0 ? i * rowH : topOf(i))
36307
36707
  });
36308
36708
  }
36309
36709
  return tops;
@@ -36349,7 +36749,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
36349
36749
  * until the height is known would flash an empty pane; a screenful is both
36350
36750
  * safe and close.
36351
36751
  */
36352
- const ASSUMED_VIEWPORT_PX = 1200;
36752
+ const ASSUMED_VIEWPORT_PX$1 = 1200;
36353
36753
  /** Whether two windows paint the same rows AND reserve the same total height.
36354
36754
  * Comparing only start/end leaves a previous file's bottom spacer behind when
36355
36755
  * two long files happen to expose the same viewport-sized row range. */
@@ -36365,7 +36765,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
36365
36765
  * @param fallback - used when it is neither finite nor usable.
36366
36766
  * @returns a finite, non-negative number.
36367
36767
  */
36368
- function sane(value, fallback) {
36768
+ function sane$1(value, fallback) {
36369
36769
  return Number.isFinite(value) && value > 0 ? value : fallback;
36370
36770
  }
36371
36771
  /**
@@ -36380,7 +36780,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
36380
36780
  */
36381
36781
  function rowWindow(scrollTop, viewportH, rowCount, rowH = 20, overscan = 40) {
36382
36782
  const rows = Math.max(0, Math.trunc(rowCount));
36383
- const height = sane(rowH, 20);
36783
+ const height = sane$1(rowH, 20);
36384
36784
  if (rows <= 400) return {
36385
36785
  start: 0,
36386
36786
  end: rows,
@@ -36388,7 +36788,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
36388
36788
  padBottom: 0
36389
36789
  };
36390
36790
  const top = Number.isFinite(scrollTop) && scrollTop > 0 ? scrollTop : 0;
36391
- const view = sane(viewportH, ASSUMED_VIEWPORT_PX);
36791
+ const view = sane$1(viewportH, ASSUMED_VIEWPORT_PX$1);
36392
36792
  const pad = Math.max(0, Math.trunc(overscan));
36393
36793
  const first = Math.max(0, Math.floor(top / height) - pad);
36394
36794
  const last = Math.min(rows, Math.ceil((top + view) / height) + pad);
@@ -36540,6 +36940,154 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
36540
36940
  });
36541
36941
  }
36542
36942
  //#endregion
36943
+ //#region src/client/h-rail.ts
36944
+ /** Before anything has been measured, and for a column that is not mounted. */
36945
+ const NO_RAIL = {
36946
+ view: 0,
36947
+ content: 0
36948
+ };
36949
+ /**
36950
+ * Whether a column overflows by enough to be worth a scrollbar.
36951
+ *
36952
+ * The one-pixel slack is not tidiness: a grid track rounds to a fraction, so a
36953
+ * column whose longest line exactly fits still reports a scroll width a third
36954
+ * of a pixel wider than its client width. Without the slack every file gets a
36955
+ * rail, and every one of those rails scrolls nowhere.
36956
+ *
36957
+ * @param metric - the column's measured widths.
36958
+ * @returns true when a rail should be shown for it.
36959
+ */
36960
+ function railNeeded(metric) {
36961
+ const { view, content } = metric;
36962
+ if (!Number.isFinite(view) || !Number.isFinite(content)) return false;
36963
+ if (view <= 0) return false;
36964
+ return content > view + 1;
36965
+ }
36966
+ /** Whether the rail row is shown at all — either column overflowing is enough,
36967
+ * since the row is one sticky strip and a lone rail still needs its space. */
36968
+ function railsShown(left, right) {
36969
+ return railNeeded(left) || railNeeded(right);
36970
+ }
36971
+ /**
36972
+ * Whether two measurements are the same.
36973
+ *
36974
+ * The measuring effect runs on every render, so this is what keeps it from
36975
+ * setting state on every render and rendering again forever.
36976
+ *
36977
+ * @param a - the measurement held in state.
36978
+ * @param b - the one just read from the DOM.
36979
+ */
36980
+ function sameMetric(a, b) {
36981
+ return a.view === b.view && a.content === b.content;
36982
+ }
36983
+ //#endregion
36984
+ //#region src/client/SideRails.tsx
36985
+ /**
36986
+ * Measure one column and keep its rail's scroll position tied to it.
36987
+ *
36988
+ * The measurement runs on EVERY render rather than on a dependency list: the
36989
+ * content width changes for reasons no list can name — a window scrolled to
36990
+ * longer rows, syntax spans replacing plain text, the editor mounting into the
36991
+ * right column, wrap turning on. Two integer reads is a cheap price for never
36992
+ * being stale, and {@link sameMetric} is what keeps setting state on every
36993
+ * render from becoming a render on every render.
36994
+ *
36995
+ * @param colRef - the column, which clips and scrolls its own content.
36996
+ * @param railRef - the strip at the bottom of the pane that stands in for it.
36997
+ * @returns the column's measured widths.
36998
+ */
36999
+ function useRail(colRef, railRef) {
37000
+ const [metric, setMetric] = (0, react.useState)(NO_RAIL);
37001
+ const measure = (col) => {
37002
+ const next = {
37003
+ view: col.clientWidth,
37004
+ content: col.scrollWidth
37005
+ };
37006
+ setMetric((prev) => sameMetric(prev, next) ? prev : next);
37007
+ };
37008
+ (0, react.useLayoutEffect)(() => {
37009
+ const col = colRef.current;
37010
+ if (col !== null) measure(col);
37011
+ });
37012
+ (0, react.useEffect)(() => {
37013
+ const col = colRef.current;
37014
+ if (col === null) return;
37015
+ const observer = new ResizeObserver(() => {
37016
+ measure(col);
37017
+ });
37018
+ observer.observe(col);
37019
+ return () => {
37020
+ observer.disconnect();
37021
+ };
37022
+ }, [colRef]);
37023
+ (0, react.useEffect)(() => {
37024
+ const col = colRef.current;
37025
+ const rail = railRef.current;
37026
+ if (col === null || rail === null) return;
37027
+ const toRail = () => {
37028
+ if (rail.scrollLeft !== col.scrollLeft) rail.scrollLeft = col.scrollLeft;
37029
+ };
37030
+ const toCol = () => {
37031
+ if (col.scrollLeft !== rail.scrollLeft) col.scrollLeft = rail.scrollLeft;
37032
+ };
37033
+ col.addEventListener("scroll", toRail, { passive: true });
37034
+ rail.addEventListener("scroll", toCol, { passive: true });
37035
+ toRail();
37036
+ return () => {
37037
+ col.removeEventListener("scroll", toRail);
37038
+ rail.removeEventListener("scroll", toCol);
37039
+ };
37040
+ }, [colRef, railRef]);
37041
+ return metric;
37042
+ }
37043
+ /**
37044
+ * The side-by-side pane's horizontal scrollbars, stuck to the bottom of the
37045
+ * pane instead of the bottom of the file — see `h-rail.ts` for why they are
37046
+ * here at all.
37047
+ *
37048
+ * Always rendered, never conditionally: the strip collapses to nothing through
37049
+ * a class when neither column overflows, so both rails keep their elements and
37050
+ * the effects above keep stable dependencies. A rail that mounted and
37051
+ * unmounted with the measurement would re-attach its listeners on every file.
37052
+ *
37053
+ * @param leftRef - the left column.
37054
+ * @param rightRef - the right column.
37055
+ * @param split - the divider's position, as a fraction of the pane's width;
37056
+ * the rails carry the same flex sizing so each sits under the
37057
+ * column it scrolls.
37058
+ * @returns the sticky rail strip.
37059
+ */
37060
+ function SideRails({ leftRef, rightRef, split }) {
37061
+ const leftRailRef = (0, react.useRef)(null);
37062
+ const rightRailRef = (0, react.useRef)(null);
37063
+ const left = useRail(leftRef, leftRailRef);
37064
+ const right = useRail(rightRef, rightRailRef);
37065
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
37066
+ className: railsShown(left, right) ? `${GitWorkbenchPanel_module_css_default.sideRails} ${GitWorkbenchPanel_module_css_default.sideRailsOn}` : GitWorkbenchPanel_module_css_default.sideRails,
37067
+ "aria-hidden": "true",
37068
+ children: [
37069
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
37070
+ ref: leftRailRef,
37071
+ className: GitWorkbenchPanel_module_css_default.sideRail,
37072
+ style: { flexBasis: `${split * 100}%` },
37073
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
37074
+ className: GitWorkbenchPanel_module_css_default.sideRailSpan,
37075
+ style: { width: left.content }
37076
+ })
37077
+ }),
37078
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: GitWorkbenchPanel_module_css_default.sideRailGap }),
37079
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
37080
+ ref: rightRailRef,
37081
+ className: `${GitWorkbenchPanel_module_css_default.sideRail} ${GitWorkbenchPanel_module_css_default.sideRailRight}`,
37082
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
37083
+ className: GitWorkbenchPanel_module_css_default.sideRailSpan,
37084
+ style: { width: right.content }
37085
+ })
37086
+ })
37087
+ ]
37088
+ });
37089
+ }
37090
+ //#endregion
36543
37091
  //#region src/client/use-row-window.ts
36544
37092
  /**
36545
37093
  * The rows a diff actually has to put in the DOM, tracked against its scroller.
@@ -36555,9 +37103,13 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
36555
37103
  *
36556
37104
  * @param scrollRef - the element that scrolls the rows.
36557
37105
  * @param rowCount - how many rows the diff has.
37106
+ * @param enabled - false while soft wrap is on, when the rows are no longer a
37107
+ * fixed height and `use-variable-row-window.ts` answers
37108
+ * instead. Attaching both panes' listeners at once would put
37109
+ * two readers on the same scroll.
36558
37110
  * @returns the rows to render and the spacer heights standing in for the rest.
36559
37111
  */
36560
- function useRowWindow(scrollRef, rowCount, mountKey) {
37112
+ function useRowWindow(scrollRef, rowCount, mountKey, enabled = true) {
36561
37113
  const count = Number.isFinite(rowCount) ? Math.max(0, Math.trunc(rowCount)) : 0;
36562
37114
  const [held, setHeld] = (0, react.useState)(() => ({
36563
37115
  mountKey,
@@ -36567,7 +37119,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
36567
37119
  const visible = rowWindowForMount(held, count, mountKey);
36568
37120
  (0, react.useEffect)(() => {
36569
37121
  const el = scrollRef.current;
36570
- if (el === null) return;
37122
+ if (el === null || !enabled) return;
36571
37123
  const read = () => {
36572
37124
  const next = rowWindow(el.scrollTop, el.clientHeight, count);
36573
37125
  setHeld((prev) => prev.mountKey === mountKey && prev.rowCount === count && sameRowWindow(prev.win, next) ? prev : {
@@ -36587,11 +37139,475 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
36587
37139
  }, [
36588
37140
  scrollRef,
36589
37141
  count,
36590
- mountKey
37142
+ mountKey,
37143
+ enabled
36591
37144
  ]);
36592
37145
  return visible;
36593
37146
  }
36594
37147
  //#endregion
37148
+ //#region src/client/row-heights.ts
37149
+ /**
37150
+ * Row offsets when the rows are NOT all the same height — what soft wrap makes
37151
+ * of a diff pane.
37152
+ *
37153
+ * `row-window.ts` places row `i` at `i * 20px` with no measuring at all, and
37154
+ * says so: the panes guarantee it by writing `white-space: pre`, so nothing
37155
+ * ever wraps. Turn wrapping on and that guarantee is gone — one row can be
37156
+ * five lines tall — and every number the window computes from it (which rows
37157
+ * are visible, how tall the spacers are, where a change sits) is wrong.
37158
+ *
37159
+ * The replacement keeps the same shape and the same rule: work proportional to
37160
+ * the VIEWPORT. Heights are MEASURED, but only for rows that are actually in
37161
+ * the DOM, which is the window plus its overscan; every other row carries an
37162
+ * estimate computed from its text. So a 20,000-row file costs one array and
37163
+ * some arithmetic, not 20,000 measurements.
37164
+ *
37165
+ * The prefix sums are a Fenwick tree rather than a running array, because both
37166
+ * things this is asked for happen on the scroll path and must not be linear:
37167
+ * "where does row i start" is a prefix query, "which row is at y" is a search
37168
+ * for a prefix, and a measurement that lands changes one row's height. All
37169
+ * three are O(log n) here; as a plain array of running totals, the third would
37170
+ * rewrite every entry after the row that moved.
37171
+ *
37172
+ * Pure: no React, no DOM. `tests/row-heights.test.ts` loads it directly.
37173
+ *
37174
+ * @module @young1lin/dsh-ui-gitworkbench/client/row-heights
37175
+ */
37176
+ /** A viewport height to assume before the scroller has been measured, matching
37177
+ * `row-window.ts`: rendering nothing until the height is known flashes an
37178
+ * empty pane, and a screenful is both safe and close. */
37179
+ const ASSUMED_VIEWPORT_PX = 1200;
37180
+ /**
37181
+ * How many display columns a line occupies.
37182
+ *
37183
+ * Only for the ESTIMATE of rows nobody has measured, so it does not have to
37184
+ * agree with the browser to the character — it has to be close enough that
37185
+ * the scrollbar is the right length and a jump to the middle of the file lands
37186
+ * near where it should. Tabs advance to the next stop (`tab-size: 4` in the
37187
+ * panes' grid) and full-width characters take two columns, which are the two
37188
+ * ways a naive `text.length` is badly wrong rather than slightly wrong.
37189
+ *
37190
+ * @param text - one row's text.
37191
+ * @param tabSize - columns a tab advances to the next multiple of.
37192
+ */
37193
+ function displayColumns(text, tabSize = 4) {
37194
+ let columns = 0;
37195
+ for (const char of text) {
37196
+ if (char === " ") {
37197
+ columns += tabSize - columns % tabSize;
37198
+ continue;
37199
+ }
37200
+ columns += isWide(char) ? 2 : 1;
37201
+ }
37202
+ return columns;
37203
+ }
37204
+ /**
37205
+ * Whether a character takes two columns in a monospace font: CJK, Hangul,
37206
+ * kana, the full-width forms, and the emoji blocks. The ranges rather than a
37207
+ * table because this runs over a file's worth of text and the answer only
37208
+ * feeds an estimate.
37209
+ */
37210
+ function isWide(char) {
37211
+ const code = char.codePointAt(0) ?? 0;
37212
+ 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;
37213
+ }
37214
+ /**
37215
+ * The height a row is guessed to have before it has been measured.
37216
+ *
37217
+ * @param text - the row's text.
37218
+ * @param columns - how many columns fit across the pane; 0 or less means the
37219
+ * pane has not been measured yet, and one line is assumed.
37220
+ * @param rowH - one line's height in px.
37221
+ */
37222
+ function estimateRowHeight(text, columns, rowH) {
37223
+ if (!Number.isFinite(columns) || columns <= 0) return rowH;
37224
+ return Math.max(1, Math.ceil(displayColumns(text) / columns)) * rowH;
37225
+ }
37226
+ /**
37227
+ * Per-row heights with O(log n) offsets, backed by a Fenwick tree.
37228
+ *
37229
+ * Bounded by construction: two arrays of `rowCount`, which the panes already
37230
+ * cap. {@link measured} reports how much of it is real rather than guessed, so
37231
+ * a test can prove the measuring stays viewport-sized instead of trusting it.
37232
+ */
37233
+ var RowHeights = class {
37234
+ /** Each row's height. */
37235
+ heights;
37236
+ /** Fenwick sums, 1-based; `tree[i]` covers a block ending at row `i - 1`. */
37237
+ tree;
37238
+ /** Rows whose height came from the DOM rather than from an estimate. */
37239
+ real;
37240
+ realCount = 0;
37241
+ /**
37242
+ * @param initial - each row's starting height, normally an estimate.
37243
+ */
37244
+ constructor(initial) {
37245
+ const n = initial.length;
37246
+ this.heights = new Float64Array(n);
37247
+ this.tree = new Float64Array(n + 1);
37248
+ this.real = new Uint8Array(n);
37249
+ for (let i = 0; i < n; i += 1) this.heights[i] = sane(initial[i]);
37250
+ for (let i = 1; i <= n; i += 1) {
37251
+ this.tree[i] += this.heights[i - 1];
37252
+ const parent = i + (i & -i);
37253
+ if (parent <= n) this.tree[parent] += this.tree[i];
37254
+ }
37255
+ }
37256
+ /** How many rows there are. */
37257
+ get count() {
37258
+ return this.heights.length;
37259
+ }
37260
+ /** How many rows carry a measured height rather than an estimate. */
37261
+ measured() {
37262
+ return this.realCount;
37263
+ }
37264
+ /**
37265
+ * Record one row's real height.
37266
+ * @returns whether anything moved, so a caller can avoid a re-render.
37267
+ */
37268
+ set(index, px) {
37269
+ if (!Number.isInteger(index) || index < 0 || index >= this.count) return false;
37270
+ const next = sane(px);
37271
+ if (this.real[index] === 0) {
37272
+ this.real[index] = 1;
37273
+ this.realCount += 1;
37274
+ }
37275
+ const delta = next - this.heights[index];
37276
+ if (delta === 0) return false;
37277
+ this.heights[index] = next;
37278
+ for (let i = index + 1; i <= this.count; i += i & -i) this.tree[i] += delta;
37279
+ return true;
37280
+ }
37281
+ /** One row's height. */
37282
+ heightAt(index) {
37283
+ if (index < 0 || index >= this.count) return 0;
37284
+ return this.heights[index];
37285
+ }
37286
+ /** Where a row starts, measured from the top of the scrolled content. */
37287
+ topAt(index) {
37288
+ let sum = 0;
37289
+ for (let i = Math.max(0, Math.min(index, this.count)); i > 0; i -= i & -i) sum += this.tree[i];
37290
+ return sum;
37291
+ }
37292
+ /** Every row's height together. */
37293
+ total() {
37294
+ return this.topAt(this.count);
37295
+ }
37296
+ /**
37297
+ * The row that contains `y`, by binary lifting over the tree — the same
37298
+ * search a running-totals array would do, without the array.
37299
+ */
37300
+ rowAt(y) {
37301
+ if (this.count === 0) return 0;
37302
+ let remaining = Number.isFinite(y) && y > 0 ? y : 0;
37303
+ let index = 0;
37304
+ let step = 1;
37305
+ while (step * 2 <= this.count) step *= 2;
37306
+ for (; step > 0; step = Math.floor(step / 2)) {
37307
+ const probe = index + step;
37308
+ if (probe <= this.count && this.tree[probe] <= remaining) {
37309
+ remaining -= this.tree[probe];
37310
+ index = probe;
37311
+ }
37312
+ }
37313
+ return Math.min(index, this.count - 1);
37314
+ }
37315
+ };
37316
+ /** @param value - a number from the DOM, which can be NaN or negative. */
37317
+ function sane(value) {
37318
+ return Number.isFinite(value) && value > 0 ? value : 0;
37319
+ }
37320
+ /**
37321
+ * The window of rows to render when the rows are not all one height.
37322
+ *
37323
+ * Same answer shape as {@link rowWindow}, and the same short-circuit below
37324
+ * {@link WINDOW_WHOLE_BELOW}: windowing has costs of its own, and a file that
37325
+ * was never slow should render exactly as it did before.
37326
+ *
37327
+ * @param scrollTop - the scroller's current offset in px.
37328
+ * @param viewportH - the scroller's visible height in px; 0 before measuring.
37329
+ * @param heights - the rows' heights.
37330
+ * @param overscan - rows to keep beyond each edge.
37331
+ */
37332
+ function variableRowWindow(scrollTop, viewportH, heights, overscan = 40) {
37333
+ const rows = heights.count;
37334
+ if (rows <= 400) return {
37335
+ start: 0,
37336
+ end: rows,
37337
+ padTop: 0,
37338
+ padBottom: 0
37339
+ };
37340
+ const top = Number.isFinite(scrollTop) && scrollTop > 0 ? scrollTop : 0;
37341
+ const view = Number.isFinite(viewportH) && viewportH > 0 ? viewportH : ASSUMED_VIEWPORT_PX;
37342
+ const pad = Math.max(0, Math.trunc(overscan));
37343
+ const start = Math.max(0, heights.rowAt(top) - pad);
37344
+ const end = Math.min(rows, heights.rowAt(top + view) + 1 + pad);
37345
+ const safeEnd = Math.max(start, end);
37346
+ return {
37347
+ start,
37348
+ end: safeEnd,
37349
+ padTop: heights.topAt(start),
37350
+ padBottom: Math.max(0, heights.total() - heights.topAt(safeEnd))
37351
+ };
37352
+ }
37353
+ //#endregion
37354
+ //#region src/client/use-variable-row-window.ts
37355
+ /**
37356
+ * The windowing hook for panes whose rows are not all one height — a diff
37357
+ * pane with soft wrap on.
37358
+ *
37359
+ * The fixed-height hook beside this one (`use-row-window.ts`) needs nothing
37360
+ * from the DOM but the scroll offset, because `i * 20px` is the answer. Once
37361
+ * lines wrap, only the DOM knows how tall a row came out, so this one measures
37362
+ * — and measures ONLY the rows it just rendered, which is the window plus its
37363
+ * overscan. Every other row carries an estimate computed from its text, so the
37364
+ * scrollbar is close from the first paint instead of growing as the reader
37365
+ * scrolls.
37366
+ *
37367
+ * A measurement that replaces an estimate for a row ABOVE the viewport moves
37368
+ * everything below it, including what the reader is looking at. The scroll
37369
+ * offset is corrected by the same delta in the same layout pass, so the rows
37370
+ * on screen stay under the eye rather than sliding.
37371
+ *
37372
+ * @module @young1lin/dsh-ui-gitworkbench/client/use-variable-row-window
37373
+ */
37374
+ /** Attribute a pane puts on each measurable element, as `scope:index`. */
37375
+ const ROW_INDEX_ATTR = "data-gw-row";
37376
+ /**
37377
+ * What a pane writes on an element it wants measured.
37378
+ *
37379
+ * The scope is half the value rather than a second attribute because two
37380
+ * height models can be mounted inside one scroller — the aligned diff and the
37381
+ * dense index column beside the editor — and a bare index would let each read
37382
+ * the other's rows as its own.
37383
+ *
37384
+ * @param scope - which height model the element belongs to.
37385
+ * @param index - the row's index WITHIN that model.
37386
+ */
37387
+ function rowMark(scope, index) {
37388
+ return { [ROW_INDEX_ATTR]: `${scope}:${index}` };
37389
+ }
37390
+ /**
37391
+ * Columns that fit across a pane, from its width and one character's advance.
37392
+ *
37393
+ * Measured off a probe rather than assumed: the panes' font size is a theme
37394
+ * variable a reader can change, so a hard-coded character width would put the
37395
+ * estimate out by a third on the smallest setting.
37396
+ *
37397
+ * @param grid - the element the rows are laid out in.
37398
+ * @returns columns across, or 0 when the element is not laid out yet.
37399
+ */
37400
+ function columnsAcross(grid) {
37401
+ if (grid === null) return 0;
37402
+ const width = grid.clientWidth;
37403
+ if (!Number.isFinite(width) || width <= 0) return 0;
37404
+ const probe = document.createElement("span");
37405
+ probe.textContent = "0".repeat(100);
37406
+ probe.style.cssText = "position:absolute;visibility:hidden;white-space:pre;pointer-events:none";
37407
+ grid.appendChild(probe);
37408
+ const advance = probe.getBoundingClientRect().width / 100;
37409
+ probe.remove();
37410
+ if (!Number.isFinite(advance) || advance <= 0) return 0;
37411
+ return Math.max(1, Math.floor(width / advance));
37412
+ }
37413
+ function useVariableRowWindow({ scrollRef, rowsRef, widthRef, texts, mountKey, scope, enabled, rowH = 20 }) {
37414
+ /** Columns across the pane. State, because the estimate depends on it and a
37415
+ * dragged divider changes it. */
37416
+ const [columns, setColumns] = (0, react.useState)(0);
37417
+ /**
37418
+ * Bumped when a measurement moved a row.
37419
+ *
37420
+ * The heights live in a mutable structure, so changing one changes no
37421
+ * identity React watches — and the side-by-side pane reads them DURING
37422
+ * render, to impose each row's height on both of its cells. Without this
37423
+ * the first pass would measure correctly and then never re-render to apply
37424
+ * what it measured, which is a row that is one line taller on one side than
37425
+ * on the other. Bumped only when something actually moved, and imposing a
37426
+ * height does not change what is measured (the measured box is inside the
37427
+ * cell), so it settles in one extra pass.
37428
+ */
37429
+ const [, bumpHeights] = (0, react.useState)(0);
37430
+ const [win, setWin] = (0, react.useState)(() => ({
37431
+ start: 0,
37432
+ end: texts.length,
37433
+ padTop: 0,
37434
+ padBottom: 0
37435
+ }));
37436
+ /** Rebuilt only when the diff or the width really changes — never on scroll. */
37437
+ const heights = (0, react.useMemo)(() => new RowHeights(enabled ? texts.map((text) => estimateRowHeight(text, columns, rowH)) : []), [
37438
+ texts,
37439
+ columns,
37440
+ rowH,
37441
+ mountKey,
37442
+ enabled
37443
+ ]);
37444
+ const read = (0, react.useCallback)(() => {
37445
+ const el = scrollRef.current;
37446
+ if (el === null || heights.count === 0) return;
37447
+ const next = variableRowWindow(el.scrollTop, el.clientHeight, heights);
37448
+ setWin((prev) => sameRowWindow(prev, next) ? prev : next);
37449
+ }, [scrollRef, heights]);
37450
+ (0, react.useEffect)(() => {
37451
+ const el = scrollRef.current;
37452
+ if (el === null || !enabled) return;
37453
+ read();
37454
+ const observer = new ResizeObserver(() => {
37455
+ read();
37456
+ setColumns((prev) => {
37457
+ const measured = columnsAcross((widthRef ?? rowsRef).current);
37458
+ return measured === 0 || measured === prev ? prev : measured;
37459
+ });
37460
+ });
37461
+ el.addEventListener("scroll", read, { passive: true });
37462
+ observer.observe(el);
37463
+ return () => {
37464
+ el.removeEventListener("scroll", read);
37465
+ observer.disconnect();
37466
+ };
37467
+ }, [
37468
+ scrollRef,
37469
+ rowsRef,
37470
+ widthRef,
37471
+ read,
37472
+ enabled
37473
+ ]);
37474
+ (0, react.useLayoutEffect)(() => {
37475
+ const root = rowsRef.current;
37476
+ const scroller = scrollRef.current;
37477
+ if (!enabled || root === null || scroller === null) return;
37478
+ const before = heights.topAt(win.start);
37479
+ const tallest = /* @__PURE__ */ new Map();
37480
+ for (const node of root.querySelectorAll(`[${ROW_INDEX_ATTR}^="${scope}:"]`)) {
37481
+ const index = Number(node.getAttribute(ROW_INDEX_ATTR)?.slice(scope.length + 1));
37482
+ if (!Number.isInteger(index)) continue;
37483
+ tallest.set(index, Math.max(tallest.get(index) ?? 0, node.getBoundingClientRect().height));
37484
+ }
37485
+ let moved = false;
37486
+ for (const [index, height] of tallest) if (heights.set(index, height)) moved = true;
37487
+ if (!moved) return;
37488
+ const after = heights.topAt(win.start);
37489
+ if (after !== before) scroller.scrollTop += after - before;
37490
+ bumpHeights((version) => version + 1);
37491
+ read();
37492
+ });
37493
+ return {
37494
+ win,
37495
+ rowTop: (0, react.useCallback)((index) => enabled ? heights.topAt(index) : Math.max(0, Math.trunc(index)) * rowH, [
37496
+ enabled,
37497
+ heights,
37498
+ rowH
37499
+ ]),
37500
+ rowHeight: (0, react.useCallback)((index) => enabled ? heights.heightAt(index) : rowH, [
37501
+ enabled,
37502
+ heights,
37503
+ rowH
37504
+ ])
37505
+ };
37506
+ }
37507
+ //#endregion
37508
+ //#region src/client/diff-cells.tsx
37509
+ /** Classes that paint only a block's OUTER perimeter. Internal rows carry the
37510
+ * vertical edges but no top/bottom line, avoiding the blue ladder a large
37511
+ * addition block used to draw. Absent side-cells return no class at all. */
37512
+ function blockHotClass(rows, index, side, hot) {
37513
+ if (!hot) return "";
37514
+ const edge = blockEdge(rows, index, side);
37515
+ if (edge === null) return "";
37516
+ const first = edge === "first" || edge === "single" ? ` ${GitWorkbenchPanel_module_css_default.sideBlockHotFirst}` : "";
37517
+ const last = edge === "last" || edge === "single" ? ` ${GitWorkbenchPanel_module_css_default.sideBlockHotLast}` : "";
37518
+ return ` ${GitWorkbenchPanel_module_css_default.sideBlockHot}${first}${last}`;
37519
+ }
37520
+ /** Line-number cell class: a PRESENT cell of a changed row carries its side's
37521
+ * tint into the gutter; an absent one stays blank, the way a split diff shows
37522
+ * a one-sided change with an empty opposite pane rather than a tinted void. */
37523
+ function sideNumClass(row, side) {
37524
+ if ((side === "left" ? row.left : row.right) === null || row.kind === "same") return GitWorkbenchPanel_module_css_default.sideNum;
37525
+ return `${GitWorkbenchPanel_module_css_default.sideNum} ${side === "left" ? GitWorkbenchPanel_module_css_default.sideNumDel : GitWorkbenchPanel_module_css_default.sideNumAdd}`;
37526
+ }
37527
+ /** Code cell class: deletions tint left, additions right, context stays quiet. */
37528
+ function sideCodeClass(row, side) {
37529
+ if ((side === "left" ? row.left : row.right) === null || row.kind === "same") return GitWorkbenchPanel_module_css_default.sideCodeSame;
37530
+ return `${side === "left" ? GitWorkbenchPanel_module_css_default.sideCodeDel : GitWorkbenchPanel_module_css_default.sideCodeAdd} ${GitWorkbenchPanel_module_css_default.sideCellBlock}`;
37531
+ }
37532
+ /** One text with every carriage return drawn as the CR glyph. No CR means
37533
+ * the text comes back untouched — the common line, on both sides, costs one
37534
+ * `includes`. The glyph spans are aria-hidden and unselectable, so copying a
37535
+ * line copies code, not markers. */
37536
+ function renderWithCrMarks(text) {
37537
+ const parts = splitOnCr(text);
37538
+ if (parts.length === 1) return text;
37539
+ const out = [parts[0]];
37540
+ for (let i = 1; i < parts.length; i += 1) {
37541
+ out.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
37542
+ className: GitWorkbenchPanel_module_css_default.crMark,
37543
+ "aria-hidden": "true",
37544
+ children: "␍"
37545
+ }, `cr${i}`));
37546
+ out.push(parts[i]);
37547
+ }
37548
+ return out;
37549
+ }
37550
+ /** One cell's Shiki runs, or its plain text when no tokens exist; either way
37551
+ * each carriage return in the cell is drawn, so a line whose only change is
37552
+ * its ending shows the difference instead of two identical-looking cells. */
37553
+ function renderSideCode(cell, tokens) {
37554
+ if (cell === null) return "";
37555
+ if (tokens === void 0 || tokens.length === 0) return renderWithCrMarks(cell.text);
37556
+ if (tokens.length === 1 && tokens[0].color === void 0 && !tokens[0].italic) return renderWithCrMarks(cell.text);
37557
+ return tokens.map((tok, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
37558
+ style: tok.color === void 0 && !tok.italic ? void 0 : {
37559
+ color: tok.color,
37560
+ fontStyle: tok.italic ? "italic" : void 0
37561
+ },
37562
+ children: renderWithCrMarks(tok.text)
37563
+ }, i));
37564
+ }
37565
+ /**
37566
+ * The spacer standing in for the rows above or below the window.
37567
+ *
37568
+ * It spans every column of the grid, so a blame gutter does not change it.
37569
+ * @param height - px of rows it stands in for; nothing is rendered for 0.
37570
+ */
37571
+ function RowSpacer({ height }) {
37572
+ if (height <= 0) return null;
37573
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
37574
+ className: GitWorkbenchPanel_module_css_default.sideSpacer,
37575
+ style: { height: `${height}px` },
37576
+ "aria-hidden": "true"
37577
+ });
37578
+ }
37579
+ /**
37580
+ * One side of one aligned row: its line-number cell and its code cell.
37581
+ *
37582
+ * Written once and used by all three columns the pane draws — the left side of
37583
+ * a diff, the right side, and the dense index column beside the editor —
37584
+ * because the three differ only in which cell they read, what hangs the block
37585
+ * bar and whether a click arms the editor. Everything else about them, from
37586
+ * the block outline to the CR markers, is the same in all three and was
37587
+ * previously the same three times.
37588
+ */
37589
+ function SideCells({ row, side, index, rows, current, tokens, mark, minHeight, bar, armable, onArm }) {
37590
+ const hot = blockHotClass(rows, index, side, current);
37591
+ const cell = side === "left" ? row.left : row.right;
37592
+ const code = renderSideCode(cell, tokens);
37593
+ const box = minHeight !== void 0 && minHeight > 0 ? { minHeight: `${minHeight}px` } : void 0;
37594
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
37595
+ className: `${sideNumClass(row, side)}${hot}`,
37596
+ style: box,
37597
+ children: cell === null ? "" : cell.line
37598
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
37599
+ className: `${GitWorkbenchPanel_module_css_default.sideCode} ${sideCodeClass(row, side)}${hot}${armable === true ? ` ${GitWorkbenchPanel_module_css_default.sideArmable}` : ""}`,
37600
+ style: box,
37601
+ "data-block": cell !== null && row.block >= 0 ? row.block : void 0,
37602
+ onClick: onArm,
37603
+ children: [mark === void 0 ? code : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
37604
+ className: GitWorkbenchPanel_module_css_default.sideFlow,
37605
+ ...mark,
37606
+ children: code
37607
+ }), bar]
37608
+ })] });
37609
+ }
37610
+ //#endregion
36595
37611
  //#region src/client/DiffViews.tsx
36596
37612
  const SPLIT_MIN = .15;
36597
37613
  const SPLIT_MAX = .85;
@@ -36632,14 +37648,24 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
36632
37648
  * a block child of a scroller is only ever as wide as the scrollport. A header
36633
37649
  * outside the scrolled box has neither problem.
36634
37650
  */
36635
- function DiffView({ segment, path, palette, t }) {
37651
+ function DiffView({ segment, path, palette, t, wrap }) {
36636
37652
  const lang = shikiLangOf(path);
36637
37653
  const shikiTheme = shikiThemeOf(palette);
36638
37654
  const grammarGen = (0, react.useSyncExternalStore)(subscribeGrammarLoaded, grammarLoadCount);
36639
37655
  const rowsWithWords = (0, react.useMemo)(() => attachWordRanges(parseRows(segment)), [segment]);
36640
37656
  const sides = (0, react.useMemo)(() => gutterSides(rowsWithWords), [rowsWithWords]);
36641
37657
  const scrollRef = (0, react.useRef)(null);
36642
- const win = useRowWindow(scrollRef, rowsWithWords.length, path);
37658
+ const preRef = (0, react.useRef)(null);
37659
+ const fixed = useRowWindow(scrollRef, rowsWithWords.length, path, !wrap);
37660
+ const flow = useVariableRowWindow({
37661
+ scrollRef,
37662
+ rowsRef: preRef,
37663
+ texts: (0, react.useMemo)(() => rowsWithWords.map((row) => row.text), [rowsWithWords]),
37664
+ mountKey: path,
37665
+ scope: "u",
37666
+ enabled: wrap
37667
+ });
37668
+ const win = wrap ? flow.win : fixed;
36643
37669
  const syntax = (0, react.useMemo)(() => highlightForRowsWindow(rowsWithWords, lang, shikiTheme, win.start, win.end), [
36644
37670
  rowsWithWords,
36645
37671
  lang,
@@ -36652,7 +37678,9 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
36652
37678
  const changes = (0, react.useMemo)(() => countBlocks(blocks), [blocks]);
36653
37679
  const blocksForNav = (0, react.useRef)(blocks);
36654
37680
  blocksForNav.current = blocks;
36655
- const { goToChange } = useChangeNav(scrollRef, (0, react.useCallback)(() => blockTopsFromRows(blocksForNav.current, 20, 8), []));
37681
+ const placeRow = (0, react.useRef)(void 0);
37682
+ placeRow.current = wrap ? flow.rowTop : void 0;
37683
+ const { goToChange } = useChangeNav(scrollRef, (0, react.useCallback)(() => blockTopsFromRows(blocksForNav.current, 20, 8, placeRow.current), []));
36656
37684
  const walk = (0, react.useRef)(goToChange);
36657
37685
  walk.current = goToChange;
36658
37686
  (0, react.useEffect)(() => {
@@ -36703,7 +37731,8 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
36703
37731
  className: GitWorkbenchPanel_module_css_default.diffScroll,
36704
37732
  tabIndex: -1,
36705
37733
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("pre", {
36706
- className: GitWorkbenchPanel_module_css_default.diffPre,
37734
+ ref: preRef,
37735
+ className: wrap ? `${GitWorkbenchPanel_module_css_default.diffPre} ${GitWorkbenchPanel_module_css_default.diffPreWrap}` : GitWorkbenchPanel_module_css_default.diffPre,
36707
37736
  children: [
36708
37737
  win.padTop > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
36709
37738
  className: GitWorkbenchPanel_module_css_default.diffSpacer,
@@ -36715,6 +37744,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
36715
37744
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
36716
37745
  className: `${GitWorkbenchPanel_module_css_default.line} ${rowClass(row.kind)}`,
36717
37746
  "data-block": blocks[i] >= 0 ? blocks[i] : void 0,
37747
+ ...rowMark("u", i),
36718
37748
  children: [
36719
37749
  sides.old ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
36720
37750
  className: GitWorkbenchPanel_module_css_default.lnOld,
@@ -36799,7 +37829,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
36799
37829
  * (history and compare keep it unconditionally), with a notice — a silently
36800
37830
  * different view reads as a broken one, not a guarded one.
36801
37831
  */
36802
- function SideBySideView({ t, path, palette, statsPath, fetchSides, writeChecked, scopeKey, gen, fallbackSegment, fallbackLoading, onBlockAction, onSaved, onDirtyChange }) {
37832
+ function SideBySideView({ t, path, palette, wrap, statsPath, fetchSides, writeChecked, scopeKey, gen, fallbackSegment, fallbackLoading, phantomListed, onBlockAction, onSaved, onDirtyChange }) {
36803
37833
  const [layer, setLayer] = (0, react.useState)("unstaged");
36804
37834
  const [split, setSplit] = (0, react.useState)(.5);
36805
37835
  const colsRef = (0, react.useRef)(null);
@@ -36898,7 +37928,25 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
36898
37928
  const alignedBlockTops = (0, react.useMemo)(() => blockTopsFromRows(rows.map((row) => row.block), 20, navOffset), [rows, navOffset]);
36899
37929
  const editorBlockTops = (0, react.useMemo)(() => blockTopsFromSideRows(rows, "right", 20, navOffset), [rows, navOffset]);
36900
37930
  const rowWindowKey = `${scopeKey}\x1f${path}\x1f${layer}\x1f${sides?.diffSha ?? ""}`;
36901
- const win = useRowWindow(scrollRef, rows.length, rowWindowKey);
37931
+ const alignedGridRef = (0, react.useRef)(null);
37932
+ const denseGridRef = (0, react.useRef)(null);
37933
+ const leftColRef = (0, react.useRef)(null);
37934
+ const rightColRef = (0, react.useRef)(null);
37935
+ const fixedWin = useRowWindow(scrollRef, rows.length, rowWindowKey, !wrap);
37936
+ const flow = useVariableRowWindow({
37937
+ scrollRef,
37938
+ rowsRef: colsRef,
37939
+ widthRef: alignedGridRef,
37940
+ texts: (0, react.useMemo)(() => rows.map((row) => {
37941
+ const left = row.left?.text ?? "";
37942
+ const right = row.right?.text ?? "";
37943
+ return left.length >= right.length ? left : right;
37944
+ }), [rows]),
37945
+ mountKey: rowWindowKey,
37946
+ scope: "a",
37947
+ enabled: wrap
37948
+ });
37949
+ const win = wrap ? flow.win : fixedWin;
36902
37950
  const leftLines = (0, react.useMemo)(() => rows.map((row) => row.left === null ? "" : row.left.text), [rows]);
36903
37951
  const rightLines = (0, react.useMemo)(() => rows.map((row) => row.right === null ? "" : row.right.text), [rows]);
36904
37952
  const leftSyntax = (0, react.useMemo)(() => highlightWindow(leftLines, lang, shikiTheme, win.start, win.end), [
@@ -36949,7 +37997,16 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
36949
37997
  row,
36950
37998
  i
36951
37999
  })).filter((entry) => entry.row.left !== null), [rows]);
36952
- const leftWin = useRowWindow(scrollRef, leftRows.length, rowWindowKey);
38000
+ const fixedLeftWin = useRowWindow(scrollRef, leftRows.length, rowWindowKey, !wrap);
38001
+ const leftFlow = useVariableRowWindow({
38002
+ scrollRef,
38003
+ rowsRef: denseGridRef,
38004
+ texts: (0, react.useMemo)(() => leftRows.map((entry) => entry.row.left?.text ?? ""), [leftRows]),
38005
+ mountKey: rowWindowKey,
38006
+ scope: "d",
38007
+ enabled: wrap
38008
+ });
38009
+ const leftWin = wrap ? leftFlow.win : fixedLeftWin;
36953
38010
  /** Arm the editor from the payload on screen; the unstaged tab, and only
36954
38011
  * for a payload `editableSides` accepts — armEdit itself refuses the rest,
36955
38012
  * so even a stray call cannot put CRLF text into the buffer. */
@@ -37144,10 +38201,11 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
37144
38201
  segment: fallbackSegment,
37145
38202
  path,
37146
38203
  palette,
37147
- t
38204
+ t,
38205
+ wrap
37148
38206
  }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
37149
38207
  className: GitWorkbenchPanel_module_css_default.empty,
37150
- children: fallbackLoading ? t("loadingDiff") : t("noTextDiff")
38208
+ children: fallbackLoading ? t("loadingDiff") : phantomListed ? t("phantomNotice") : t("noTextDiff")
37151
38209
  });
37152
38210
  if (failed) return unifiedFallback();
37153
38211
  if (sides === null) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
@@ -37369,8 +38427,8 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
37369
38427
  className: GitWorkbenchPanel_module_css_default.sideScroll,
37370
38428
  children: bodyState.kind === "empty" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
37371
38429
  className: GitWorkbenchPanel_module_css_default.empty,
37372
- children: t("noTextDiff")
37373
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
38430
+ children: phantomListed ? t("phantomNotice") : t("noTextDiff")
38431
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
37374
38432
  ref: colsRef,
37375
38433
  className: GitWorkbenchPanel_module_css_default.sideCols,
37376
38434
  onMouseOver: onBodyHover,
@@ -37380,29 +38438,31 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
37380
38438
  },
37381
38439
  children: [
37382
38440
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
37383
- className: GitWorkbenchPanel_module_css_default.sideCol,
38441
+ ref: leftColRef,
38442
+ className: wrap ? `${GitWorkbenchPanel_module_css_default.sideCol} ${GitWorkbenchPanel_module_css_default.sideColWrap}` : GitWorkbenchPanel_module_css_default.sideCol,
37384
38443
  style: {
37385
38444
  flexBasis: `${split * 100}%`,
37386
38445
  paddingTop: blockBarClearance
37387
38446
  },
37388
38447
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
37389
- className: GitWorkbenchPanel_module_css_default.sideColGrid,
38448
+ ref: bodyState.kind === "editor" ? denseGridRef : alignedGridRef,
38449
+ className: wrap ? `${GitWorkbenchPanel_module_css_default.sideColGrid} ${GitWorkbenchPanel_module_css_default.sideColGridWrap}` : GitWorkbenchPanel_module_css_default.sideColGrid,
37390
38450
  children: bodyState.kind === "editor" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
37391
38451
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RowSpacer, { height: leftWin.padTop }),
37392
38452
  leftRows.slice(leftWin.start, leftWin.end).map((entry, kk) => {
37393
38453
  const k = leftWin.start + kk;
37394
38454
  const { row, i } = entry;
37395
38455
  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}`);
38456
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SideCells, {
38457
+ row,
38458
+ side: "left",
38459
+ index: i,
38460
+ rows,
38461
+ current: row.block >= 0 && row.block === currentBlock,
38462
+ tokens: leftSyntax?.[i],
38463
+ mark: wrap ? rowMark("d", k) : void 0,
38464
+ bar: hot && k === hotFirstLeft ? blockBar(row.block) : null
38465
+ }, `l${i}`);
37406
38466
  }),
37407
38467
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RowSpacer, { height: leftWin.padBottom })
37408
38468
  ] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
@@ -37410,17 +38470,17 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
37410
38470
  rows.slice(win.start, win.end).map((row, k) => {
37411
38471
  const i = win.start + k;
37412
38472
  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);
38473
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SideCells, {
38474
+ row,
38475
+ side: "left",
38476
+ index: i,
38477
+ rows,
38478
+ current: row.block >= 0 && row.block === currentBlock,
38479
+ tokens: leftSyntax?.[i],
38480
+ mark: wrap ? rowMark("a", i) : void 0,
38481
+ minHeight: wrap ? flow.rowHeight(i) : void 0,
38482
+ bar: hot && i === hotFirst && row.right === null ? blockBar(row.block) : null
38483
+ }, i);
37424
38484
  }),
37425
38485
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RowSpacer, { height: win.padBottom })
37426
38486
  ] })
@@ -37431,7 +38491,8 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
37431
38491
  onDrag: onSplitDrag
37432
38492
  }),
37433
38493
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
37434
- className: `${GitWorkbenchPanel_module_css_default.sideCol} ${GitWorkbenchPanel_module_css_default.sideColRight}`,
38494
+ ref: rightColRef,
38495
+ 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
38496
  style: { paddingTop: blockBarClearance },
37436
38497
  children: bodyState.kind === "editor" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CodeEditor, {
37437
38498
  value: edit.buffer,
@@ -37447,33 +38508,39 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
37447
38508
  ariaLabel: path,
37448
38509
  onSave: () => {
37449
38510
  if (dirty && !saving) runSave(edit.baseSha);
37450
- }
38511
+ },
38512
+ wrap
37451
38513
  }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
37452
- className: GitWorkbenchPanel_module_css_default.sideColGrid,
38514
+ className: wrap ? `${GitWorkbenchPanel_module_css_default.sideColGrid} ${GitWorkbenchPanel_module_css_default.sideColGridWrap}` : GitWorkbenchPanel_module_css_default.sideColGrid,
37453
38515
  children: [
37454
38516
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RowSpacer, { height: win.padTop }),
37455
38517
  rows.slice(win.start, win.end).map((row, k) => {
37456
38518
  const i = win.start + k;
37457
38519
  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);
38520
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SideCells, {
38521
+ row,
38522
+ side: "right",
38523
+ index: i,
38524
+ rows,
38525
+ current: row.block >= 0 && row.block === currentBlock,
38526
+ tokens: rightSyntax?.[i],
38527
+ mark: wrap ? rowMark("a", i) : void 0,
38528
+ minHeight: wrap ? flow.rowHeight(i) : void 0,
38529
+ bar: hot && i === hotFirst && row.right !== null ? blockBar(row.block) : null,
38530
+ armable: layer === "unstaged" && armable,
38531
+ onArm: layer === "unstaged" && armable ? armFromCell : void 0
38532
+ }, i);
37470
38533
  }),
37471
38534
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RowSpacer, { height: win.padBottom })
37472
38535
  ]
37473
38536
  })
37474
38537
  })
37475
38538
  ]
37476
- })
38539
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SideRails, {
38540
+ leftRef: leftColRef,
38541
+ rightRef: rightColRef,
38542
+ split
38543
+ })] })
37477
38544
  }),
37478
38545
  pendingLayer !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LeaveEditsConfirm, {
37479
38546
  t,
@@ -37547,76 +38614,6 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
37547
38614
  })
37548
38615
  });
37549
38616
  }
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
38617
  //#endregion
37621
38618
  //#region src/client/ChromeGlyph.tsx
37622
38619
  /**
@@ -37632,7 +38629,8 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
37632
38629
  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
38630
  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
38631
  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"
38632
+ 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",
38633
+ 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
38634
  };
37637
38635
  function ChromeGlyph({ of }) {
37638
38636
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
@@ -39251,6 +40249,10 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
39251
40249
  * field of the appearance object: that one is about colour, and this choice
39252
40250
  * has to survive a build that adds a palette. */
39253
40251
  const STORE_HISTORY_LAYOUT = "dsh-ui-gitworkbench:history-layout";
40252
+ /** Soft wrap, on or off. A reading preference rather than a project setting:
40253
+ * it says how THIS person wants long lines shown, so it lives beside the
40254
+ * pane sizes in localStorage rather than in the shared style store. */
40255
+ const STORE_WRAP = "dsh-ui-gitworkbench:wrap";
39254
40256
  const DEFAULT_PANES = {
39255
40257
  commits: null,
39256
40258
  tree: null,
@@ -39320,7 +40322,10 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
39320
40322
  * unvisited worktree does not re-render the browser on every pass. */
39321
40323
  const EMPTY_TREE = {
39322
40324
  paths: [],
39323
- truncated: false
40325
+ truncated: false,
40326
+ ignored: [],
40327
+ ignoredTruncated: false,
40328
+ children: NO_IGNORED_READS
39324
40329
  };
39325
40330
  /** The overlay with nothing on it — one instance, so an empty overlay never
39326
40331
  * re-renders the tree that receives it. */
@@ -39346,7 +40351,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
39346
40351
  for (const preferred of ["main", "master"]) if (branches.includes(preferred) && preferred !== head) return preferred;
39347
40352
  return branches.find((branch) => branch !== head) ?? "";
39348
40353
  }
39349
- function GitWorkbenchPanel({ sessionId, useSessions, t, fetchStats, fetchFileDiff, fetchFileSides, writeChecked, fetchBlame, fetchFileImage, fetchWorktreeStatus, fetchSessionBinding, fetchCommitStats, fetchCommits, fetchAuthors, fetchRepoTree, fetchCompare, fetchStyle, saveStyle, fetchSync, runGitOp, fetchDiscardPlan }) {
40354
+ 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
40355
  const worktreePath = useSessions((state) => state?.byId?.[sessionId]?.cwd);
39351
40356
  /** Whether the session's agent has a turn in flight — the store mirrors it
39352
40357
  * live, so it is the signal for polling faster while there is something to
@@ -39367,10 +40372,10 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
39367
40372
  const rememberPlace = (0, react.useCallback)((key, next) => {
39368
40373
  setFilesPlaces((prev) => withPlace(prev, key, next));
39369
40374
  }, []);
39370
- const rememberTree = (0, react.useCallback)((key, next) => {
40375
+ const rememberTree = (0, react.useCallback)((key, update) => {
39371
40376
  setFilesTrees((prev) => {
39372
40377
  const map = new Map(prev);
39373
- map.set(key, next);
40378
+ map.set(key, update(prev.get(key) ?? EMPTY_TREE));
39374
40379
  return map;
39375
40380
  });
39376
40381
  }, []);
@@ -39443,6 +40448,10 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
39443
40448
  * the row height it implies is published from.
39444
40449
  */
39445
40450
  const [historyLayout, setHistoryLayout] = (0, react.useState)(() => readStored(STORE_HISTORY_LAYOUT, isHistoryLayout, DEFAULT_HISTORY_LAYOUT));
40451
+ /** Soft wrap. Default OFF, which is what the panes have always done: code is
40452
+ * written in columns, and wrapping it is a choice about one long file, not
40453
+ * a better default for every file. */
40454
+ const [wrap, setWrap] = (0, react.useState)(() => readStored(STORE_WRAP, (value) => typeof value === "boolean", false));
39446
40455
  /** Per-project and global styling; both scopes, unresolved. */
39447
40456
  const [style, setStyle] = (0, react.useState)(EMPTY_SETTINGS);
39448
40457
  /** Whether dsh's resolved palette is currently dark. */
@@ -40113,6 +41122,21 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
40113
41122
  setHistoryLayout(next);
40114
41123
  writeStored(STORE_HISTORY_LAYOUT, next);
40115
41124
  };
41125
+ /**
41126
+ * Turn soft wrap on or off, and remember it.
41127
+ *
41128
+ * A plain function, like every other handler below the two guards above:
41129
+ * those `return null`s are the reason nothing past this point may be a HOOK.
41130
+ * A `useCallback` here rendered fewer hooks than the previous pass on the
41131
+ * frame the first stats arrived, which React reports as error #310 and the
41132
+ * shell reports as "slot entry crashed" — the chip simply vanishes.
41133
+ */
41134
+ const toggleWrap = () => {
41135
+ setWrap((prev) => {
41136
+ writeStored(STORE_WRAP, !prev);
41137
+ return !prev;
41138
+ });
41139
+ };
40116
41140
  /** Tab switch. No direction refetches the working tree: `viewKey` already
40117
41141
  * separates the tabs' per-file diff caches, so bumping `gen` here only cost a
40118
41142
  * redundant round trip. */
@@ -40212,6 +41236,8 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
40212
41236
  onHistoryLayout: applyHistoryLayout,
40213
41237
  onClose: () => setOpen(false),
40214
41238
  onRefresh: refresh,
41239
+ wrap,
41240
+ onToggleWrap: toggleWrap,
40215
41241
  commitDraft,
40216
41242
  onCommitDraft: setCommitDraft,
40217
41243
  commitAmend,
@@ -40229,6 +41255,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
40229
41255
  writeChecked,
40230
41256
  fetchBlame,
40231
41257
  fetchFileImage,
41258
+ fetchIgnoredDir,
40232
41259
  viewKey,
40233
41260
  gen,
40234
41261
  collapsed,
@@ -40290,7 +41317,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
40290
41317
  ]
40291
41318
  });
40292
41319
  }
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 }) {
41320
+ 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
41321
  const body = shown ?? EMPTY_STATS;
40295
41322
  /** The file list with ticks still awaiting git laid over them. The tree and
40296
41323
  * the commit box read this, so a click moves its box and the "N ticked"
@@ -40315,8 +41342,8 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
40315
41342
  const rememberPlaceHere = (0, react.useCallback)((next) => {
40316
41343
  onFilesPlace(filesKey, next);
40317
41344
  }, [onFilesPlace, filesKey]);
40318
- const rememberTreeHere = (0, react.useCallback)((next) => {
40319
- onFilesTree(filesKey, next);
41345
+ const rememberTreeHere = (0, react.useCallback)((update) => {
41346
+ onFilesTree(filesKey, update);
40320
41347
  }, [onFilesTree, filesKey]);
40321
41348
  const browsablePaths = (0, react.useMemo)(() => stats.files.filter((file) => file.status !== "deleted").map((file) => file.path), [stats.files]);
40322
41349
  const active = preferredFile(body.files, activeFilterPaths, selected);
@@ -40329,6 +41356,11 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
40329
41356
  const bundled = active === null ? "" : segments.get(active) ?? "";
40330
41357
  const activeKey = active === null ? null : `${viewKey}\x1f${active}`;
40331
41358
  const segment = bundled.length > 0 ? bundled : activeKey === null ? "" : fetched.get(activeKey) ?? "";
41359
+ /** The CRLF phantom: listed modified, whole-file diff empty (see
41360
+ * {@link isPhantomModified}). Changes-tab only — history and compare list
41361
+ * files from real ref diffs, where an empty segment means a failed fetch,
41362
+ * and the phantom notice would mislead. */
41363
+ const phantomListed = tab === "changes" && isPhantomModified(activeFile?.status, segment);
40332
41364
  (0, react.useEffect)(() => {
40333
41365
  const path = active;
40334
41366
  if (path === null || activeKey === null || bundled.length > 0) return;
@@ -40683,6 +41715,15 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
40683
41715
  onClick: onToggleMaximized,
40684
41716
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChromeGlyph, { of: maximized ? "restore" : "maximize" })
40685
41717
  }),
41718
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
41719
+ type: "button",
41720
+ 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}`,
41721
+ "aria-pressed": wrap,
41722
+ "aria-label": wrap ? t("wrapLinesOff") : t("wrapLines"),
41723
+ title: wrap ? t("wrapLinesOff") : t("wrapLines"),
41724
+ onClick: onToggleWrap,
41725
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChromeGlyph, { of: "wrap" })
41726
+ }),
40686
41727
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
40687
41728
  type: "button",
40688
41729
  className: `${GitWorkbenchPanel_module_css_default.btn} ${GitWorkbenchPanel_module_css_default.btnIcon}`,
@@ -40841,7 +41882,9 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
40841
41882
  onPlace: rememberPlaceHere,
40842
41883
  cached: filesTrees.get(filesKey) ?? EMPTY_TREE,
40843
41884
  onTree: rememberTreeHere,
41885
+ wrap,
40844
41886
  fetchRepoTree,
41887
+ fetchIgnoredDir,
40845
41888
  fetchFileSides,
40846
41889
  writeChecked,
40847
41890
  fetchBlame,
@@ -40893,48 +41936,61 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
40893
41936
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
40894
41937
  className: GitWorkbenchPanel_module_css_default.diffPane,
40895
41938
  "data-gs-part": "diff",
40896
- children: [tab === "compare" && !comparable ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
40897
- className: GitWorkbenchPanel_module_css_default.empty,
40898
- children: t("comparePick")
40899
- }) : shown === null && tab !== "changes" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
40900
- className: GitWorkbenchPanel_module_css_default.empty,
40901
- children: tab === "compare" ? t("loadingCompare") : t("loadingCommit")
40902
- }) : activeFile !== null && activeFile.previousPath !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
40903
- className: GitWorkbenchPanel_module_css_default.renameLine,
40904
- children: [
40905
- t("renamedFrom"),
40906
- " ",
40907
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", { children: activeFile.previousPath })
40908
- ]
40909
- }) : null, shown === null && tab !== "changes" || tab === "compare" && !comparable ? null : activeFile !== null && activeFile.binary ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
40910
- className: GitWorkbenchPanel_module_css_default.empty,
40911
- children: t("binaryFile")
40912
- }) : tab === "changes" && active !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SideBySideView, {
40913
- t,
40914
- path: active,
40915
- palette: theme,
40916
- statsPath,
40917
- fetchSides: fetchFileSides,
40918
- writeChecked,
40919
- scopeKey: viewKey,
40920
- gen,
40921
- fallbackSegment: segment,
40922
- fallbackLoading: loading && segment.length === 0,
40923
- onBlockAction: askBlockAction,
40924
- onSaved: onRefresh,
40925
- onDirtyChange: onSideDirty
40926
- }) : loading && segment.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
40927
- className: GitWorkbenchPanel_module_css_default.empty,
40928
- children: t("loadingDiff")
40929
- }) : segment.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DiffView, {
40930
- segment,
40931
- path: active ?? "",
40932
- palette: theme,
40933
- t
40934
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
40935
- className: GitWorkbenchPanel_module_css_default.empty,
40936
- children: t("noTextDiff")
40937
- })]
41939
+ children: [
41940
+ tab === "compare" && !comparable ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
41941
+ className: GitWorkbenchPanel_module_css_default.empty,
41942
+ children: t("comparePick")
41943
+ }) : shown === null && tab !== "changes" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
41944
+ className: GitWorkbenchPanel_module_css_default.empty,
41945
+ children: tab === "compare" ? t("loadingCompare") : t("loadingCommit")
41946
+ }) : activeFile !== null && activeFile.previousPath !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
41947
+ className: GitWorkbenchPanel_module_css_default.renameLine,
41948
+ children: [
41949
+ t("renamedFrom"),
41950
+ " ",
41951
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", { children: activeFile.previousPath })
41952
+ ]
41953
+ }) : null,
41954
+ tab === "compare" && comparable && shown !== null && body.files.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
41955
+ className: GitWorkbenchPanel_module_css_default.empty,
41956
+ children: t("compareEmptyHint", {
41957
+ base: baseRef ?? "",
41958
+ head: headRef ?? ""
41959
+ })
41960
+ }) : null,
41961
+ shown === null && tab !== "changes" || tab === "compare" && !comparable ? null : activeFile !== null && activeFile.binary ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
41962
+ className: GitWorkbenchPanel_module_css_default.empty,
41963
+ children: t("binaryFile")
41964
+ }) : tab === "changes" && active !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SideBySideView, {
41965
+ t,
41966
+ path: active,
41967
+ palette: theme,
41968
+ wrap,
41969
+ statsPath,
41970
+ fetchSides: fetchFileSides,
41971
+ writeChecked,
41972
+ scopeKey: viewKey,
41973
+ gen,
41974
+ fallbackSegment: segment,
41975
+ fallbackLoading: loading && segment.length === 0,
41976
+ phantomListed,
41977
+ onBlockAction: askBlockAction,
41978
+ onSaved: onRefresh,
41979
+ onDirtyChange: onSideDirty
41980
+ }) : loading && segment.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
41981
+ className: GitWorkbenchPanel_module_css_default.empty,
41982
+ children: t("loadingDiff")
41983
+ }) : segment.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DiffView, {
41984
+ segment,
41985
+ path: active ?? "",
41986
+ palette: theme,
41987
+ t,
41988
+ wrap
41989
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
41990
+ className: GitWorkbenchPanel_module_css_default.empty,
41991
+ children: t("noTextDiff")
41992
+ })
41993
+ ]
40938
41994
  })
40939
41995
  ] })
40940
41996
  })]
@@ -40998,6 +42054,11 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
40998
42054
  tabFiles: "文件",
40999
42055
  fileSearchPlaceholder: "搜索文件…",
41000
42056
  filesTruncated: "文件太多,列表已截断;用搜索找剩下的。",
42057
+ filesIgnored: "已被 gitignore",
42058
+ wrapLines: "自动换行",
42059
+ wrapLinesOff: "取消自动换行",
42060
+ filesIgnoredCut: "这个目录条目太多,已截断;搜索找不到没列出的部分。",
42061
+ filesIgnoredFailed: "无法列出被忽略的文件。",
41001
42062
  filesEmpty: "这个仓库还没有文件",
41002
42063
  filesNoMatch: "没有匹配的文件",
41003
42064
  filesMore: "还有 {count} 个,用上面的搜索找",
@@ -41019,6 +42080,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
41019
42080
  compareHead: "对比",
41020
42081
  comparePick: "选择两个不同的分支进行对比",
41021
42082
  compareCommits: "{count} 个提交(自共同祖先起)",
42083
+ compareEmptyHint: "{base}...{head} 比较的是自分叉点到 {head} 的变化,没有找到差异;想看另一方向的变化请交换两端。",
41022
42084
  loadingCompare: "加载对比…",
41023
42085
  noBranches: "没有可对比的分支",
41024
42086
  refSearch: "筛选分支…",
@@ -41175,6 +42237,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
41175
42237
  blameTruncated: "文件过长,追溯信息只显示了前面一部分。",
41176
42238
  crlfNotice: "这个文件的行尾是 CRLF,暂不支持在线编辑(编辑框会把行尾统一成 LF,保存时整份文件都会被改写);建议把行尾统一成 LF。差异里的回车已用 ␍ 标出;查看和按块暂存/撤回不受影响。",
41177
42239
  encodingNotice: "这个文件不是 UTF-8 编码(可能是 GBK、Shift JIS 之类),暂不支持在线编辑:页面上看到的文字是一次有损解码,保存回去会把文件里每一个非 ASCII 字节都改写掉,包括你没动过的行。查看和按块暂存/撤回不受影响。",
42240
+ phantomNotice: "git 把这个文件列为已修改,但行尾归一化(CRLF / autocrlf / eol 属性)之后没有内容差异——这是 Windows 检出常见的「永远已修改」幻影,不是显示故障。统一行尾(建议把行尾统一成 LF)或让 git 重新比对后,这一条目就会消失。",
41178
42241
  saveFailed: "保存失败",
41179
42242
  saveUnavailable: "当前宿主还不支持保存(需要重启 dsh web 加载新版宿主端)。",
41180
42243
  saveRetry: "重试保存",
@@ -41212,6 +42275,11 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
41212
42275
  tabFiles: "Files",
41213
42276
  fileSearchPlaceholder: "Search files…",
41214
42277
  filesTruncated: "Too many files to list; use the search for the rest.",
42278
+ filesIgnored: "Ignored (gitignore)",
42279
+ wrapLines: "Wrap long lines",
42280
+ wrapLinesOff: "Stop wrapping lines",
42281
+ filesIgnoredCut: "This directory has too many entries; the list is cut, and the search cannot reach the rest.",
42282
+ filesIgnoredFailed: "Could not list the ignored files.",
41215
42283
  filesEmpty: "This repository has no files yet",
41216
42284
  filesNoMatch: "No matching files",
41217
42285
  filesMore: "and {count} more — use the search above",
@@ -41233,6 +42301,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
41233
42301
  compareHead: "Compare",
41234
42302
  comparePick: "Pick two different branches to compare",
41235
42303
  compareCommits: "{count} commits since they diverged",
42304
+ compareEmptyHint: "{base}...{head} compares from the fork point up to {head} and found no differences; to see what changed on the other side, swap the two ends.",
41236
42305
  loadingCompare: "Loading comparison…",
41237
42306
  noBranches: "No branches to compare",
41238
42307
  refSearch: "Filter branches…",
@@ -41389,6 +42458,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
41389
42458
  blameTruncated: "The file is long, so blame is shown for the first part only.",
41390
42459
  crlfNotice: "This file has CRLF line endings, which the editor does not support yet (the edit box would turn every ending into LF, so a save rewrites the whole file); normalising the endings to LF is recommended. Carriage returns are marked ␍ in the diff; viewing and block staging/rolling back still work.",
41391
42460
  encodingNotice: "This file is not UTF-8 (GBK, Shift JIS or similar), so the editor is unavailable: the text shown is a lossy decode of it, and saving that back would rewrite every non-ASCII byte in the file, including lines you never touched. Viewing and block staging/rolling back still work.",
42461
+ phantomNotice: "git lists this file as modified, but after line-ending normalisation (CRLF / autocrlf / eol attributes) there is no content difference — the classic \"modified forever\" phantom of a Windows checkout, not a broken view. Once the endings agree (normalising the endings to LF is recommended) the entry disappears.",
41392
42462
  saveFailed: "Save failed",
41393
42463
  saveUnavailable: "This host does not support saving yet — restart dsh web to load the new host half.",
41394
42464
  saveRetry: "Retry save",
@@ -41516,6 +42586,17 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
41516
42586
  const result = await connection.rpc.call("/api", "gitWorkbench/repoTree", { args: { worktreePath: worktreePath ?? "" } }, signal);
41517
42587
  return result.ok ? result.value : null;
41518
42588
  },
42589
+ fetchIgnoredDir: async (worktreePath, dir, signal) => {
42590
+ try {
42591
+ const result = await connection.rpc.call("/api", "gitWorkbench/ignoredDir", { args: {
42592
+ worktreePath: worktreePath ?? "",
42593
+ dir
42594
+ } }, signal);
42595
+ return result.ok ? result.value : null;
42596
+ } catch {
42597
+ return null;
42598
+ }
42599
+ },
41519
42600
  fetchCompare: async (worktreePath, base, head, signal) => {
41520
42601
  const result = await connection.rpc.call("/api", "gitWorkbench/compareRefs", { args: {
41521
42602
  worktreePath: worktreePath ?? "",