dsh-diff-approval 0.11.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -3
- package/README.zh.md +9 -3
- package/lib/client.js +1650 -313
- package/lib/index.js +24 -30
- package/lib/types/client/PendingPanel.d.ts +47 -1
- package/lib/types/client/conversation-access.d.ts +67 -0
- package/lib/types/client/index.d.ts +2 -1
- package/lib/types/client/locales.d.ts +16 -0
- package/lib/types/client/reference.d.ts +32 -2
- package/lib/types/client/remap-sync.d.ts +44 -0
- package/lib/types/client/settings.d.ts +29 -0
- package/lib/types/client/slots.d.ts +8 -0
- package/lib/types/client/split-diff.d.ts +51 -0
- package/lib/types/client/store.d.ts +2 -0
- package/lib/types/types.d.ts +3 -0
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -18,6 +18,7 @@ window.__ModuleLoader__.load({
|
|
|
18
18
|
//#endregion
|
|
19
19
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
20
20
|
let react = require("react");
|
|
21
|
+
let react_dom = require("react-dom");
|
|
21
22
|
let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
22
23
|
//#region node_modules/.pnpm/diff@9.0.0/node_modules/diff/libesm/diff/base.js
|
|
23
24
|
var Diff = class {
|
|
@@ -654,6 +655,16 @@ window.__ModuleLoader__.load({
|
|
|
654
655
|
function computeWholeFileDiff(oldText, newText) {
|
|
655
656
|
oldText = oldText.replace(/\r\n?/g, "\n");
|
|
656
657
|
newText = newText.replace(/\r\n?/g, "\n");
|
|
658
|
+
if (oldText === newText) return {
|
|
659
|
+
rows: contentLines(oldText).map((text, index) => ({
|
|
660
|
+
kind: "context",
|
|
661
|
+
text,
|
|
662
|
+
oldLine: index + 1,
|
|
663
|
+
newLine: index + 1
|
|
664
|
+
})),
|
|
665
|
+
removed: 0,
|
|
666
|
+
added: 0
|
|
667
|
+
};
|
|
657
668
|
const oldLines = contentLines(oldText);
|
|
658
669
|
const newLines = contentLines(newText);
|
|
659
670
|
const context = Math.max(1, oldLines.length, newLines.length);
|
|
@@ -712,6 +723,111 @@ window.__ModuleLoader__.load({
|
|
|
712
723
|
return (text.endsWith("\n") ? text.slice(0, -1) : text).split("\n");
|
|
713
724
|
}
|
|
714
725
|
//#endregion
|
|
726
|
+
//#region lib/types/client/split-diff.js
|
|
727
|
+
/**
|
|
728
|
+
* Side-by-side (split) diff model: the unified whole-file diff rows are
|
|
729
|
+
* regrouped into line-aligned pairs for a two-column "before | current" view.
|
|
730
|
+
* Pure derivation; the view owns rendering. A context row becomes a pair with
|
|
731
|
+
* both sides, a deletion a left-only pair, an addition a right-only pair, and
|
|
732
|
+
* an adjacent deletion/addition run is paired line-by-line into a single
|
|
733
|
+
* replacement pair so the two columns line up.
|
|
734
|
+
* @module dsh-diff-approval/client/split-diff
|
|
735
|
+
*/
|
|
736
|
+
/** One whole-file diff row → its visible left side text. */
|
|
737
|
+
function leftSideOf(row) {
|
|
738
|
+
if (row.kind === "add") return void 0;
|
|
739
|
+
return {
|
|
740
|
+
text: row.text,
|
|
741
|
+
line: row.oldLine
|
|
742
|
+
};
|
|
743
|
+
}
|
|
744
|
+
/** One whole-file diff row → its visible right side text. */
|
|
745
|
+
function rightSideOf(row) {
|
|
746
|
+
if (row.kind === "del") return void 0;
|
|
747
|
+
return {
|
|
748
|
+
text: row.text,
|
|
749
|
+
line: row.newLine
|
|
750
|
+
};
|
|
751
|
+
}
|
|
752
|
+
/**
|
|
753
|
+
* Regroup the whole-file rows into aligned split pairs, pairing a deletion run
|
|
754
|
+
* with a following addition run line-by-line (so a replaced line is one pair),
|
|
755
|
+
* and leaving a stray deletion or addition as a one-sided pair.
|
|
756
|
+
* @param rows - the whole-file diff rows.
|
|
757
|
+
* @returns the split pairs plus the row→pair index map.
|
|
758
|
+
*/
|
|
759
|
+
function computeSideBySideDiff(rows) {
|
|
760
|
+
const pairs = [];
|
|
761
|
+
const pairOfRow = /* @__PURE__ */ new Map();
|
|
762
|
+
const pendingDel = [];
|
|
763
|
+
const push = (pair, rows) => {
|
|
764
|
+
const index = pairs.length;
|
|
765
|
+
pairs.push(pair);
|
|
766
|
+
for (const r of rows) pairOfRow.set(r, index);
|
|
767
|
+
};
|
|
768
|
+
const flushPending = () => {
|
|
769
|
+
for (const { index, row } of pendingDel) push({
|
|
770
|
+
kind: "del",
|
|
771
|
+
left: leftSideOf(row),
|
|
772
|
+
right: void 0
|
|
773
|
+
}, [index]);
|
|
774
|
+
pendingDel.length = 0;
|
|
775
|
+
};
|
|
776
|
+
for (let i = 0; i < rows.length; i++) {
|
|
777
|
+
const row = rows[i];
|
|
778
|
+
if (row.kind === "context") {
|
|
779
|
+
flushPending();
|
|
780
|
+
push({
|
|
781
|
+
kind: "context",
|
|
782
|
+
left: leftSideOf(row),
|
|
783
|
+
right: rightSideOf(row)
|
|
784
|
+
}, [i]);
|
|
785
|
+
} else if (row.kind === "del") pendingDel.push({
|
|
786
|
+
index: i,
|
|
787
|
+
row
|
|
788
|
+
});
|
|
789
|
+
else {
|
|
790
|
+
const del = pendingDel.shift();
|
|
791
|
+
if (del !== void 0) push({
|
|
792
|
+
kind: "replace",
|
|
793
|
+
left: leftSideOf(del.row),
|
|
794
|
+
right: rightSideOf(row)
|
|
795
|
+
}, [del.index, i]);
|
|
796
|
+
else push({
|
|
797
|
+
kind: "add",
|
|
798
|
+
left: void 0,
|
|
799
|
+
right: rightSideOf(row)
|
|
800
|
+
}, [i]);
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
flushPending();
|
|
804
|
+
return {
|
|
805
|
+
pairs,
|
|
806
|
+
pairOfRow
|
|
807
|
+
};
|
|
808
|
+
}
|
|
809
|
+
/**
|
|
810
|
+
* Pair indices whose left or right text contains the query (case-insensitive).
|
|
811
|
+
* A pair counts once however many times the query appears, so split search
|
|
812
|
+
* highlights the whole pair on both columns and a single "current" pair is
|
|
813
|
+
* stepped through — not each individual left/right occurrence.
|
|
814
|
+
* @param pairs - the split pairs.
|
|
815
|
+
* @param query - the query text; an empty query matches nothing.
|
|
816
|
+
* @returns matching pair indices, in file order.
|
|
817
|
+
*/
|
|
818
|
+
function searchPairs(pairs, query) {
|
|
819
|
+
if (query === "") return [];
|
|
820
|
+
const lower = query.toLowerCase();
|
|
821
|
+
const matches = [];
|
|
822
|
+
for (let index = 0; index < pairs.length; index++) {
|
|
823
|
+
const p = pairs[index];
|
|
824
|
+
if (p === void 0) continue;
|
|
825
|
+
if (p.left !== void 0 && p.left.text.toLowerCase().includes(lower)) matches.push(index);
|
|
826
|
+
else if (p.right !== void 0 && p.right.text.toLowerCase().includes(lower)) matches.push(index);
|
|
827
|
+
}
|
|
828
|
+
return matches;
|
|
829
|
+
}
|
|
830
|
+
//#endregion
|
|
715
831
|
//#region node_modules/.pnpm/@shikijs+types@4.4.3/node_modules/@shikijs/types/dist/index.mjs
|
|
716
832
|
var ShikiError = class extends Error {
|
|
717
833
|
constructor(message) {
|
|
@@ -11588,6 +11704,15 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
11588
11704
|
//#endregion
|
|
11589
11705
|
//#region lib/types/client/reference.js
|
|
11590
11706
|
/**
|
|
11707
|
+
* Reference labels for the selection toolbar: a workspace-relative path (or
|
|
11708
|
+
* the absolute path when the file is outside the workspace) plus a 1-based
|
|
11709
|
+
* line range. Pure derivation so the display rule is unit-testable without
|
|
11710
|
+
* the panel.
|
|
11711
|
+
* @module dsh-diff-approval/client/reference
|
|
11712
|
+
*/
|
|
11713
|
+
/** The marker that replaces a reference's line number when its lines are gone. */
|
|
11714
|
+
const LINE_MISSING_LABEL = "LINE_MISSING";
|
|
11715
|
+
/**
|
|
11591
11716
|
* The path embedded in a copied reference: workspace-relative (forward
|
|
11592
11717
|
* slashes) when the file lives inside the current workspace, the absolute
|
|
11593
11718
|
* path otherwise. A bare file name is never enough — a reference must resolve
|
|
@@ -11615,15 +11740,66 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
11615
11740
|
return start === end ? String(start) : `${start}-${end}`;
|
|
11616
11741
|
}
|
|
11617
11742
|
/**
|
|
11618
|
-
* Build the clipboard text for a selected line range
|
|
11743
|
+
* Build the clipboard text for a selected line range, wrapped in parentheses so
|
|
11744
|
+
* the reference reads as one unambiguous token (and can be matched precisely).
|
|
11619
11745
|
* @param path - the selected file's path.
|
|
11620
11746
|
* @param workspacePath - the current workspace root, or `undefined`.
|
|
11621
11747
|
* @param start - first selected line number.
|
|
11622
11748
|
* @param end - last selected line number.
|
|
11623
|
-
* @returns the `path:range` reference text.
|
|
11749
|
+
* @returns the `(path:range)` reference text.
|
|
11624
11750
|
*/
|
|
11625
11751
|
function referenceOf(path, workspacePath, start, end) {
|
|
11626
|
-
return
|
|
11752
|
+
return `(${referencePathOf(path, workspacePath)}:${lineRangeLabel(start, end)})`;
|
|
11753
|
+
}
|
|
11754
|
+
/**
|
|
11755
|
+
* Map one referenced line range from `oldContent` coordinates to `newContent`
|
|
11756
|
+
* coordinates. Lines that survive (unchanged context) map to their new line
|
|
11757
|
+
* numbers, and the surviving lines' min/max span is returned. When every line
|
|
11758
|
+
* in the range was removed, returns `undefined` (the reference is expired).
|
|
11759
|
+
* @param oldContent - the file content the reference was made against.
|
|
11760
|
+
* @param newContent - the file content now.
|
|
11761
|
+
* @param start - first referenced line (1-based, inclusive).
|
|
11762
|
+
* @param end - last referenced line (1-based, inclusive).
|
|
11763
|
+
* @returns the surviving range, or `undefined` when nothing survives.
|
|
11764
|
+
*/
|
|
11765
|
+
function remapReferenceRange(oldContent, newContent, start, end) {
|
|
11766
|
+
const diff = computeWholeFileDiff(oldContent, newContent);
|
|
11767
|
+
const oldToNew = /* @__PURE__ */ new Map();
|
|
11768
|
+
for (const row of diff.rows) if (row.oldLine !== void 0 && row.newLine !== void 0) oldToNew.set(row.oldLine, row.newLine);
|
|
11769
|
+
let min = Infinity;
|
|
11770
|
+
let max = -Infinity;
|
|
11771
|
+
for (let line = start; line <= end; line++) {
|
|
11772
|
+
const next = oldToNew.get(line);
|
|
11773
|
+
if (next === void 0) continue;
|
|
11774
|
+
min = Math.min(min, next);
|
|
11775
|
+
max = Math.max(max, next);
|
|
11776
|
+
}
|
|
11777
|
+
if (min === Infinity) return void 0;
|
|
11778
|
+
return {
|
|
11779
|
+
start: min,
|
|
11780
|
+
end: max
|
|
11781
|
+
};
|
|
11782
|
+
}
|
|
11783
|
+
/**
|
|
11784
|
+
* Rewrite every `(referencePath:line)` / `(referencePath:start-end)` occurrence
|
|
11785
|
+
* in `text`, remapping each range from `oldContent` to `newContent`. A range
|
|
11786
|
+
* whose lines all survived becomes the new range; one whose lines were all
|
|
11787
|
+
* removed becomes `(referencePath:LINE_MISSING)`.
|
|
11788
|
+
* @param text - the free text (composer draft, queued message) to rewrite.
|
|
11789
|
+
* @param referencePath - the file's reference path (workspace-relative or absolute).
|
|
11790
|
+
* @param oldContent - the file content the references were made against.
|
|
11791
|
+
* @param newContent - the file content now.
|
|
11792
|
+
* @returns the rewritten text.
|
|
11793
|
+
*/
|
|
11794
|
+
function remapReferences(text, referencePath, oldContent, newContent) {
|
|
11795
|
+
const escaped = referencePath.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
11796
|
+
const regex = new RegExp(`\\(${escaped}:(\\d+)(?:-(\\d+))?\\)`, "g");
|
|
11797
|
+
return text.replace(regex, (_whole, startText, endText) => {
|
|
11798
|
+
const start = Number(startText);
|
|
11799
|
+
const mapped = remapReferenceRange(oldContent, newContent, start, endText === void 0 ? start : Number(endText));
|
|
11800
|
+
if (mapped === void 0) return `(${referencePath}:${LINE_MISSING_LABEL})`;
|
|
11801
|
+
return `(${referencePath}:${lineRangeLabel(mapped.start, mapped.end)})`;
|
|
11802
|
+
});
|
|
11627
11803
|
}
|
|
11628
11804
|
//#endregion
|
|
11629
11805
|
//#region lib/types/client/settings.js
|
|
@@ -11631,6 +11807,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
11631
11807
|
const PASTE_ON_COPY_KEY = "diff-approval:paste-on-copy";
|
|
11632
11808
|
const IMPORT_UNTRACKED_KEY = "diff-approval:import-untracked";
|
|
11633
11809
|
const TAB_WIDTH_KEY = "diff-approval:tab-size";
|
|
11810
|
+
const SPLIT_MODE_KEY = "diff-approval:split-mode";
|
|
11634
11811
|
const WRAP_PREFIX = "diff-approval:wrap:";
|
|
11635
11812
|
/**
|
|
11636
11813
|
* Whether copying a reference should also paste it into the chat input and
|
|
@@ -11686,9 +11863,55 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
11686
11863
|
function setTabWidth(value) {
|
|
11687
11864
|
localStorage.setItem(TAB_WIDTH_KEY, String(value));
|
|
11688
11865
|
}
|
|
11866
|
+
/**
|
|
11867
|
+
* Whether the whole-file diff view uses the two-column (side-by-side) layout.
|
|
11868
|
+
* Default off (single column): the unified diff. Only an explicit `'1'` enables
|
|
11869
|
+
* split mode.
|
|
11870
|
+
* @returns whether the split (two-column) diff view is used.
|
|
11871
|
+
*/
|
|
11872
|
+
function splitMode() {
|
|
11873
|
+
return localStorage.getItem(SPLIT_MODE_KEY) === "1";
|
|
11874
|
+
}
|
|
11875
|
+
/** Persist the split-view preference. */
|
|
11876
|
+
function setSplitMode(value) {
|
|
11877
|
+
localStorage.setItem(SPLIT_MODE_KEY, value ? "1" : "0");
|
|
11878
|
+
}
|
|
11879
|
+
const QUICK_SUMMON_KEY = "diff-approval:quick-summon-key";
|
|
11880
|
+
/**
|
|
11881
|
+
* The quick-summon chord. Stored as `Modifier+...+Key`; falls back to
|
|
11882
|
+
* {@link DEFAULT_QUICK_SUMMON}.
|
|
11883
|
+
* @returns the chord string.
|
|
11884
|
+
*/
|
|
11885
|
+
function quickSummonKey() {
|
|
11886
|
+
return localStorage.getItem(QUICK_SUMMON_KEY) ?? "Ctrl+D";
|
|
11887
|
+
}
|
|
11888
|
+
/** Persist the quick-summon chord. */
|
|
11889
|
+
function setQuickSummonKey(value) {
|
|
11890
|
+
localStorage.setItem(QUICK_SUMMON_KEY, value);
|
|
11891
|
+
}
|
|
11892
|
+
/**
|
|
11893
|
+
* Whether a keyboard event matches a chord string like `Ctrl+D`. Modifier
|
|
11894
|
+
* names are matched case-insensitively (`Ctrl`/`Control`, `Alt`/`Option`,
|
|
11895
|
+
* `Shift`, `Meta`/`Cmd`/`Command`/`Win`); the final part is the key. Exact
|
|
11896
|
+
* modifier set is required (extra modifiers do not match).
|
|
11897
|
+
* @param event - the keydown event.
|
|
11898
|
+
* @param shortcut - the chord string.
|
|
11899
|
+
* @returns whether the event matches.
|
|
11900
|
+
*/
|
|
11901
|
+
function matchesShortcut(event, shortcut) {
|
|
11902
|
+
const parts = shortcut.split("+").map((part) => part.trim().toLowerCase());
|
|
11903
|
+
const key = parts.pop();
|
|
11904
|
+
if (key === void 0 || key === "") return false;
|
|
11905
|
+
const mods = new Set(parts);
|
|
11906
|
+
const ctrl = mods.has("ctrl") || mods.has("control");
|
|
11907
|
+
const alt = mods.has("alt") || mods.has("option");
|
|
11908
|
+
const shift = mods.has("shift");
|
|
11909
|
+
const meta = mods.has("meta") || mods.has("cmd") || mods.has("command") || mods.has("win");
|
|
11910
|
+
return event.key.toLowerCase() === key && event.ctrlKey === ctrl && event.altKey === alt && event.shiftKey === shift && event.metaKey === meta;
|
|
11911
|
+
}
|
|
11689
11912
|
//#endregion
|
|
11690
11913
|
//#region \0dsh-css:/home/runner/work/dsh-diff-approval/dsh-diff-approval/src/client/PendingPanel.module.css.mjs
|
|
11691
|
-
const css = ".F1KBNa_layer{box-sizing:border-box;flex:none;align-items:center;width:calc(100% + 4px);height:42px;margin:4px -2px;display:flex;position:relative}.F1KBNa_footerButtons{align-items:center;width:100%;display:flex}.F1KBNa_badge{box-sizing:border-box;width:100%;height:42px;color:var(--dsw-alias-label-primary);cursor:pointer;background:0 0;border:none;border-radius:12px;align-items:center;gap:8px;padding:0 10px 0 8px;font-family:inherit;font-size:14px;line-height:22px;display:flex;overflow:hidden}.F1KBNa_badge:hover{background:var(--dsw-alias-interactive-bg-hover)}.F1KBNa_badge[data-active]{background:var(--dsw-alias-interactive-bg-hover-solid)}.F1KBNa_badge:disabled{color:var(--dsw-alias-label-tertiary);cursor:default;background:0 0}.F1KBNa_badgeLabel{text-overflow:ellipsis;white-space:nowrap;min-width:0;overflow:hidden}.F1KBNa_badgeCount{color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;flex:none;margin-left:auto;font-size:12px;line-height:16px}.F1KBNa_layer.F1KBNa_rail{width:36px;height:36px;margin:8px 0 10px}.F1KBNa_rail .F1KBNa_badge{border-radius:50%;justify-content:center;gap:0;width:36px;height:36px;padding:0}.F1KBNa_rail .F1KBNa_badgeLabel{display:none}.F1KBNa_rail .F1KBNa_badgeCount{box-sizing:border-box;background:var(--dsw-alias-state-business-primary);min-width:18px;height:18px;color:var(--dsw-alias-label-primary-foreground);font-variant-numeric:tabular-nums;border-radius:9px;justify-content:center;align-items:center;padding:0 4px;font-size:11px;line-height:18px;display:flex;position:absolute;top:-3px;right:-7px}.F1KBNa_fullscreenBackdrop{z-index:29;background:var(--dsw-specific-sidebar-fill);position:fixed;inset:0}.F1KBNa_panel{z-index:30;border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-base);width:auto;max-width:none;box-shadow:var(--dsw-shadow-lv2);--dsh-scrollbar-thumb:var(--dsw-alias-scrollbar-bg-l2);--dsh-scrollbar-thumb-hover:var(--dsw-alias-scrollbar-hover-l2);border-radius:12px;flex-direction:column;display:flex;position:fixed;inset:8px 8px 128px;overflow:hidden}.F1KBNa_header{box-sizing:border-box;border-bottom:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-base);flex:none;justify-content:space-between;align-items:center;min-height:44px;padding:10px 12px;display:flex}.F1KBNa_headerActions{align-items:center;gap:2px;display:flex}.F1KBNa_settingsPage{padding:8px 12px}.F1KBNa_settingsRow{border-bottom:1px solid var(--dsw-alias-border-l2);align-items:center;gap:8px;padding:16px 0;display:flex}.F1KBNa_settingsRowText{flex-direction:column;flex:1;gap:4px;min-width:0;padding-right:48px;display:flex}.F1KBNa_settingsRowTitle{color:var(--dsw-alias-label-primary);font-size:14px;font-weight:400;line-height:22px}.F1KBNa_settingsRowDesc{color:var(--dsw-alias-label-tertiary);font-size:12px;font-weight:400;line-height:18px}.F1KBNa_settingsSelector{background:var(--dsw-alias-bg-module-platform);height:36px;font:inherit;color:var(--dsw-alias-label-primary);cursor:pointer;border:none;border-radius:18px;align-items:center;gap:12px;padding:0 14px;font-size:14px;line-height:22px;display:inline-flex}.F1KBNa_settingsSelector:hover{background:var(--dsw-alias-interactive-bg-hover)}.F1KBNa_settingsSelectorChevron{flex:none}.F1KBNa_states{flex-direction:column;flex:1;min-height:0;padding:4px 12px 12px;display:flex;overflow-y:auto}.F1KBNa_split{flex:1;align-items:stretch;min-height:0;display:flex;position:relative}.F1KBNa_fileListFloat{z-index:40;box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-base);box-shadow:var(--dsw-shadow-lv2);border-radius:12px;flex-direction:column;padding:6px 8px 10px;display:flex;position:absolute;overflow:hidden}.F1KBNa_fileList{box-sizing:border-box;border-right:1px solid var(--dsw-alias-border-l2);flex-direction:column;flex:none;width:240px;min-height:0;padding:4px 8px 12px;display:flex}.F1KBNa_listScroll{flex:1;min-height:0;overflow-y:auto}.F1KBNa_bulkActions{flex:none;gap:6px;padding-top:8px;display:flex}.F1KBNa_bulkActions .F1KBNa_action{text-align:center;flex:1}.F1KBNa_resizeHandle{cursor:col-resize;background:0 0;flex:none;width:5px;margin:0 -2px}.F1KBNa_resizeHandle:hover{background:var(--dsw-alias-border-l2)}.F1KBNa_detail{flex-direction:column;flex:1;min-width:0;min-height:0;display:flex}.F1KBNa_detailEmpty{color:var(--dsw-alias-label-tertiary);text-align:center;flex:1;justify-content:center;align-items:center;margin:0;padding:24px;font-size:12px;line-height:18px;display:flex}.F1KBNa_title{color:var(--dsw-alias-label-primary);font-size:13px;font-weight:500;line-height:20px}.F1KBNa_note,.F1KBNa_readError,.F1KBNa_hint{color:var(--dsw-alias-label-tertiary);margin:4px 0;font-size:12px;line-height:18px}.F1KBNa_noteCentered{text-align:center;margin:auto}.F1KBNa_emptyState{flex-direction:column;align-items:center;gap:10px;margin:auto;display:flex}.F1KBNa_importButton{border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-primary);cursor:pointer;background:0 0;border-radius:8px;padding:5px 14px;font-family:inherit;font-size:12px;line-height:18px}.F1KBNa_importButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.F1KBNa_importButton:disabled{color:var(--dsw-alias-label-tertiary);cursor:default}.F1KBNa_importNote{color:var(--dsw-alias-label-tertiary);text-align:center;margin:0;font-size:12px;line-height:18px}.F1KBNa_readError{color:var(--dsw-alias-state-error-primary)}.F1KBNa_group{color:var(--dsw-alias-label-tertiary);margin:8px 0 4px;font-size:12px;font-weight:500;line-height:16px}.F1KBNa_rows{margin:0;padding:0;list-style:none}.F1KBNa_row{border:1px solid #0000;border-radius:10px;margin:2px 0}.F1KBNa_rowHead{width:100%;color:var(--dsw-alias-label-primary);font:inherit;text-align:left;cursor:pointer;background:0 0;border:none;border-radius:10px;align-items:baseline;gap:8px;padding:6px 8px;display:flex}.F1KBNa_rowHead:hover{background:var(--dsw-alias-interactive-bg-hover-solid)}.F1KBNa_rowHead[data-selected]{background:var(--dsw-alias-interactive-bg-hover)}.F1KBNa_rowPath{text-overflow:ellipsis;white-space:nowrap;min-width:0;font:var(--dsw-font-markdown-code-block);overflow:hidden}.F1KBNa_kindTag{border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-tertiary);white-space:nowrap;border-radius:6px;flex:none;padding:1px 6px;font-size:11px;line-height:14px}.F1KBNa_kindHint{color:var(--dsw-alias-label-tertiary);flex:none;font-size:12px;line-height:16px}.F1KBNa_divergedHint,.F1KBNa_missingHint{border-bottom:1px solid var(--dsw-alias-border-l2);margin:0;padding:6px 8px;font-size:12px;line-height:18px}.F1KBNa_missing{border:1px solid var(--dsw-alias-state-warn-primary);color:var(--dsw-alias-state-warn-label,var(--dsw-alias-state-warn-primary));white-space:nowrap;border-radius:6px;flex:none;padding:1px 6px;font-size:11px;line-height:14px}.F1KBNa_rowFailed{border:1px solid var(--dsw-alias-state-error-primary);color:var(--dsw-alias-state-error-primary);white-space:nowrap;border-radius:6px;flex:none;padding:1px 6px;font-size:11px;line-height:14px}.F1KBNa_actionError{border-bottom:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-state-error-primary);overflow-wrap:anywhere;margin:0;padding:6px 8px;font-size:12px;line-height:18px}.F1KBNa_missingHint{color:var(--dsw-alias-state-warn-label,var(--dsw-alias-state-warn-primary))}.F1KBNa_rowMeta{color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;flex:none;gap:6px;margin-left:auto;font-size:12px;line-height:16px;display:inline-flex}.F1KBNa_addCount{color:color-mix(in srgb, var(--dsw-alias-state-success-primary) 60%, var(--dsw-alias-label-primary))}.F1KBNa_delCount{color:color-mix(in srgb, var(--dsw-alias-state-error-primary) 60%, var(--dsw-alias-label-primary))}.F1KBNa_diff{background:var(--dsw-alias-markdown-code-block);flex-direction:column;flex:1;min-height:0;display:flex;position:relative;overflow:hidden}.F1KBNa_diffHeader{border-bottom:1px solid var(--dsw-alias-border-l2);align-items:center;gap:8px;padding:6px 8px;display:flex}.F1KBNa_diffPath{text-overflow:ellipsis;white-space:nowrap;min-width:0;color:var(--dsw-alias-label-primary);font:var(--dsw-font-markdown-code-block);flex:1;font-size:12px;line-height:18px;overflow:hidden}.F1KBNa_diffActions{border-bottom:1px solid var(--dsw-alias-border-l2);align-items:center;gap:8px;padding:6px 8px;display:flex}.F1KBNa_diffStats{color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;font-size:12px;line-height:16px}.F1KBNa_flexSpacer{flex:1}.F1KBNa_action{border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-primary);cursor:pointer;background:0 0;border-radius:8px;padding:3px 12px;font-family:inherit;font-size:12px;line-height:18px}.F1KBNa_action:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.F1KBNa_action:disabled{color:var(--dsw-alias-label-tertiary);cursor:default}.F1KBNa_actionPrimary{background:var(--dsw-alias-state-business-primary);border-color:var(--dsw-alias-state-business-primary);color:var(--dsw-static-neutral-bluish-50)}.F1KBNa_actionPrimary:hover:not(:disabled){background:color-mix(in srgb, var(--dsw-alias-state-business-primary) 85%, var(--dsw-static-neutral-bluish-1000))}.F1KBNa_actionPrimary:disabled{background:var(--dsw-alias-state-business-primary);color:var(--dsw-static-neutral-bluish-50);opacity:.55}.F1KBNa_actionQuietDisabled:disabled{cursor:pointer;color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-border-l1);background:0 0}.F1KBNa_actionPrimary.F1KBNa_actionQuietDisabled:disabled{background:var(--dsw-alias-state-business-primary);border-color:var(--dsw-alias-state-business-primary);color:var(--dsw-static-neutral-bluish-50);opacity:1}.F1KBNa_iconAction{justify-content:center;align-items:center;min-height:25px;padding:4px 6px;display:inline-flex}.F1KBNa_close{cursor:pointer;width:28px;height:28px;color:var(--dsw-alias-label-tertiary);background:0 0;border:none;border-radius:28px;justify-content:center;align-items:center;padding:0;display:inline-flex}.F1KBNa_close:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}.F1KBNa_expand{cursor:pointer;width:28px;height:28px;color:var(--dsw-alias-label-tertiary);background:0 0;border:none;border-radius:28px;justify-content:center;align-items:center;padding:0;display:inline-flex}.F1KBNa_expand:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}.F1KBNa_expandExpanded svg{transform:rotate(180deg)}.F1KBNa_diffBodyWrap{flex:1;min-height:0;display:flex;position:relative}.F1KBNa_diffBody{min-width:0;font:var(--dsw-font-markdown-code-block);cursor:text;outline:none;flex:1;padding:0;position:relative;overflow:auto}.F1KBNa_diffBody::-webkit-scrollbar,.F1KBNa_diffBody::-webkit-scrollbar-track,.F1KBNa_diffBody::-webkit-scrollbar-thumb{cursor:default}.F1KBNa_blockActions{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-markdown-code-block);box-shadow:var(--dsw-shadow-lv1);z-index:2;border-radius:10px;align-items:center;gap:9px;padding:5px;display:flex;position:absolute;right:8px}.F1KBNa_blockPosition{color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;white-space:nowrap;margin-top:1px;margin-left:7px;font-size:12px;line-height:16px}.F1KBNa_searchBar{z-index:3;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-markdown-code-block);box-shadow:var(--dsw-shadow-lv1);border-radius:10px;align-items:center;gap:4px;padding:4px;display:flex;position:absolute;top:8px;right:8px}.F1KBNa_searchInput{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-base);width:150px;color:var(--dsw-alias-label-primary);border-radius:6px;outline:none;padding:3px 8px;font-family:inherit;font-size:12px;line-height:18px}.F1KBNa_searchInput:focus{border-color:var(--dsw-alias-state-business-primary)}.F1KBNa_searchCount{text-align:center;min-width:34px;color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;white-space:nowrap;font-size:12px;line-height:16px}.F1KBNa_diffBody [data-diff-search=hit]{background-image:linear-gradient(#facc1529,#facc1529)}.F1KBNa_diffBody [data-diff-search=current]{background-image:linear-gradient(#facc1552,#facc1552)}.F1KBNa_blockFlash{z-index:1;box-sizing:border-box;border:2px solid var(--dsw-alias-state-business-primary);pointer-events:none;border-radius:4px;animation:1s ease-out forwards F1KBNa_diffFlash;position:absolute;left:0;right:0}@keyframes F1KBNa_diffFlash{0%{opacity:1}to{opacity:0}}.F1KBNa_overviewRuler{pointer-events:none;opacity:.5;width:4px;position:absolute;top:0;bottom:0;right:0}.F1KBNa_overviewMarker{border-radius:2px;width:100%;min-height:2px;position:absolute;right:0}.F1KBNa_markerDel{background-color:var(--dsw-alias-state-error-primary)}.F1KBNa_markerAdd{background-color:var(--dsw-alias-state-success-primary)}.F1KBNa_lines{border-spacing:0;width:max-content;min-width:100%;display:table}.F1KBNa_line{height:22px;line-height:22px;display:table-row}.F1KBNa_vSpacer{display:table-row}.F1KBNa_gutter{box-sizing:border-box;text-align:right;width:44px;color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;user-select:none;cursor:default;padding-right:10px;display:table-cell}.F1KBNa_code{white-space:pre;display:table-cell}.F1KBNa_context{color:var(--dsw-alias-label-primary)}.F1KBNa_del{background-color:color-mix(in srgb, var(--dsw-alias-state-error-primary) 12%, transparent)}.F1KBNa_add{background-color:color-mix(in srgb, var(--dsw-alias-state-success-primary) 10%, transparent)}.F1KBNa_statusBar{box-sizing:border-box;border-top:1px solid var(--dsw-alias-border-l2);height:34px;color:var(--dsw-alias-label-tertiary);font-family:var(--dsw-font-markdown-code-block);font-variant-numeric:tabular-nums;flex:none;align-items:center;gap:8px;padding:0 8px;font-size:12px;line-height:18px;display:flex}.F1KBNa_statusAction{border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-primary);white-space:nowrap;cursor:pointer;background:0 0;border-radius:8px;padding:3px 12px;font-family:inherit;font-size:12px;line-height:18px}.F1KBNa_statusAction:hover{background:var(--dsw-alias-interactive-bg-hover)}.F1KBNa_langSelect{border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-primary);cursor:pointer;background:0 0;border-radius:8px;flex:none;align-items:center;gap:4px;padding:3px 12px;font-family:inherit;font-size:12px;line-height:18px;display:inline-flex}.F1KBNa_langSelect:hover{background:var(--dsw-alias-interactive-bg-hover)}.F1KBNa_langLabel{text-overflow:ellipsis;white-space:nowrap;max-width:140px;overflow:hidden}.F1KBNa_notice{z-index:60;border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-base);max-width:360px;box-shadow:var(--dsw-shadow-lv3);border-radius:10px;align-items:flex-start;gap:12px;padding:12px 14px;display:flex;position:fixed;bottom:24px;right:24px}.F1KBNa_noticeText{font:var(--dsw-font-caption);color:var(--dsw-alias-label-primary);flex:1;margin:0;line-height:1.45}.F1KBNa_noticeButton{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary);font:var(--dsw-font-caption);cursor:pointer;border-radius:6px;flex:none;padding:4px 10px}.F1KBNa_noticeButton:hover{background:var(--dsw-alias-interactive-bg-hover-solid)}.F1KBNa_wrap .F1KBNa_line{height:auto;min-height:22px}.F1KBNa_subline{white-space:pre;height:22px;line-height:22px;display:block;overflow:hidden}.F1KBNa_wrapActive{background:var(--dsw-alias-state-business-primary);border-color:var(--dsw-alias-state-business-primary);color:var(--dsw-static-neutral-bluish-50)}.F1KBNa_wrapActive:hover{background:color-mix(in srgb, var(--dsw-alias-state-business-primary) 85%, var(--dsw-static-neutral-bluish-1000))}";
|
|
11914
|
+
const css = ".F1KBNa_layer{box-sizing:border-box;flex:none;align-items:center;width:calc(100% + 4px);height:42px;margin:4px -2px;display:flex;position:relative}.F1KBNa_footerButtons{align-items:center;width:100%;display:flex}.F1KBNa_badge{box-sizing:border-box;width:100%;height:42px;color:var(--dsw-alias-label-primary);cursor:pointer;background:0 0;border:none;border-radius:12px;align-items:center;gap:8px;padding:0 10px 0 8px;font-family:inherit;font-size:14px;line-height:22px;display:flex;overflow:hidden}.F1KBNa_badge:hover{background:var(--dsw-alias-interactive-bg-hover)}.F1KBNa_badge[data-active]{background:var(--dsw-alias-interactive-bg-hover-solid)}.F1KBNa_badge:disabled{color:var(--dsw-alias-label-tertiary);cursor:default;background:0 0}.F1KBNa_badgeLabel{text-overflow:ellipsis;white-space:nowrap;min-width:0;overflow:hidden}.F1KBNa_badgeCount{color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;flex:none;margin-left:auto;font-size:12px;line-height:16px}.F1KBNa_layer.F1KBNa_rail{width:36px;height:36px;margin:8px 0 10px}.F1KBNa_rail .F1KBNa_badge{border-radius:50%;justify-content:center;gap:0;width:36px;height:36px;padding:0}.F1KBNa_rail .F1KBNa_badgeLabel{display:none}.F1KBNa_rail .F1KBNa_badgeCount{box-sizing:border-box;background:var(--dsw-alias-state-business-primary);min-width:18px;height:18px;color:var(--dsw-alias-label-primary-foreground);font-variant-numeric:tabular-nums;border-radius:9px;justify-content:center;align-items:center;padding:0 4px;font-size:11px;line-height:18px;display:flex;position:absolute;top:-3px;right:-7px}.F1KBNa_fullscreenBackdrop{z-index:29;background:var(--dsw-specific-sidebar-fill);position:fixed;inset:0}.F1KBNa_panel{z-index:30;border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-base);width:auto;max-width:none;box-shadow:var(--dsw-shadow-lv2);--dsh-scrollbar-thumb:var(--dsw-alias-scrollbar-bg-l2);--dsh-scrollbar-thumb-hover:var(--dsw-alias-scrollbar-hover-l2);border-radius:12px;flex-direction:column;display:flex;position:fixed;inset:8px 8px 128px;overflow:hidden}.F1KBNa_header{box-sizing:border-box;border-bottom:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-base);flex:none;justify-content:space-between;align-items:center;min-height:44px;padding:10px 12px;display:flex}.F1KBNa_headerActions{align-items:center;gap:2px;display:flex}.F1KBNa_settingsPage{padding:8px 12px}.F1KBNa_settingsRow{border-bottom:1px solid var(--dsw-alias-border-l2);align-items:center;gap:8px;padding:16px 0;display:flex}.F1KBNa_settingsRowText{flex-direction:column;flex:1;gap:4px;min-width:0;padding-right:48px;display:flex}.F1KBNa_settingsRowTitle{color:var(--dsw-alias-label-primary);font-size:14px;font-weight:400;line-height:22px}.F1KBNa_settingsRowDesc{color:var(--dsw-alias-label-tertiary);font-size:12px;font-weight:400;line-height:18px}.F1KBNa_settingsSelector{background:var(--dsw-alias-bg-module-platform);height:36px;font:inherit;color:var(--dsw-alias-label-primary);cursor:pointer;border:none;border-radius:18px;align-items:center;gap:12px;padding:0 14px;font-size:14px;line-height:22px;display:inline-flex}.F1KBNa_settingsSelector:hover{background:var(--dsw-alias-interactive-bg-hover)}.F1KBNa_settingsSelectorChevron{flex:none}.F1KBNa_states{flex-direction:column;flex:1;min-height:0;padding:4px 12px 12px;display:flex;overflow-y:auto}.F1KBNa_split{flex:1;align-items:stretch;min-height:0;display:flex;position:relative}.F1KBNa_fileListFloat{z-index:40;box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-base);box-shadow:var(--dsw-shadow-lv2);border-radius:12px;flex-direction:column;padding:6px 8px 10px;display:flex;position:absolute;overflow:hidden}.F1KBNa_fileList{box-sizing:border-box;border-right:1px solid var(--dsw-alias-border-l2);flex-direction:column;flex:none;width:240px;min-height:0;padding:4px 8px 12px;display:flex}.F1KBNa_listScroll{flex:1;min-height:0;overflow-y:auto}.F1KBNa_bulkActions{flex:none;gap:6px;padding-top:8px;display:flex}.F1KBNa_bulkActions .F1KBNa_action{text-align:center;flex:1}.F1KBNa_resizeHandle{cursor:col-resize;background:0 0;flex:none;width:5px;margin:0 -2px}.F1KBNa_resizeHandle:hover{background:var(--dsw-alias-border-l2)}.F1KBNa_detail{flex-direction:column;flex:1;min-width:0;min-height:0;display:flex}.F1KBNa_detailEmpty{color:var(--dsw-alias-label-tertiary);text-align:center;flex:1;justify-content:center;align-items:center;margin:0;padding:24px;font-size:12px;line-height:18px;display:flex}.F1KBNa_confirmBackdrop{z-index:5;background:color-mix(in srgb, var(--dsw-alias-bg-base) 55%, transparent);justify-content:center;align-items:center;padding:24px;display:flex;position:absolute;inset:0}.F1KBNa_confirmCard{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-base);max-width:340px;box-shadow:var(--dsw-shadow-lv3);border-radius:12px;flex-direction:column;gap:12px;padding:16px;display:flex}.F1KBNa_confirmText{font:var(--dsw-font-caption);color:var(--dsw-alias-label-primary);overflow-wrap:anywhere;margin:0;line-height:1.5}.F1KBNa_confirmActions{justify-content:flex-end;gap:8px;display:flex}.F1KBNa_title{color:var(--dsw-alias-label-primary);font-size:13px;font-weight:500;line-height:20px}.F1KBNa_note,.F1KBNa_readError,.F1KBNa_hint{color:var(--dsw-alias-label-tertiary);margin:4px 0;font-size:12px;line-height:18px}.F1KBNa_noteCentered{text-align:center;margin:auto}.F1KBNa_emptyState{flex-direction:column;align-items:center;gap:10px;margin:auto;display:flex}.F1KBNa_importButton{border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-primary);cursor:pointer;background:0 0;border-radius:8px;padding:5px 14px;font-family:inherit;font-size:12px;line-height:18px}.F1KBNa_importButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.F1KBNa_importButton:disabled{color:var(--dsw-alias-label-tertiary);cursor:default}.F1KBNa_importNote{color:var(--dsw-alias-label-tertiary);text-align:center;margin:0;font-size:12px;line-height:18px}.F1KBNa_readError{color:var(--dsw-alias-state-error-primary)}.F1KBNa_group{color:var(--dsw-alias-label-tertiary);margin:8px 0 4px;font-size:12px;font-weight:500;line-height:16px}.F1KBNa_rows{margin:0;padding:0;list-style:none}.F1KBNa_row{border:1px solid #0000;border-radius:10px;margin:2px 0}.F1KBNa_rowHead{width:100%;color:var(--dsw-alias-label-primary);font:inherit;text-align:left;cursor:pointer;background:0 0;border:none;border-radius:10px;align-items:baseline;gap:8px;padding:6px 8px;display:flex}.F1KBNa_rowHead:hover{background:var(--dsw-alias-interactive-bg-hover-solid)}.F1KBNa_rowHead[data-selected]{background:var(--dsw-alias-interactive-bg-hover)}.F1KBNa_rowPath{text-overflow:ellipsis;white-space:nowrap;min-width:0;font:var(--dsw-font-markdown-code-block);overflow:hidden}.F1KBNa_kindTag{border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-tertiary);white-space:nowrap;border-radius:6px;flex:none;padding:1px 6px;font-size:11px;line-height:14px}.F1KBNa_kindHint{color:var(--dsw-alias-label-tertiary);flex:none;font-size:12px;line-height:16px}.F1KBNa_divergedHint,.F1KBNa_missingHint{border-bottom:1px solid var(--dsw-alias-border-l2);margin:0;padding:6px 8px;font-size:12px;line-height:18px}.F1KBNa_missing{border:1px solid var(--dsw-alias-state-warn-primary);color:var(--dsw-alias-state-warn-label,var(--dsw-alias-state-warn-primary));white-space:nowrap;border-radius:6px;flex:none;padding:1px 6px;font-size:11px;line-height:14px}.F1KBNa_rowFailed{border:1px solid var(--dsw-alias-state-error-primary);color:var(--dsw-alias-state-error-primary);white-space:nowrap;border-radius:6px;flex:none;padding:1px 6px;font-size:11px;line-height:14px}.F1KBNa_actionError{border-bottom:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-state-error-primary);overflow-wrap:anywhere;margin:0;padding:6px 8px;font-size:12px;line-height:18px}.F1KBNa_missingHint{color:var(--dsw-alias-state-warn-label,var(--dsw-alias-state-warn-primary))}.F1KBNa_rowMeta{color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;flex:none;gap:6px;margin-left:auto;font-size:12px;line-height:16px;display:inline-flex}.F1KBNa_addCount{color:color-mix(in srgb, var(--dsw-alias-state-success-primary) 60%, var(--dsw-alias-label-primary))}.F1KBNa_delCount{color:color-mix(in srgb, var(--dsw-alias-state-error-primary) 60%, var(--dsw-alias-label-primary))}.F1KBNa_diff{background:var(--dsw-alias-markdown-code-block);flex-direction:column;flex:1;min-height:0;display:flex;position:relative;overflow:hidden}.F1KBNa_diffHeader{border-bottom:1px solid var(--dsw-alias-border-l2);align-items:center;gap:8px;padding:6px 8px;display:flex}.F1KBNa_diffPath{text-overflow:ellipsis;white-space:nowrap;min-width:0;color:var(--dsw-alias-label-primary);font:var(--dsw-font-markdown-code-block);flex:1;font-size:12px;line-height:18px;overflow:hidden}.F1KBNa_diffActions{border-bottom:1px solid var(--dsw-alias-border-l2);align-items:center;gap:8px;padding:6px 8px;display:flex}.F1KBNa_diffStats{color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;font-size:12px;line-height:16px}.F1KBNa_flexSpacer{flex:1}.F1KBNa_action{border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-primary);cursor:pointer;background:0 0;border-radius:8px;padding:3px 12px;font-family:inherit;font-size:12px;line-height:18px}.F1KBNa_action:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.F1KBNa_action:disabled{color:var(--dsw-alias-label-tertiary);cursor:default}.F1KBNa_actionPrimary{background:var(--dsw-alias-state-business-primary);border-color:var(--dsw-alias-state-business-primary);color:var(--dsw-static-neutral-bluish-50)}.F1KBNa_actionPrimary:hover:not(:disabled){background:color-mix(in srgb, var(--dsw-alias-state-business-primary) 85%, var(--dsw-static-neutral-bluish-1000))}.F1KBNa_actionPrimary:disabled{background:var(--dsw-alias-state-business-primary);color:var(--dsw-static-neutral-bluish-50);opacity:.55}.F1KBNa_actionQuietDisabled:disabled{cursor:pointer;color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-border-l1);background:0 0}.F1KBNa_actionPrimary.F1KBNa_actionQuietDisabled:disabled{background:var(--dsw-alias-state-business-primary);border-color:var(--dsw-alias-state-business-primary);color:var(--dsw-static-neutral-bluish-50);opacity:1}.F1KBNa_iconAction{justify-content:center;align-items:center;min-height:25px;padding:4px 6px;display:inline-flex}.F1KBNa_close{cursor:pointer;width:28px;height:28px;color:var(--dsw-alias-label-tertiary);background:0 0;border:none;border-radius:28px;justify-content:center;align-items:center;padding:0;display:inline-flex}.F1KBNa_close:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}.F1KBNa_expand{cursor:pointer;width:28px;height:28px;color:var(--dsw-alias-label-tertiary);background:0 0;border:none;border-radius:28px;justify-content:center;align-items:center;padding:0;display:inline-flex}.F1KBNa_expand:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}.F1KBNa_expandExpanded svg{transform:rotate(180deg)}.F1KBNa_diffBodyWrap{flex:1;min-height:0;display:flex;position:relative}.F1KBNa_diffBody{min-width:0;font:var(--dsw-font-markdown-code-block);-webkit-text-size-adjust:100%;text-size-adjust:100%;cursor:text;outline:none;flex:1;padding:0;position:relative;overflow:auto}.F1KBNa_diffBody::-webkit-scrollbar,.F1KBNa_diffBody::-webkit-scrollbar-track,.F1KBNa_diffBody::-webkit-scrollbar-thumb{cursor:default}.F1KBNa_blockActions{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-markdown-code-block);box-shadow:var(--dsw-shadow-lv1);z-index:2;border-radius:10px;align-items:center;gap:9px;padding:5px;display:flex;position:absolute;right:8px}.F1KBNa_blockPosition{color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;white-space:nowrap;margin-top:1px;margin-left:7px;font-size:12px;line-height:16px}.F1KBNa_searchBar{z-index:3;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-markdown-code-block);box-shadow:var(--dsw-shadow-lv1);border-radius:10px;align-items:center;gap:4px;padding:4px;display:flex;position:absolute;top:8px;right:8px}.F1KBNa_searchInput{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-base);width:150px;color:var(--dsw-alias-label-primary);border-radius:6px;outline:none;padding:3px 8px;font-family:inherit;font-size:12px;line-height:18px}.F1KBNa_searchInput:focus{border-color:var(--dsw-alias-state-business-primary)}.F1KBNa_searchCount{text-align:center;min-width:34px;color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;white-space:nowrap;font-size:12px;line-height:16px}.F1KBNa_diffBody [data-diff-search=hit]{background-image:linear-gradient(#facc1529,#facc1529)}.F1KBNa_diffBody [data-diff-search=current]{background-image:linear-gradient(#facc1552,#facc1552)}.F1KBNa_blockFlash{z-index:1;box-sizing:border-box;border:2px solid var(--dsw-alias-state-business-primary);pointer-events:none;border-radius:4px;animation:1s ease-out forwards F1KBNa_diffFlash;position:absolute;left:0;right:0}@keyframes F1KBNa_diffFlash{0%{opacity:1}to{opacity:0}}.F1KBNa_overviewRuler{pointer-events:none;opacity:.5;width:4px;position:absolute;top:0;bottom:0;right:0}.F1KBNa_overviewMarker{border-radius:2px;width:100%;min-height:2px;position:absolute;right:0}.F1KBNa_markerDel{background-color:var(--dsw-alias-state-error-primary)}.F1KBNa_markerAdd{background-color:var(--dsw-alias-state-success-primary)}.F1KBNa_lines{border-spacing:0;width:max-content;min-width:100%;display:table}.F1KBNa_line{height:22px;line-height:22px;display:table-row}.F1KBNa_vSpacer{display:table-row}.F1KBNa_gutter{box-sizing:border-box;text-align:right;width:44px;color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;user-select:none;cursor:default;padding-right:10px;display:table-cell}.F1KBNa_code{white-space:pre;display:table-cell}.F1KBNa_context{color:var(--dsw-alias-label-primary)}.F1KBNa_del{background-color:color-mix(in srgb, var(--dsw-alias-state-error-primary) 12%, transparent)}.F1KBNa_add{background-color:color-mix(in srgb, var(--dsw-alias-state-success-primary) 10%, transparent)}.F1KBNa_statusBar{box-sizing:border-box;border-top:1px solid var(--dsw-alias-border-l2);height:34px;color:var(--dsw-alias-label-tertiary);font-family:var(--dsw-font-markdown-code-block);font-variant-numeric:tabular-nums;flex:none;align-items:center;gap:8px;padding:0 8px;font-size:12px;line-height:18px;display:flex}.F1KBNa_statusAction{border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-primary);white-space:nowrap;cursor:pointer;background:0 0;border-radius:8px;padding:3px 12px;font-family:inherit;font-size:12px;line-height:18px}.F1KBNa_statusAction:hover{background:var(--dsw-alias-interactive-bg-hover)}.F1KBNa_langSelect{border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-primary);cursor:pointer;background:0 0;border-radius:8px;flex:none;align-items:center;gap:4px;padding:3px 12px;font-family:inherit;font-size:12px;line-height:18px;display:inline-flex}.F1KBNa_langSelect:hover{background:var(--dsw-alias-interactive-bg-hover)}.F1KBNa_langLabel{text-overflow:ellipsis;white-space:nowrap;max-width:140px;overflow:hidden}.F1KBNa_notice{z-index:60;border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-base);max-width:360px;box-shadow:var(--dsw-shadow-lv3);border-radius:10px;align-items:flex-start;gap:12px;padding:12px 14px;display:flex;position:fixed;bottom:24px;right:24px}.F1KBNa_noticeText{font:var(--dsw-font-caption);color:var(--dsw-alias-label-primary);flex:1;margin:0;line-height:1.45}.F1KBNa_noticeButton{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary);font:var(--dsw-font-caption);cursor:pointer;border-radius:6px;flex:none;padding:4px 10px}.F1KBNa_noticeButton:hover{background:var(--dsw-alias-interactive-bg-hover-solid)}.F1KBNa_wrap .F1KBNa_line{height:auto;min-height:22px}.F1KBNa_subline{white-space:pre;height:22px;line-height:22px;display:block;overflow:hidden}.F1KBNa_wrapActive{background:var(--dsw-alias-state-business-primary);border-color:var(--dsw-alias-state-business-primary);color:var(--dsw-static-neutral-bluish-50)}.F1KBNa_wrapActive:hover{background:color-mix(in srgb, var(--dsw-alias-state-business-primary) 85%, var(--dsw-static-neutral-bluish-1000))}.F1KBNa_splitRoot{flex-direction:column;flex:1;min-height:0;display:flex;position:relative}.F1KBNa_diffBodySplit{flex:1;min-height:0;overflow-x:hidden}.F1KBNa_splitCols{width:100%;min-width:0;display:flex}.F1KBNa_splitCol{box-sizing:border-box;flex:1 1 0;min-width:0;overflow:hidden}.F1KBNa_splitDivider{background:var(--dsw-alias-border-l2);flex:0 0 1px}.F1KBNa_splitCol .F1KBNa_gutter,.F1KBNa_splitCol .F1KBNa_code{vertical-align:top}.F1KBNa_splitHScrollRow{border-top:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-base);flex:none;width:100%;min-width:0;display:flex}.F1KBNa_splitHScroll{flex:1 1 0;min-width:0;height:15px;overflow:auto hidden}.F1KBNa_splitHScroll::-webkit-scrollbar{height:15px}.F1KBNa_splitHScroll::-webkit-scrollbar-track{cursor:default}.F1KBNa_splitHScroll::-webkit-scrollbar-thumb{cursor:default}.F1KBNa_splitHScrollFill{height:1px}.F1KBNa_splitLdel{background-color:color-mix(in srgb, var(--dsw-alias-state-error-primary) 12%, transparent)}.F1KBNa_splitLadd{background-color:color-mix(in srgb, var(--dsw-alias-state-success-primary) 10%, transparent)}.F1KBNa_splitRdel{background-color:color-mix(in srgb, var(--dsw-alias-state-error-primary) 12%, transparent)}.F1KBNa_splitRadd{background-color:color-mix(in srgb, var(--dsw-alias-state-success-primary) 10%, transparent)}";
|
|
11692
11915
|
const tagId = "dsh-diff-approval/PendingPanel.module.css";
|
|
11693
11916
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
|
|
11694
11917
|
const tag = document.createElement("style");
|
|
@@ -11698,99 +11921,115 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
11698
11921
|
document.head.appendChild(tag);
|
|
11699
11922
|
}
|
|
11700
11923
|
var PendingPanel_module_css_default = {
|
|
11701
|
-
"
|
|
11702
|
-
"
|
|
11703
|
-
"
|
|
11704
|
-
"
|
|
11705
|
-
"
|
|
11706
|
-
"
|
|
11707
|
-
"
|
|
11708
|
-
"
|
|
11709
|
-
"
|
|
11710
|
-
"readError": "F1KBNa_readError",
|
|
11711
|
-
"gutter": "F1KBNa_gutter",
|
|
11712
|
-
"markerAdd": "F1KBNa_markerAdd",
|
|
11713
|
-
"del": "F1KBNa_del",
|
|
11714
|
-
"fileList": "F1KBNa_fileList",
|
|
11715
|
-
"delCount": "F1KBNa_delCount",
|
|
11716
|
-
"diffBodyWrap": "F1KBNa_diffBodyWrap",
|
|
11924
|
+
"kindHint": "F1KBNa_kindHint",
|
|
11925
|
+
"searchBar": "F1KBNa_searchBar",
|
|
11926
|
+
"diffPath": "F1KBNa_diffPath",
|
|
11927
|
+
"flexSpacer": "F1KBNa_flexSpacer",
|
|
11928
|
+
"splitRdel": "F1KBNa_splitRdel",
|
|
11929
|
+
"settingsRowTitle": "F1KBNa_settingsRowTitle",
|
|
11930
|
+
"settingsSelectorChevron": "F1KBNa_settingsSelectorChevron",
|
|
11931
|
+
"group": "F1KBNa_group",
|
|
11932
|
+
"iconAction": "F1KBNa_iconAction",
|
|
11717
11933
|
"blockFlash": "F1KBNa_blockFlash",
|
|
11718
|
-
"
|
|
11719
|
-
"
|
|
11720
|
-
"diff": "F1KBNa_diff",
|
|
11721
|
-
"rowMeta": "F1KBNa_rowMeta",
|
|
11722
|
-
"panel": "F1KBNa_panel",
|
|
11723
|
-
"title": "F1KBNa_title",
|
|
11724
|
-
"expand": "F1KBNa_expand",
|
|
11725
|
-
"lines": "F1KBNa_lines",
|
|
11726
|
-
"missingHint": "F1KBNa_missingHint",
|
|
11727
|
-
"headerActions": "F1KBNa_headerActions",
|
|
11728
|
-
"settingsRowDesc": "F1KBNa_settingsRowDesc",
|
|
11934
|
+
"overviewRuler": "F1KBNa_overviewRuler",
|
|
11935
|
+
"row": "F1KBNa_row",
|
|
11729
11936
|
"close": "F1KBNa_close",
|
|
11730
|
-
"
|
|
11731
|
-
"
|
|
11732
|
-
"
|
|
11733
|
-
"diffPath": "F1KBNa_diffPath",
|
|
11734
|
-
"importNote": "F1KBNa_importNote",
|
|
11735
|
-
"missing": "F1KBNa_missing",
|
|
11736
|
-
"footerButtons": "F1KBNa_footerButtons",
|
|
11737
|
-
"resizeHandle": "F1KBNa_resizeHandle",
|
|
11738
|
-
"line": "F1KBNa_line",
|
|
11937
|
+
"settingsRowDesc": "F1KBNa_settingsRowDesc",
|
|
11938
|
+
"states": "F1KBNa_states",
|
|
11939
|
+
"code": "F1KBNa_code",
|
|
11739
11940
|
"kindTag": "F1KBNa_kindTag",
|
|
11740
|
-
"
|
|
11741
|
-
"
|
|
11941
|
+
"badgeCount": "F1KBNa_badgeCount",
|
|
11942
|
+
"badge": "F1KBNa_badge",
|
|
11742
11943
|
"emptyState": "F1KBNa_emptyState",
|
|
11743
|
-
"
|
|
11744
|
-
"
|
|
11944
|
+
"rowPath": "F1KBNa_rowPath",
|
|
11945
|
+
"notice": "F1KBNa_notice",
|
|
11946
|
+
"diffBodyWrap": "F1KBNa_diffBodyWrap",
|
|
11947
|
+
"missingHint": "F1KBNa_missingHint",
|
|
11948
|
+
"resizeHandle": "F1KBNa_resizeHandle",
|
|
11949
|
+
"splitRoot": "F1KBNa_splitRoot",
|
|
11950
|
+
"expandExpanded": "F1KBNa_expandExpanded",
|
|
11951
|
+
"splitLadd": "F1KBNa_splitLadd",
|
|
11952
|
+
"settingsPage": "F1KBNa_settingsPage",
|
|
11953
|
+
"fullscreenBackdrop": "F1KBNa_fullscreenBackdrop",
|
|
11745
11954
|
"layer": "F1KBNa_layer",
|
|
11746
|
-
"
|
|
11747
|
-
"
|
|
11748
|
-
"settingsSelectorChevron": "F1KBNa_settingsSelectorChevron",
|
|
11955
|
+
"settingsSelector": "F1KBNa_settingsSelector",
|
|
11956
|
+
"readError": "F1KBNa_readError",
|
|
11749
11957
|
"importButton": "F1KBNa_importButton",
|
|
11958
|
+
"addCount": "F1KBNa_addCount",
|
|
11959
|
+
"context": "F1KBNa_context",
|
|
11750
11960
|
"header": "F1KBNa_header",
|
|
11751
|
-
"
|
|
11752
|
-
"
|
|
11753
|
-
"
|
|
11754
|
-
"
|
|
11755
|
-
"
|
|
11961
|
+
"gutter": "F1KBNa_gutter",
|
|
11962
|
+
"confirmActions": "F1KBNa_confirmActions",
|
|
11963
|
+
"searchInput": "F1KBNa_searchInput",
|
|
11964
|
+
"importNote": "F1KBNa_importNote",
|
|
11965
|
+
"noticeText": "F1KBNa_noticeText",
|
|
11966
|
+
"wrap": "F1KBNa_wrap",
|
|
11967
|
+
"splitHScrollRow": "F1KBNa_splitHScrollRow",
|
|
11756
11968
|
"blockActions": "F1KBNa_blockActions",
|
|
11757
|
-
"
|
|
11758
|
-
"
|
|
11759
|
-
"
|
|
11969
|
+
"splitHScroll": "F1KBNa_splitHScroll",
|
|
11970
|
+
"splitHScrollFill": "F1KBNa_splitHScrollFill",
|
|
11971
|
+
"fileListFloat": "F1KBNa_fileListFloat",
|
|
11972
|
+
"noteCentered": "F1KBNa_noteCentered",
|
|
11760
11973
|
"diffActions": "F1KBNa_diffActions",
|
|
11761
|
-
"expandExpanded": "F1KBNa_expandExpanded",
|
|
11762
|
-
"settingsRow": "F1KBNa_settingsRow",
|
|
11763
|
-
"blockPosition": "F1KBNa_blockPosition",
|
|
11764
|
-
"statusAction": "F1KBNa_statusAction",
|
|
11765
|
-
"searchBar": "F1KBNa_searchBar",
|
|
11766
|
-
"searchInput": "F1KBNa_searchInput",
|
|
11767
|
-
"rows": "F1KBNa_rows",
|
|
11768
|
-
"statusBar": "F1KBNa_statusBar",
|
|
11769
|
-
"settingsPage": "F1KBNa_settingsPage",
|
|
11770
|
-
"overviewMarker": "F1KBNa_overviewMarker",
|
|
11771
|
-
"add": "F1KBNa_add",
|
|
11772
|
-
"langLabel": "F1KBNa_langLabel",
|
|
11773
|
-
"notice": "F1KBNa_notice",
|
|
11774
|
-
"states": "F1KBNa_states",
|
|
11775
|
-
"rowPath": "F1KBNa_rowPath",
|
|
11776
11974
|
"searchCount": "F1KBNa_searchCount",
|
|
11975
|
+
"add": "F1KBNa_add",
|
|
11777
11976
|
"diffStats": "F1KBNa_diffStats",
|
|
11778
|
-
"
|
|
11977
|
+
"langLabel": "F1KBNa_langLabel",
|
|
11978
|
+
"detailEmpty": "F1KBNa_detailEmpty",
|
|
11979
|
+
"actionPrimary": "F1KBNa_actionPrimary",
|
|
11980
|
+
"diffBody": "F1KBNa_diffBody",
|
|
11981
|
+
"rowMeta": "F1KBNa_rowMeta",
|
|
11982
|
+
"diffHeader": "F1KBNa_diffHeader",
|
|
11983
|
+
"blockPosition": "F1KBNa_blockPosition",
|
|
11984
|
+
"subline": "F1KBNa_subline",
|
|
11985
|
+
"splitCol": "F1KBNa_splitCol",
|
|
11986
|
+
"missing": "F1KBNa_missing",
|
|
11987
|
+
"splitDivider": "F1KBNa_splitDivider",
|
|
11988
|
+
"rowFailed": "F1KBNa_rowFailed",
|
|
11989
|
+
"listScroll": "F1KBNa_listScroll",
|
|
11990
|
+
"actionError": "F1KBNa_actionError",
|
|
11991
|
+
"headerActions": "F1KBNa_headerActions",
|
|
11992
|
+
"confirmCard": "F1KBNa_confirmCard",
|
|
11993
|
+
"actionQuietDisabled": "F1KBNa_actionQuietDisabled",
|
|
11779
11994
|
"markerDel": "F1KBNa_markerDel",
|
|
11780
|
-
"
|
|
11781
|
-
"
|
|
11995
|
+
"bulkActions": "F1KBNa_bulkActions",
|
|
11996
|
+
"divergedHint": "F1KBNa_divergedHint",
|
|
11997
|
+
"lines": "F1KBNa_lines",
|
|
11998
|
+
"badgeLabel": "F1KBNa_badgeLabel",
|
|
11999
|
+
"detail": "F1KBNa_detail",
|
|
12000
|
+
"splitRadd": "F1KBNa_splitRadd",
|
|
12001
|
+
"footerButtons": "F1KBNa_footerButtons",
|
|
12002
|
+
"expand": "F1KBNa_expand",
|
|
12003
|
+
"overviewMarker": "F1KBNa_overviewMarker",
|
|
12004
|
+
"markerAdd": "F1KBNa_markerAdd",
|
|
12005
|
+
"hint": "F1KBNa_hint",
|
|
12006
|
+
"line": "F1KBNa_line",
|
|
12007
|
+
"wrapActive": "F1KBNa_wrapActive",
|
|
12008
|
+
"diffBodySplit": "F1KBNa_diffBodySplit",
|
|
12009
|
+
"delCount": "F1KBNa_delCount",
|
|
12010
|
+
"confirmText": "F1KBNa_confirmText",
|
|
12011
|
+
"note": "F1KBNa_note",
|
|
12012
|
+
"rows": "F1KBNa_rows",
|
|
12013
|
+
"settingsRow": "F1KBNa_settingsRow",
|
|
11782
12014
|
"diffFlash": "F1KBNa_diffFlash",
|
|
11783
|
-
"
|
|
12015
|
+
"del": "F1KBNa_del",
|
|
12016
|
+
"title": "F1KBNa_title",
|
|
12017
|
+
"statusAction": "F1KBNa_statusAction",
|
|
12018
|
+
"panel": "F1KBNa_panel",
|
|
12019
|
+
"fileList": "F1KBNa_fileList",
|
|
12020
|
+
"splitCols": "F1KBNa_splitCols",
|
|
12021
|
+
"settingsRowText": "F1KBNa_settingsRowText",
|
|
12022
|
+
"diff": "F1KBNa_diff",
|
|
12023
|
+
"confirmBackdrop": "F1KBNa_confirmBackdrop",
|
|
12024
|
+
"statusBar": "F1KBNa_statusBar",
|
|
12025
|
+
"splitLdel": "F1KBNa_splitLdel",
|
|
11784
12026
|
"split": "F1KBNa_split",
|
|
11785
|
-
"
|
|
11786
|
-
"
|
|
11787
|
-
"row": "F1KBNa_row",
|
|
11788
|
-
"wrapActive": "F1KBNa_wrapActive",
|
|
11789
|
-
"settingsRowTitle": "F1KBNa_settingsRowTitle",
|
|
11790
|
-
"group": "F1KBNa_group",
|
|
12027
|
+
"langSelect": "F1KBNa_langSelect",
|
|
12028
|
+
"rail": "F1KBNa_rail",
|
|
11791
12029
|
"rowHead": "F1KBNa_rowHead",
|
|
11792
|
-
"
|
|
11793
|
-
"
|
|
12030
|
+
"action": "F1KBNa_action",
|
|
12031
|
+
"vSpacer": "F1KBNa_vSpacer",
|
|
12032
|
+
"noticeButton": "F1KBNa_noticeButton"
|
|
11794
12033
|
};
|
|
11795
12034
|
//#endregion
|
|
11796
12035
|
//#region lib/types/client/PendingPanel.js
|
|
@@ -11829,6 +12068,10 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
11829
12068
|
/** Total width of the two line-number gutters, subtracted from the code width
|
|
11830
12069
|
* when measuring wrapped line heights. */
|
|
11831
12070
|
const WRAP_GUTTERS_PX = 88;
|
|
12071
|
+
/** The dsh shell's sidebar auto-collapse breakpoint (ui-layout columns.ts):
|
|
12072
|
+
* below it the sidebar auto-collapses, and the file list floats on the same
|
|
12073
|
+
* breakpoint so the two stay consistent. */
|
|
12074
|
+
const SIDEBAR_AUTO_COLLAPSE_PX = 1024;
|
|
11832
12075
|
/** A shared canvas for measuring wrapped line heights (CPU-only, no DOM reflow). */
|
|
11833
12076
|
let measureCanvas;
|
|
11834
12077
|
/**
|
|
@@ -12155,66 +12398,633 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
12155
12398
|
});
|
|
12156
12399
|
return blocks;
|
|
12157
12400
|
}
|
|
12158
|
-
/**
|
|
12159
|
-
function
|
|
12160
|
-
if (
|
|
12161
|
-
const
|
|
12162
|
-
if (
|
|
12163
|
-
|
|
12164
|
-
|
|
12165
|
-
|
|
12166
|
-
|
|
12167
|
-
|
|
12168
|
-
|
|
12169
|
-
|
|
12170
|
-
|
|
12171
|
-
|
|
12172
|
-
|
|
12173
|
-
|
|
12174
|
-
|
|
12175
|
-
|
|
12176
|
-
before += current.length;
|
|
12177
|
-
current = walker.nextNode();
|
|
12178
|
-
}
|
|
12179
|
-
if (node instanceof Element) {
|
|
12180
|
-
const children = [...node.childNodes];
|
|
12181
|
-
for (let i = 0; i < Math.min(offset, children.length); i++) {
|
|
12182
|
-
const inner = document.createTreeWalker(children[i], NodeFilter.SHOW_TEXT);
|
|
12183
|
-
let text = inner.nextNode();
|
|
12184
|
-
while (text !== null) {
|
|
12185
|
-
before += text.length;
|
|
12186
|
-
text = inner.nextNode();
|
|
12187
|
-
}
|
|
12188
|
-
}
|
|
12189
|
-
}
|
|
12190
|
-
return before;
|
|
12191
|
-
}
|
|
12192
|
-
/** Length of the code text on the line holding a node. */
|
|
12193
|
-
function lineLengthAt(node) {
|
|
12194
|
-
return ((node instanceof Element ? node : node.parentElement)?.closest("[data-diff-code]"))?.textContent?.length ?? 0;
|
|
12401
|
+
/** One side's line-content for the split view: the highlighted runs or plain text. */
|
|
12402
|
+
function splitSideContent(side, wrapped, runs) {
|
|
12403
|
+
if (side === void 0) return "";
|
|
12404
|
+
const highlighted = runs !== void 0 && runs.length > 0;
|
|
12405
|
+
if (wrapped === void 0) return highlighted ? runs.map((span, i) => (0, react_jsx_runtime.jsx)("span", {
|
|
12406
|
+
style: span.style,
|
|
12407
|
+
children: span.text
|
|
12408
|
+
}, i)) : side.text === "" ? "\xA0" : side.text;
|
|
12409
|
+
let offset = 0;
|
|
12410
|
+
return wrapped.map((line, i) => {
|
|
12411
|
+
const start = offset;
|
|
12412
|
+
offset += line.length;
|
|
12413
|
+
const content = highlighted ? clipRuns(runs, start, offset) : line === "" ? "\xA0" : line;
|
|
12414
|
+
return (0, react_jsx_runtime.jsx)("div", {
|
|
12415
|
+
className: PendingPanel_module_css_default.subline,
|
|
12416
|
+
children: content
|
|
12417
|
+
}, i);
|
|
12418
|
+
});
|
|
12195
12419
|
}
|
|
12196
12420
|
/**
|
|
12197
|
-
*
|
|
12198
|
-
*
|
|
12199
|
-
*
|
|
12200
|
-
*
|
|
12421
|
+
* One side of a split pair row, rendered inside its own column. The two columns
|
|
12422
|
+
* are drawn by two independent `.splitCol` scrollers (each with its own
|
|
12423
|
+
* horizontal scrollbar) that share one vertical scroller, and each row gets the
|
|
12424
|
+
* same fixed `height` (the pair's max of the two sides' wrapped sub-line
|
|
12425
|
+
* counts) so the left/right halves always align on the same Y — no jump when
|
|
12426
|
+
* one side is longer. The gutter and code are top-aligned so sub-lines line up
|
|
12427
|
+
* across the divider.
|
|
12201
12428
|
*/
|
|
12202
|
-
function
|
|
12203
|
-
|
|
12204
|
-
|
|
12205
|
-
|
|
12206
|
-
|
|
12207
|
-
|
|
12208
|
-
|
|
12209
|
-
|
|
12210
|
-
|
|
12211
|
-
|
|
12212
|
-
|
|
12213
|
-
|
|
12214
|
-
|
|
12215
|
-
|
|
12216
|
-
|
|
12217
|
-
|
|
12429
|
+
function SplitSideRow({ index, side, wrapped, runs, kind, isLeft, height, focused, searchHit, searchCurrent, onHover }) {
|
|
12430
|
+
const tint = isLeft ? kind === "del" || kind === "replace" ? PendingPanel_module_css_default.splitLdel : "" : kind === "add" || kind === "replace" ? PendingPanel_module_css_default.splitRadd : "";
|
|
12431
|
+
return (0, react_jsx_runtime.jsxs)("div", {
|
|
12432
|
+
className: PendingPanel_module_css_default.line,
|
|
12433
|
+
style: { height },
|
|
12434
|
+
"data-diff-split-row": true,
|
|
12435
|
+
"data-diff-split-index": index,
|
|
12436
|
+
"data-diff-split-side": isLeft ? "left" : "right",
|
|
12437
|
+
"data-diff-focused": focused ? "" : void 0,
|
|
12438
|
+
"data-diff-search": searchHit ? searchCurrent ? "current" : "hit" : void 0,
|
|
12439
|
+
onMouseEnter: onHover,
|
|
12440
|
+
children: [(0, react_jsx_runtime.jsx)("span", {
|
|
12441
|
+
className: PendingPanel_module_css_default.gutter,
|
|
12442
|
+
children: side?.line ?? ""
|
|
12443
|
+
}), (0, react_jsx_runtime.jsx)("span", {
|
|
12444
|
+
className: `${PendingPanel_module_css_default.code} ${tint}`,
|
|
12445
|
+
"data-diff-code": true,
|
|
12446
|
+
children: splitSideContent(side, wrapped, runs)
|
|
12447
|
+
})]
|
|
12448
|
+
});
|
|
12449
|
+
}
|
|
12450
|
+
/** The two-column (side-by-side) whole-file diff view. */
|
|
12451
|
+
const SplitDiff = (0, react.forwardRef)(function SplitDiff({ file, model, runs, langWrap, tabWidthSpaces, busy, t, onBlockKeep, onBlockRevert }, ref) {
|
|
12452
|
+
const { pairs, pairOfRow } = (0, react.useMemo)(() => computeSideBySideDiff(model.diff.rows), [model]);
|
|
12453
|
+
const pairCount = pairs.length;
|
|
12454
|
+
const bodyRef = (0, react.useRef)(null);
|
|
12455
|
+
const [scrollTop, setScrollTop] = (0, react.useState)(0);
|
|
12456
|
+
const [viewportH, setViewportH] = (0, react.useState)(0);
|
|
12457
|
+
const [bodyWidth, setBodyWidth] = (0, react.useState)(0);
|
|
12458
|
+
const [hoveredBlock, setHoveredBlock] = (0, react.useState)(void 0);
|
|
12459
|
+
const [focus, setFocus] = (0, react.useState)(0);
|
|
12460
|
+
const [flashKey, setFlashKey] = (0, react.useState)(0);
|
|
12461
|
+
const hoveredBlockRef = (0, react.useRef)(void 0);
|
|
12462
|
+
const leftColRef = (0, react.useRef)(null);
|
|
12463
|
+
const rightColRef = (0, react.useRef)(null);
|
|
12464
|
+
const leftHScrollRef = (0, react.useRef)(null);
|
|
12465
|
+
const rightHScrollRef = (0, react.useRef)(null);
|
|
12466
|
+
const [fillWidth, setFillWidth] = (0, react.useState)({
|
|
12467
|
+
left: 0,
|
|
12468
|
+
right: 0
|
|
12469
|
+
});
|
|
12470
|
+
const [searchOpen, setSearchOpen] = (0, react.useState)(false);
|
|
12471
|
+
const [searchQuery, setSearchQuery] = (0, react.useState)("");
|
|
12472
|
+
const [searchIndex, setSearchIndex] = (0, react.useState)(0);
|
|
12473
|
+
const searchInputRef = (0, react.useRef)(null);
|
|
12474
|
+
(0, react.useEffect)(() => {
|
|
12475
|
+
setFocus(0);
|
|
12476
|
+
bodyRef.current?.focus();
|
|
12477
|
+
setFlashKey((k) => k + 1);
|
|
12478
|
+
setHoveredBlock(void 0);
|
|
12479
|
+
setSearchOpen(false);
|
|
12480
|
+
setSearchQuery("");
|
|
12481
|
+
setSearchIndex(0);
|
|
12482
|
+
}, [file.id]);
|
|
12483
|
+
(0, react.useEffect)(() => {
|
|
12484
|
+
const body = bodyRef.current;
|
|
12485
|
+
if (body === null) return;
|
|
12486
|
+
const measure = () => {
|
|
12487
|
+
setViewportH(body.clientHeight);
|
|
12488
|
+
setBodyWidth(body.clientWidth);
|
|
12489
|
+
};
|
|
12490
|
+
measure();
|
|
12491
|
+
const observer = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(measure);
|
|
12492
|
+
observer?.observe(body);
|
|
12493
|
+
return () => {
|
|
12494
|
+
observer?.disconnect();
|
|
12495
|
+
};
|
|
12496
|
+
}, [file.id]);
|
|
12497
|
+
const blockOfPair = (0, react.useMemo)(() => model.blocks.map((block) => ({
|
|
12498
|
+
start: pairOfRow.get(block.start) ?? 0,
|
|
12499
|
+
end: pairOfRow.get(block.end) ?? 0
|
|
12500
|
+
})), [model, pairOfRow]);
|
|
12501
|
+
const blockIndexByPair = (0, react.useMemo)(() => {
|
|
12502
|
+
const map = /* @__PURE__ */ new Map();
|
|
12503
|
+
blockOfPair.forEach((block, bi) => {
|
|
12504
|
+
for (let k = block.start; k <= block.end; k++) map.set(k, bi);
|
|
12505
|
+
});
|
|
12506
|
+
return map;
|
|
12507
|
+
}, [blockOfPair]);
|
|
12508
|
+
const onPairHover = (0, react.useCallback)((k) => {
|
|
12509
|
+
const bi = blockIndexByPair.get(k);
|
|
12510
|
+
setHoveredBlock(bi);
|
|
12511
|
+
if (bi !== void 0) hoveredBlockRef.current = bi;
|
|
12512
|
+
}, [blockIndexByPair]);
|
|
12513
|
+
const colWidth = Math.max(0, (bodyWidth - 1) / 2);
|
|
12514
|
+
const pairWrapped = (0, react.useMemo)(() => {
|
|
12515
|
+
if (!langWrap || bodyWidth === 0) return null;
|
|
12516
|
+
const measure = makeMeasurer(codeFontOf());
|
|
12517
|
+
if (measure === void 0) return null;
|
|
12518
|
+
const charWidth = measure("0");
|
|
12519
|
+
const wrapW = colWidth - WRAP_GUTTERS_PX / 2 - charWidth;
|
|
12520
|
+
const tabPx = tabWidthSpaces * measure(" ");
|
|
12521
|
+
return pairs.map((p) => ({
|
|
12522
|
+
left: p.left === void 0 ? void 0 : wrapInto(p.left.text, wrapW, measure, tabPx),
|
|
12523
|
+
right: p.right === void 0 ? void 0 : wrapInto(p.right.text, wrapW, measure, tabPx)
|
|
12524
|
+
}));
|
|
12525
|
+
}, [
|
|
12526
|
+
pairs,
|
|
12527
|
+
langWrap,
|
|
12528
|
+
bodyWidth,
|
|
12529
|
+
colWidth,
|
|
12530
|
+
tabWidthSpaces
|
|
12531
|
+
]);
|
|
12532
|
+
const pairHeights = (0, react.useMemo)(() => {
|
|
12533
|
+
if (pairWrapped === null) return null;
|
|
12534
|
+
return pairWrapped.map((w) => Math.max(w.left?.length ?? 1, w.right?.length ?? 1) * ROW_HEIGHT_PX);
|
|
12535
|
+
}, [pairWrapped]);
|
|
12536
|
+
const pairOffsets = (0, react.useMemo)(() => {
|
|
12537
|
+
if (pairHeights === null) return null;
|
|
12538
|
+
const offs = new Array(pairHeights.length + 1);
|
|
12539
|
+
offs[0] = 0;
|
|
12540
|
+
for (let i = 0; i < pairHeights.length; i++) offs[i + 1] = offs[i] + pairHeights[i];
|
|
12541
|
+
return offs;
|
|
12542
|
+
}, [pairHeights]);
|
|
12543
|
+
const totalHeight = pairOffsets === null ? pairCount * ROW_HEIGHT_PX : pairOffsets[pairCount] ?? 0;
|
|
12544
|
+
const off = (k) => pairOffsets === null ? k * ROW_HEIGHT_PX : pairOffsets[Math.max(0, Math.min(k, pairCount))] ?? 0;
|
|
12545
|
+
const pairHeightAt = (k) => pairHeights === null ? ROW_HEIGHT_PX : pairHeights[k] ?? ROW_HEIGHT_PX;
|
|
12546
|
+
const widestSide = (0, react.useMemo)(() => {
|
|
12547
|
+
let left = 0;
|
|
12548
|
+
let right = 0;
|
|
12549
|
+
for (const p of pairs) {
|
|
12550
|
+
if (p.left !== void 0) left = Math.max(left, p.left.text.length);
|
|
12551
|
+
if (p.right !== void 0) right = Math.max(right, p.right.text.length);
|
|
12552
|
+
}
|
|
12553
|
+
return {
|
|
12554
|
+
left,
|
|
12555
|
+
right
|
|
12556
|
+
};
|
|
12557
|
+
}, [pairs]);
|
|
12558
|
+
(0, react.useLayoutEffect)(() => {
|
|
12559
|
+
const sync = (side) => {
|
|
12560
|
+
const col = side === "left" ? leftColRef.current : rightColRef.current;
|
|
12561
|
+
const strip = side === "left" ? leftHScrollRef.current : rightHScrollRef.current;
|
|
12562
|
+
if (col === null || strip === null) return;
|
|
12563
|
+
const width = col.scrollWidth;
|
|
12564
|
+
setFillWidth((prev) => prev[side] === width ? prev : {
|
|
12565
|
+
...prev,
|
|
12566
|
+
[side]: width
|
|
12567
|
+
});
|
|
12568
|
+
strip.scrollLeft = col.scrollLeft;
|
|
12569
|
+
};
|
|
12570
|
+
sync("left");
|
|
12571
|
+
sync("right");
|
|
12572
|
+
}, [
|
|
12573
|
+
pairs,
|
|
12574
|
+
langWrap,
|
|
12575
|
+
tabWidthSpaces,
|
|
12576
|
+
bodyWidth
|
|
12577
|
+
]);
|
|
12578
|
+
const onHScroll = (0, react.useCallback)((side) => {
|
|
12579
|
+
const strip = side === "left" ? leftHScrollRef.current : rightHScrollRef.current;
|
|
12580
|
+
const col = side === "left" ? leftColRef.current : rightColRef.current;
|
|
12581
|
+
if (strip === null || col === null) return;
|
|
12582
|
+
col.scrollLeft = strip.scrollLeft;
|
|
12583
|
+
}, []);
|
|
12584
|
+
const searchMatches = (0, react.useMemo)(() => searchPairs(pairs, searchQuery), [pairs, searchQuery]);
|
|
12585
|
+
const searchHitSet = (0, react.useMemo)(() => new Set(searchMatches), [searchMatches]);
|
|
12586
|
+
const currentSearchPair = searchMatches.length === 0 ? void 0 : searchMatches[searchIndex % searchMatches.length];
|
|
12587
|
+
const closeSearch = () => {
|
|
12588
|
+
setSearchOpen(false);
|
|
12589
|
+
setSearchQuery("");
|
|
12590
|
+
setSearchIndex(0);
|
|
12591
|
+
bodyRef.current?.focus();
|
|
12592
|
+
};
|
|
12593
|
+
const openSearch = () => {
|
|
12594
|
+
setSearchOpen(true);
|
|
12595
|
+
requestAnimationFrame(() => {
|
|
12596
|
+
searchInputRef.current?.focus();
|
|
12597
|
+
searchInputRef.current?.select();
|
|
12598
|
+
});
|
|
12599
|
+
};
|
|
12600
|
+
const goSearch = (direction) => {
|
|
12601
|
+
const len = searchMatches.length;
|
|
12602
|
+
if (len === 0) return;
|
|
12603
|
+
const next = (searchIndex + direction + len) % len;
|
|
12604
|
+
setSearchIndex(next);
|
|
12605
|
+
const pairIndex = searchMatches[next];
|
|
12606
|
+
if (pairIndex === void 0) return;
|
|
12607
|
+
const body = bodyRef.current;
|
|
12608
|
+
if (body === null) return;
|
|
12609
|
+
const target = Math.max(0, off(pairIndex) - 44);
|
|
12610
|
+
const clamped = Math.max(0, Math.min(target, body.scrollHeight - body.clientHeight));
|
|
12611
|
+
if (body.scrollTop !== clamped) body.scrollTop = clamped;
|
|
12612
|
+
setScrollTop(clamped);
|
|
12613
|
+
};
|
|
12614
|
+
const handleBlockAction = async (action) => {
|
|
12615
|
+
const operated = hoveredBlock ?? hoveredBlockRef.current;
|
|
12616
|
+
if (operated === void 0) return;
|
|
12617
|
+
const range = blockRanges[operated];
|
|
12618
|
+
if (range === void 0) return;
|
|
12619
|
+
await (action === "keep" ? onBlockKeep(file.sessionId, file.id, range) : onBlockRevert(file.sessionId, file.id, range));
|
|
12620
|
+
const count = model.blocks.length;
|
|
12621
|
+
if (count === 0) return;
|
|
12622
|
+
const next = Math.max(0, Math.min(operated, count - 1));
|
|
12623
|
+
setFocus(next);
|
|
12624
|
+
setHoveredBlock(void 0);
|
|
12625
|
+
setFlashKey((key) => key + 1);
|
|
12626
|
+
};
|
|
12627
|
+
const pairAtY = (y) => {
|
|
12628
|
+
if (pairOffsets === null) return Math.floor(y / ROW_HEIGHT_PX);
|
|
12629
|
+
if (y <= 0) return 0;
|
|
12630
|
+
let lo = 0, hi = pairCount;
|
|
12631
|
+
while (lo < hi) {
|
|
12632
|
+
const mid = lo + hi + 1 >> 1;
|
|
12633
|
+
if ((pairOffsets[mid] ?? 0) <= y) lo = mid;
|
|
12634
|
+
else hi = mid - 1;
|
|
12635
|
+
}
|
|
12636
|
+
return lo;
|
|
12637
|
+
};
|
|
12638
|
+
const viewport = viewportH > 0 ? viewportH : totalHeight;
|
|
12639
|
+
const start = Math.max(0, pairAtY(scrollTop) - OVERSCAN_ROWS);
|
|
12640
|
+
const end = Math.min(pairCount, pairAtY(scrollTop + viewport) + OVERSCAN_ROWS);
|
|
12641
|
+
const visiblePairs = pairs.slice(start, end);
|
|
12642
|
+
const jump = (direction) => {
|
|
12643
|
+
if (blockOfPair.length === 0) return;
|
|
12644
|
+
setFocus((current) => {
|
|
12645
|
+
if (direction === -1) return (current - 1 + blockOfPair.length) % blockOfPair.length;
|
|
12646
|
+
const top = bodyRef.current?.scrollTop ?? 0;
|
|
12647
|
+
for (let index = current + 1; index < blockOfPair.length; index++) if (off(blockOfPair[index].start) >= top) return index;
|
|
12648
|
+
return 0;
|
|
12649
|
+
});
|
|
12650
|
+
setFlashKey((k) => k + 1);
|
|
12651
|
+
};
|
|
12652
|
+
(0, react.useImperativeHandle)(ref, () => ({
|
|
12653
|
+
jump,
|
|
12654
|
+
openSearch
|
|
12655
|
+
}), [jump, openSearch]);
|
|
12656
|
+
(0, react.useLayoutEffect)(() => {
|
|
12657
|
+
if (pairCount === 0) return;
|
|
12658
|
+
const block = blockOfPair[focus];
|
|
12659
|
+
if (block === void 0) return;
|
|
12660
|
+
const body = bodyRef.current;
|
|
12661
|
+
if (body === null) return;
|
|
12662
|
+
const target = off(block.start) - 44;
|
|
12663
|
+
const clamped = Math.max(0, Math.min(target, body.scrollHeight - body.clientHeight));
|
|
12664
|
+
if (body.scrollTop !== clamped) body.scrollTop = clamped;
|
|
12665
|
+
setScrollTop(clamped);
|
|
12666
|
+
}, [focus, flashKey]);
|
|
12667
|
+
const onScroll = () => {
|
|
12668
|
+
setScrollTop(bodyRef.current?.scrollTop ?? 0);
|
|
12669
|
+
};
|
|
12670
|
+
const inFocused = (k) => {
|
|
12671
|
+
const block = blockOfPair[focus];
|
|
12672
|
+
return block !== void 0 && k >= block.start && k <= block.end;
|
|
12673
|
+
};
|
|
12674
|
+
const blockRanges = (0, react.useMemo)(() => model.blocks.map((block) => blockRangesOf(model.diff.rows, block)), [model]);
|
|
12675
|
+
const focusedBlock = blockOfPair[focus];
|
|
12676
|
+
const flashTop = focusedBlock === void 0 ? 0 : Math.max(0, off(focusedBlock.start) - scrollTop);
|
|
12677
|
+
const flashBottom = focusedBlock === void 0 ? 0 : Math.min(viewportH > 0 ? viewportH : Number.POSITIVE_INFINITY, off(focusedBlock.end + 1) - scrollTop);
|
|
12678
|
+
const flashHeight = Math.max(0, flashBottom - flashTop);
|
|
12679
|
+
const blockActionsTop = hoveredBlock === void 0 || blockOfPair[hoveredBlock] === void 0 ? 0 : Math.max(0, Math.min(off(blockOfPair[hoveredBlock].end + 1) - scrollTop, Math.max(0, viewportH - BLOCK_ACTIONS_FRAME_PX)));
|
|
12680
|
+
return (0, react_jsx_runtime.jsxs)("div", {
|
|
12681
|
+
className: PendingPanel_module_css_default.splitRoot,
|
|
12682
|
+
onMouseLeave: () => setHoveredBlock(void 0),
|
|
12683
|
+
children: [
|
|
12684
|
+
(0, react_jsx_runtime.jsx)("div", {
|
|
12685
|
+
className: `${PendingPanel_module_css_default.diffBody} ${PendingPanel_module_css_default.diffBodySplit}`,
|
|
12686
|
+
ref: bodyRef,
|
|
12687
|
+
tabIndex: 0,
|
|
12688
|
+
onScroll,
|
|
12689
|
+
style: { tabSize: tabWidthSpaces },
|
|
12690
|
+
"data-diff-body": true,
|
|
12691
|
+
children: (0, react_jsx_runtime.jsxs)("div", {
|
|
12692
|
+
className: PendingPanel_module_css_default.splitCols,
|
|
12693
|
+
children: [
|
|
12694
|
+
(0, react_jsx_runtime.jsx)("div", {
|
|
12695
|
+
className: PendingPanel_module_css_default.splitCol,
|
|
12696
|
+
ref: leftColRef,
|
|
12697
|
+
"data-diff-split-side": "left",
|
|
12698
|
+
children: (0, react_jsx_runtime.jsxs)("div", {
|
|
12699
|
+
className: `${PendingPanel_module_css_default.lines}${langWrap ? " " + PendingPanel_module_css_default.wrap : ""}`,
|
|
12700
|
+
style: langWrap ? void 0 : { minWidth: `max(100%, ${widestSide.left}ch)` },
|
|
12701
|
+
children: [
|
|
12702
|
+
start > 0 && (0, react_jsx_runtime.jsx)("div", {
|
|
12703
|
+
className: PendingPanel_module_css_default.vSpacer,
|
|
12704
|
+
style: { height: off(start) },
|
|
12705
|
+
"aria-hidden": "true"
|
|
12706
|
+
}),
|
|
12707
|
+
visiblePairs.map((pair, offset) => {
|
|
12708
|
+
const index = start + offset;
|
|
12709
|
+
const leftRuns = pair.left === void 0 ? void 0 : runs?.oldRuns?.[(pair.left.line ?? 0) - 1];
|
|
12710
|
+
return (0, react_jsx_runtime.jsx)(SplitSideRow, {
|
|
12711
|
+
index,
|
|
12712
|
+
side: pair.left,
|
|
12713
|
+
wrapped: pairWrapped?.[index]?.left,
|
|
12714
|
+
runs: leftRuns,
|
|
12715
|
+
kind: pair.kind,
|
|
12716
|
+
isLeft: true,
|
|
12717
|
+
height: pairHeightAt(index),
|
|
12718
|
+
focused: inFocused(index),
|
|
12719
|
+
searchHit: searchHitSet.has(index),
|
|
12720
|
+
searchCurrent: index === currentSearchPair,
|
|
12721
|
+
onHover: () => onPairHover(index)
|
|
12722
|
+
}, index);
|
|
12723
|
+
}),
|
|
12724
|
+
end < pairCount && (0, react_jsx_runtime.jsx)("div", {
|
|
12725
|
+
className: PendingPanel_module_css_default.vSpacer,
|
|
12726
|
+
style: { height: totalHeight - off(end) },
|
|
12727
|
+
"aria-hidden": "true"
|
|
12728
|
+
})
|
|
12729
|
+
]
|
|
12730
|
+
})
|
|
12731
|
+
}),
|
|
12732
|
+
(0, react_jsx_runtime.jsx)("div", { className: PendingPanel_module_css_default.splitDivider }),
|
|
12733
|
+
(0, react_jsx_runtime.jsx)("div", {
|
|
12734
|
+
className: PendingPanel_module_css_default.splitCol,
|
|
12735
|
+
ref: rightColRef,
|
|
12736
|
+
"data-diff-split-side": "right",
|
|
12737
|
+
children: (0, react_jsx_runtime.jsxs)("div", {
|
|
12738
|
+
className: `${PendingPanel_module_css_default.lines}${langWrap ? " " + PendingPanel_module_css_default.wrap : ""}`,
|
|
12739
|
+
style: langWrap ? void 0 : { minWidth: `max(100%, ${widestSide.right}ch)` },
|
|
12740
|
+
children: [
|
|
12741
|
+
start > 0 && (0, react_jsx_runtime.jsx)("div", {
|
|
12742
|
+
className: PendingPanel_module_css_default.vSpacer,
|
|
12743
|
+
style: { height: off(start) },
|
|
12744
|
+
"aria-hidden": "true"
|
|
12745
|
+
}),
|
|
12746
|
+
visiblePairs.map((pair, offset) => {
|
|
12747
|
+
const index = start + offset;
|
|
12748
|
+
const rightRuns = pair.right === void 0 ? void 0 : runs?.newRuns?.[(pair.right.line ?? 0) - 1];
|
|
12749
|
+
return (0, react_jsx_runtime.jsx)(SplitSideRow, {
|
|
12750
|
+
index,
|
|
12751
|
+
side: pair.right,
|
|
12752
|
+
wrapped: pairWrapped?.[index]?.right,
|
|
12753
|
+
runs: rightRuns,
|
|
12754
|
+
kind: pair.kind,
|
|
12755
|
+
isLeft: false,
|
|
12756
|
+
height: pairHeightAt(index),
|
|
12757
|
+
focused: inFocused(index),
|
|
12758
|
+
searchHit: searchHitSet.has(index),
|
|
12759
|
+
searchCurrent: index === currentSearchPair,
|
|
12760
|
+
onHover: () => onPairHover(index)
|
|
12761
|
+
}, index);
|
|
12762
|
+
}),
|
|
12763
|
+
end < pairCount && (0, react_jsx_runtime.jsx)("div", {
|
|
12764
|
+
className: PendingPanel_module_css_default.vSpacer,
|
|
12765
|
+
style: { height: totalHeight - off(end) },
|
|
12766
|
+
"aria-hidden": "true"
|
|
12767
|
+
})
|
|
12768
|
+
]
|
|
12769
|
+
})
|
|
12770
|
+
})
|
|
12771
|
+
]
|
|
12772
|
+
})
|
|
12773
|
+
}),
|
|
12774
|
+
(0, react_jsx_runtime.jsxs)("div", {
|
|
12775
|
+
className: PendingPanel_module_css_default.splitHScrollRow,
|
|
12776
|
+
"data-diff-hscroll-row": true,
|
|
12777
|
+
children: [
|
|
12778
|
+
(0, react_jsx_runtime.jsx)("div", {
|
|
12779
|
+
className: PendingPanel_module_css_default.splitHScroll,
|
|
12780
|
+
ref: leftHScrollRef,
|
|
12781
|
+
"data-diff-hscroll": "left",
|
|
12782
|
+
style: {
|
|
12783
|
+
width: colWidth,
|
|
12784
|
+
flex: "none"
|
|
12785
|
+
},
|
|
12786
|
+
onScroll: () => onHScroll("left"),
|
|
12787
|
+
children: (0, react_jsx_runtime.jsx)("div", {
|
|
12788
|
+
className: PendingPanel_module_css_default.splitHScrollFill,
|
|
12789
|
+
style: { width: fillWidth.left || void 0 }
|
|
12790
|
+
})
|
|
12791
|
+
}),
|
|
12792
|
+
(0, react_jsx_runtime.jsx)("div", { className: PendingPanel_module_css_default.splitDivider }),
|
|
12793
|
+
(0, react_jsx_runtime.jsx)("div", {
|
|
12794
|
+
className: PendingPanel_module_css_default.splitHScroll,
|
|
12795
|
+
ref: rightHScrollRef,
|
|
12796
|
+
"data-diff-hscroll": "right",
|
|
12797
|
+
style: {
|
|
12798
|
+
width: colWidth,
|
|
12799
|
+
flex: "none"
|
|
12800
|
+
},
|
|
12801
|
+
onScroll: () => onHScroll("right"),
|
|
12802
|
+
children: (0, react_jsx_runtime.jsx)("div", {
|
|
12803
|
+
className: PendingPanel_module_css_default.splitHScrollFill,
|
|
12804
|
+
style: { width: fillWidth.right || void 0 }
|
|
12805
|
+
})
|
|
12806
|
+
})
|
|
12807
|
+
]
|
|
12808
|
+
}),
|
|
12809
|
+
searchOpen && (0, react_jsx_runtime.jsxs)("div", {
|
|
12810
|
+
className: PendingPanel_module_css_default.searchBar,
|
|
12811
|
+
"data-diff-searchbar": true,
|
|
12812
|
+
children: [
|
|
12813
|
+
(0, react_jsx_runtime.jsx)("input", {
|
|
12814
|
+
ref: searchInputRef,
|
|
12815
|
+
className: PendingPanel_module_css_default.searchInput,
|
|
12816
|
+
"data-diff-search-input": true,
|
|
12817
|
+
value: searchQuery,
|
|
12818
|
+
placeholder: t("panel.searchPlaceholder"),
|
|
12819
|
+
onChange: (event) => {
|
|
12820
|
+
setSearchQuery(event.target.value);
|
|
12821
|
+
setSearchIndex(0);
|
|
12822
|
+
},
|
|
12823
|
+
onKeyDown: (event) => {
|
|
12824
|
+
if (event.key === "Enter") {
|
|
12825
|
+
event.preventDefault();
|
|
12826
|
+
goSearch(event.shiftKey ? -1 : 1);
|
|
12827
|
+
} else if (event.key === "Escape") closeSearch();
|
|
12828
|
+
}
|
|
12829
|
+
}),
|
|
12830
|
+
(0, react_jsx_runtime.jsx)("span", {
|
|
12831
|
+
className: PendingPanel_module_css_default.searchCount,
|
|
12832
|
+
"data-diff-search-count": true,
|
|
12833
|
+
children: searchMatches.length === 0 ? "0/0" : `${searchIndex % searchMatches.length + 1}/${searchMatches.length}`
|
|
12834
|
+
}),
|
|
12835
|
+
(0, react_jsx_runtime.jsx)("button", {
|
|
12836
|
+
type: "button",
|
|
12837
|
+
className: `${PendingPanel_module_css_default.action} ${PendingPanel_module_css_default.iconAction}`,
|
|
12838
|
+
"data-diff-search-prev": true,
|
|
12839
|
+
"aria-label": t("action.prevDiff"),
|
|
12840
|
+
disabled: searchMatches.length === 0,
|
|
12841
|
+
onClick: () => {
|
|
12842
|
+
goSearch(-1);
|
|
12843
|
+
},
|
|
12844
|
+
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronUpOutline14, { size: 14 })
|
|
12845
|
+
}),
|
|
12846
|
+
(0, react_jsx_runtime.jsx)("button", {
|
|
12847
|
+
type: "button",
|
|
12848
|
+
className: `${PendingPanel_module_css_default.action} ${PendingPanel_module_css_default.iconAction}`,
|
|
12849
|
+
"data-diff-search-next": true,
|
|
12850
|
+
"aria-label": t("action.nextDiff"),
|
|
12851
|
+
disabled: searchMatches.length === 0,
|
|
12852
|
+
onClick: () => {
|
|
12853
|
+
goSearch(1);
|
|
12854
|
+
},
|
|
12855
|
+
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, { size: 14 })
|
|
12856
|
+
}),
|
|
12857
|
+
(0, react_jsx_runtime.jsx)("button", {
|
|
12858
|
+
type: "button",
|
|
12859
|
+
className: `${PendingPanel_module_css_default.action} ${PendingPanel_module_css_default.iconAction}`,
|
|
12860
|
+
"data-diff-search-close": true,
|
|
12861
|
+
"aria-label": t("action.close"),
|
|
12862
|
+
onClick: closeSearch,
|
|
12863
|
+
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCloseOutline16, { size: 14 })
|
|
12864
|
+
})
|
|
12865
|
+
]
|
|
12866
|
+
}),
|
|
12867
|
+
focusedBlock !== void 0 && flashKey > 0 && (0, react_jsx_runtime.jsx)("div", {
|
|
12868
|
+
className: PendingPanel_module_css_default.blockFlash,
|
|
12869
|
+
"data-diff-block-flash": true,
|
|
12870
|
+
style: {
|
|
12871
|
+
top: flashTop,
|
|
12872
|
+
height: flashHeight
|
|
12873
|
+
}
|
|
12874
|
+
}, flashKey),
|
|
12875
|
+
hoveredBlock !== void 0 && blockOfPair[hoveredBlock] !== void 0 && (0, react_jsx_runtime.jsxs)("div", {
|
|
12876
|
+
className: PendingPanel_module_css_default.blockActions,
|
|
12877
|
+
"data-diff-block-actions": true,
|
|
12878
|
+
style: { top: blockActionsTop },
|
|
12879
|
+
children: [
|
|
12880
|
+
(0, react_jsx_runtime.jsx)("span", {
|
|
12881
|
+
className: PendingPanel_module_css_default.blockPosition,
|
|
12882
|
+
"data-diff-block-position": true,
|
|
12883
|
+
children: t("panel.blockPosition", {
|
|
12884
|
+
current: hoveredBlock + 1,
|
|
12885
|
+
total: blockOfPair.length
|
|
12886
|
+
})
|
|
12887
|
+
}),
|
|
12888
|
+
(0, react_jsx_runtime.jsx)("button", {
|
|
12889
|
+
type: "button",
|
|
12890
|
+
className: `${PendingPanel_module_css_default.action} ${PendingPanel_module_css_default.iconAction}`,
|
|
12891
|
+
"data-diff-block-prev": true,
|
|
12892
|
+
"aria-label": t("action.prevDiff"),
|
|
12893
|
+
disabled: busy,
|
|
12894
|
+
onClick: () => jump(-1),
|
|
12895
|
+
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronUpOutline14, { size: 14 })
|
|
12896
|
+
}),
|
|
12897
|
+
(0, react_jsx_runtime.jsx)("button", {
|
|
12898
|
+
type: "button",
|
|
12899
|
+
className: `${PendingPanel_module_css_default.action} ${PendingPanel_module_css_default.iconAction}`,
|
|
12900
|
+
"data-diff-block-next": true,
|
|
12901
|
+
"aria-label": t("action.nextDiff"),
|
|
12902
|
+
disabled: busy,
|
|
12903
|
+
onClick: () => jump(1),
|
|
12904
|
+
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, { size: 14 })
|
|
12905
|
+
}),
|
|
12906
|
+
(0, react_jsx_runtime.jsx)("button", {
|
|
12907
|
+
type: "button",
|
|
12908
|
+
className: `${PendingPanel_module_css_default.action} ${PendingPanel_module_css_default.actionPrimary}`,
|
|
12909
|
+
"data-diff-block-keep": true,
|
|
12910
|
+
disabled: busy,
|
|
12911
|
+
onClick: () => {
|
|
12912
|
+
handleBlockAction("keep");
|
|
12913
|
+
},
|
|
12914
|
+
children: t("action.keep")
|
|
12915
|
+
}),
|
|
12916
|
+
(0, react_jsx_runtime.jsx)("button", {
|
|
12917
|
+
type: "button",
|
|
12918
|
+
className: `${PendingPanel_module_css_default.action}`,
|
|
12919
|
+
"data-diff-block-revert": true,
|
|
12920
|
+
disabled: busy,
|
|
12921
|
+
onClick: () => {
|
|
12922
|
+
handleBlockAction("revert");
|
|
12923
|
+
},
|
|
12924
|
+
children: t("action.revert")
|
|
12925
|
+
})
|
|
12926
|
+
]
|
|
12927
|
+
})
|
|
12928
|
+
]
|
|
12929
|
+
});
|
|
12930
|
+
});
|
|
12931
|
+
/** The diff-row index containing a node, or undefined. */
|
|
12932
|
+
function rowIndexAt(node) {
|
|
12933
|
+
if (node === null) return void 0;
|
|
12934
|
+
const row = (node instanceof Element ? node : node.parentElement)?.closest("[data-diff-row]");
|
|
12935
|
+
if (row === null || row === void 0) return void 0;
|
|
12936
|
+
const index = Number(row.dataset.diffRow);
|
|
12937
|
+
return Number.isFinite(index) ? index : void 0;
|
|
12938
|
+
}
|
|
12939
|
+
/** The split pair index and which side (left=old, right=new) a node sits in, or undefined. */
|
|
12940
|
+
function splitRowInfoAt(node) {
|
|
12941
|
+
if (node === null) return void 0;
|
|
12942
|
+
const row = (node instanceof Element ? node : node.parentElement)?.closest("[data-diff-split-row]");
|
|
12943
|
+
if (row === null || row === void 0) return void 0;
|
|
12944
|
+
const side = row.dataset.diffSplitSide;
|
|
12945
|
+
if (side !== "left" && side !== "right") return void 0;
|
|
12946
|
+
const index = Number(row.dataset.diffSplitIndex);
|
|
12947
|
+
if (!Number.isFinite(index)) return void 0;
|
|
12948
|
+
return {
|
|
12949
|
+
pairIndex: index,
|
|
12950
|
+
side: side === "left" ? "old" : "new"
|
|
12951
|
+
};
|
|
12952
|
+
}
|
|
12953
|
+
/**
|
|
12954
|
+
* Derive the selected split pair range per side (a left-column selection
|
|
12955
|
+
* references the old file, a right-column selection the new file). A selection
|
|
12956
|
+
* spanning the divider (both sides) references two files, so it is rejected.
|
|
12957
|
+
*/
|
|
12958
|
+
function splitRowRangeOf(selection) {
|
|
12959
|
+
if (selection === null || selection.isCollapsed || selection.rangeCount === 0) return void 0;
|
|
12960
|
+
const range = selection.getRangeAt(0);
|
|
12961
|
+
const startInfo = splitRowInfoAt(range.startContainer);
|
|
12962
|
+
const endInfo = splitRowInfoAt(range.endContainer);
|
|
12963
|
+
if (startInfo === void 0 || endInfo === void 0) return void 0;
|
|
12964
|
+
if (startInfo.side !== endInfo.side) return void 0;
|
|
12965
|
+
let start = startInfo.pairIndex;
|
|
12966
|
+
let end = endInfo.pairIndex;
|
|
12967
|
+
if (lineOffsetAt(range.startContainer, range.startOffset) >= lineLengthAt(range.startContainer)) start += 1;
|
|
12968
|
+
if (lineOffsetAt(range.endContainer, range.endOffset) === 0) end -= 1;
|
|
12969
|
+
if (start > end) return void 0;
|
|
12970
|
+
return {
|
|
12971
|
+
start,
|
|
12972
|
+
end,
|
|
12973
|
+
side: startInfo.side
|
|
12974
|
+
};
|
|
12975
|
+
}
|
|
12976
|
+
/** Character offset of a selection boundary within its line's code text. */
|
|
12977
|
+
function lineOffsetAt(node, offset) {
|
|
12978
|
+
const code = (node instanceof Element ? node : node.parentElement)?.closest("[data-diff-code]");
|
|
12979
|
+
if (code === null || code === void 0) return 0;
|
|
12980
|
+
let before = 0;
|
|
12981
|
+
const walker = document.createTreeWalker(code, NodeFilter.SHOW_TEXT);
|
|
12982
|
+
let current = walker.nextNode();
|
|
12983
|
+
while (current !== null) {
|
|
12984
|
+
if (current === node) return before + offset;
|
|
12985
|
+
if (node instanceof Element && node.contains(current)) break;
|
|
12986
|
+
before += current.length;
|
|
12987
|
+
current = walker.nextNode();
|
|
12988
|
+
}
|
|
12989
|
+
if (node instanceof Element) {
|
|
12990
|
+
const children = [...node.childNodes];
|
|
12991
|
+
for (let i = 0; i < Math.min(offset, children.length); i++) {
|
|
12992
|
+
const inner = document.createTreeWalker(children[i], NodeFilter.SHOW_TEXT);
|
|
12993
|
+
let text = inner.nextNode();
|
|
12994
|
+
while (text !== null) {
|
|
12995
|
+
before += text.length;
|
|
12996
|
+
text = inner.nextNode();
|
|
12997
|
+
}
|
|
12998
|
+
}
|
|
12999
|
+
}
|
|
13000
|
+
return before;
|
|
13001
|
+
}
|
|
13002
|
+
/** Length of the code text on the line holding a node. */
|
|
13003
|
+
function lineLengthAt(node) {
|
|
13004
|
+
return ((node instanceof Element ? node : node.parentElement)?.closest("[data-diff-code]"))?.textContent?.length ?? 0;
|
|
13005
|
+
}
|
|
13006
|
+
/**
|
|
13007
|
+
* Derive the selected diff-row range from a native text selection. A
|
|
13008
|
+
* boundary sitting exactly at a line edge contributes no content: a start at
|
|
13009
|
+
* the line's end skips to the next line, an end at the line's start falls
|
|
13010
|
+
* back to the previous line.
|
|
13011
|
+
*/
|
|
13012
|
+
function rowRangeOf(selection) {
|
|
13013
|
+
if (selection === null || selection.isCollapsed || selection.rangeCount === 0) return void 0;
|
|
13014
|
+
const range = selection.getRangeAt(0);
|
|
13015
|
+
let start = rowIndexAt(range.startContainer);
|
|
13016
|
+
let end = rowIndexAt(range.endContainer);
|
|
13017
|
+
if (start === void 0 || end === void 0) return void 0;
|
|
13018
|
+
if (lineOffsetAt(range.startContainer, range.startOffset) >= lineLengthAt(range.startContainer)) start += 1;
|
|
13019
|
+
if (lineOffsetAt(range.endContainer, range.endOffset) === 0) end -= 1;
|
|
13020
|
+
if (start > end) return void 0;
|
|
13021
|
+
return {
|
|
13022
|
+
start,
|
|
13023
|
+
end
|
|
13024
|
+
};
|
|
13025
|
+
}
|
|
13026
|
+
/** One row of the file list: the clickable head in the left pane. */
|
|
13027
|
+
function PendingFileRow({ file, selected, failedMessage, t, onSelect }) {
|
|
12218
13028
|
const stats = (0, react.useMemo)(() => computeWholeFileDiff(file.oldText, file.newText), [file.oldText, file.newText]);
|
|
12219
13029
|
return (0, react_jsx_runtime.jsx)("li", {
|
|
12220
13030
|
className: PendingPanel_module_css_default.row,
|
|
@@ -12248,7 +13058,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
12248
13058
|
title: failedMessage,
|
|
12249
13059
|
children: t("row.failed")
|
|
12250
13060
|
}),
|
|
12251
|
-
(0, react_jsx_runtime.jsxs)("span", {
|
|
13061
|
+
(stats.added !== 0 || stats.removed !== 0) && (0, react_jsx_runtime.jsxs)("span", {
|
|
12252
13062
|
className: PendingPanel_module_css_default.rowMeta,
|
|
12253
13063
|
children: [(0, react_jsx_runtime.jsx)("span", {
|
|
12254
13064
|
className: PendingPanel_module_css_default.addCount,
|
|
@@ -12264,7 +13074,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
12264
13074
|
});
|
|
12265
13075
|
}
|
|
12266
13076
|
/** The selected file's diff, actions, jump controls, and copy toolbar. */
|
|
12267
|
-
function PendingDiff({ file, busy, workspacePath, jumpSignal, undoFlash, failedMessage, onPasteReference, t, onKeep, onRevert, onBlockKeep, onBlockRevert, onOpen, floatMode, floatOpen, onToggleFileList }) {
|
|
13077
|
+
function PendingDiff({ file, busy, workspacePath, jumpSignal, undoFlash, failedMessage, onPasteReference, onToast, t, onKeep, onRevert, onBlockKeep, onBlockRevert, onOpen, floatMode, floatOpen, onToggleFileList }) {
|
|
12268
13078
|
const [langOverride, setLangOverride] = (0, react.useState)(void 0);
|
|
12269
13079
|
const [langMenuOpen, setLangMenuOpen] = (0, react.useState)(false);
|
|
12270
13080
|
const langMenuItems = (0, react.useMemo)(() => [{
|
|
@@ -12288,6 +13098,8 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
12288
13098
|
setWrapEnabled(wrapKey, next);
|
|
12289
13099
|
};
|
|
12290
13100
|
const [tabWidthSpaces] = (0, react.useState)(() => tabWidth());
|
|
13101
|
+
const splitView = splitMode();
|
|
13102
|
+
const splitDiffRef = (0, react.useRef)(null);
|
|
12291
13103
|
const model = (0, react.useMemo)(() => {
|
|
12292
13104
|
const diff = computeWholeFileDiff(file.oldText, file.newText);
|
|
12293
13105
|
return {
|
|
@@ -12295,6 +13107,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
12295
13107
|
blocks: changeBlocksOf(diff)
|
|
12296
13108
|
};
|
|
12297
13109
|
}, [file.oldText, file.newText]);
|
|
13110
|
+
const splitPairs = (0, react.useMemo)(() => splitView ? computeSideBySideDiff(model.diff.rows).pairs : null, [splitView, model]);
|
|
12298
13111
|
const rulerMarkers = (0, react.useMemo)(() => {
|
|
12299
13112
|
const rows = model.diff.rows;
|
|
12300
13113
|
const total = rows.length;
|
|
@@ -12365,6 +13178,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
12365
13178
|
const [flashKey, setFlashKey] = (0, react.useState)(0);
|
|
12366
13179
|
(0, react.useEffect)(() => {
|
|
12367
13180
|
setFocus(0);
|
|
13181
|
+
setScrollTick((tick) => tick + 1);
|
|
12368
13182
|
bodyRef.current?.focus();
|
|
12369
13183
|
setFlashKey((key) => key + 1);
|
|
12370
13184
|
setHoveredBlock(void 0);
|
|
@@ -12385,6 +13199,32 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
12385
13199
|
const blockRanges = (0, react.useMemo)(() => {
|
|
12386
13200
|
return model.blocks.map((block) => blockRangesOf(model.diff.rows, block));
|
|
12387
13201
|
}, [model]);
|
|
13202
|
+
const coveredBlockIndices = (0, react.useMemo)(() => {
|
|
13203
|
+
if (selection === void 0 || splitView) return [];
|
|
13204
|
+
const covered = [];
|
|
13205
|
+
for (let index = 0; index < model.blocks.length; index++) {
|
|
13206
|
+
const block = model.blocks[index];
|
|
13207
|
+
if (selection.start <= block.start && block.end <= selection.end) covered.push(index);
|
|
13208
|
+
}
|
|
13209
|
+
return covered;
|
|
13210
|
+
}, [
|
|
13211
|
+
selection,
|
|
13212
|
+
splitView,
|
|
13213
|
+
model
|
|
13214
|
+
]);
|
|
13215
|
+
const selectionRange = (0, react.useMemo)(() => {
|
|
13216
|
+
if (coveredBlockIndices.length === 0) return void 0;
|
|
13217
|
+
const firstIndex = coveredBlockIndices[0];
|
|
13218
|
+
const lastIndex = coveredBlockIndices[coveredBlockIndices.length - 1];
|
|
13219
|
+
if (firstIndex === void 0 || lastIndex === void 0) return void 0;
|
|
13220
|
+
const first = model.blocks[firstIndex];
|
|
13221
|
+
const last = model.blocks[lastIndex];
|
|
13222
|
+
if (first === void 0 || last === void 0) return void 0;
|
|
13223
|
+
return blockRangesOf(model.diff.rows, {
|
|
13224
|
+
start: first.start,
|
|
13225
|
+
end: last.end
|
|
13226
|
+
});
|
|
13227
|
+
}, [coveredBlockIndices, model]);
|
|
12388
13228
|
const searchMatches = (0, react.useMemo)(() => {
|
|
12389
13229
|
if (searchQuery === "") return [];
|
|
12390
13230
|
const lower = searchQuery.toLowerCase();
|
|
@@ -12490,6 +13330,13 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
12490
13330
|
const visibleRows = rows.slice(start, end);
|
|
12491
13331
|
const blockEnd = hoveredBlock === void 0 ? void 0 : model.blocks[hoveredBlock]?.end;
|
|
12492
13332
|
const blockActionsTop = blockEnd === void 0 ? 0 : Math.min(offsetOf(blockEnd + 1), Math.max(0, totalHeight - BLOCK_ACTIONS_FRAME_PX));
|
|
13333
|
+
const selectionBlockEnd = (() => {
|
|
13334
|
+
if (coveredBlockIndices.length === 0) return void 0;
|
|
13335
|
+
const lastIndex = coveredBlockIndices[coveredBlockIndices.length - 1];
|
|
13336
|
+
if (lastIndex === void 0) return void 0;
|
|
13337
|
+
return model.blocks[lastIndex]?.end;
|
|
13338
|
+
})();
|
|
13339
|
+
const selectionActionsTop = selectionBlockEnd === void 0 ? 0 : Math.min(offsetOf(selectionBlockEnd + 1), Math.max(0, totalHeight - BLOCK_ACTIONS_FRAME_PX));
|
|
12493
13340
|
const widestLine = (0, react.useMemo)(() => {
|
|
12494
13341
|
let widest = 0;
|
|
12495
13342
|
for (const row of model.diff.rows) if (row.text.length > widest) widest = row.text.length;
|
|
@@ -12533,10 +13380,8 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
12533
13380
|
if (body.scrollTop !== clamped) body.scrollTop = clamped;
|
|
12534
13381
|
setScrollTop(clamped);
|
|
12535
13382
|
}, [
|
|
12536
|
-
model,
|
|
12537
13383
|
focus,
|
|
12538
13384
|
scrollTick,
|
|
12539
|
-
rowCount,
|
|
12540
13385
|
rowOffsets === null
|
|
12541
13386
|
]);
|
|
12542
13387
|
const jump = (direction) => {
|
|
@@ -12554,6 +13399,15 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
12554
13399
|
setScrollTick((tick) => tick + 1);
|
|
12555
13400
|
setFlashKey((key) => key + 1);
|
|
12556
13401
|
};
|
|
13402
|
+
const jumpBlock = (direction) => {
|
|
13403
|
+
if (splitView) {
|
|
13404
|
+
splitDiffRef.current?.jump(direction);
|
|
13405
|
+
return;
|
|
13406
|
+
}
|
|
13407
|
+
jump(direction);
|
|
13408
|
+
};
|
|
13409
|
+
const jumpBlockRef = (0, react.useRef)(jumpBlock);
|
|
13410
|
+
jumpBlockRef.current = jumpBlock;
|
|
12557
13411
|
const stepBlock = (direction) => {
|
|
12558
13412
|
const count = model.blocks.length;
|
|
12559
13413
|
if (count === 0) return;
|
|
@@ -12563,9 +13417,31 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
12563
13417
|
setScrollTick((tick) => tick + 1);
|
|
12564
13418
|
setFlashKey((key) => key + 1);
|
|
12565
13419
|
};
|
|
13420
|
+
const runBlockAction = async (action, range, operated) => {
|
|
13421
|
+
await (action === "keep" ? onBlockKeep(file.sessionId, file.id, range) : onBlockRevert(file.sessionId, file.id, range));
|
|
13422
|
+
const count = model.blocks.length;
|
|
13423
|
+
if (count === 0) return;
|
|
13424
|
+
const next = Math.max(0, Math.min(operated, count - 1));
|
|
13425
|
+
setFocus(next);
|
|
13426
|
+
setHoveredBlock(void 0);
|
|
13427
|
+
setScrollTick((tick) => tick + 1);
|
|
13428
|
+
setFlashKey((key) => key + 1);
|
|
13429
|
+
};
|
|
13430
|
+
const handleBlockAction = async (action) => {
|
|
13431
|
+
if (busy || hoveredBlock === void 0) return;
|
|
13432
|
+
const operated = hoveredBlock;
|
|
13433
|
+
const range = blockRanges[operated];
|
|
13434
|
+
await runBlockAction(action, range, operated);
|
|
13435
|
+
};
|
|
13436
|
+
const handleSelectionAction = async (action) => {
|
|
13437
|
+
if (busy || selectionRange === void 0) return;
|
|
13438
|
+
const firstCovered = coveredBlockIndices[0];
|
|
13439
|
+
if (firstCovered === void 0) return;
|
|
13440
|
+
await runBlockAction(action, selectionRange, firstCovered);
|
|
13441
|
+
};
|
|
12566
13442
|
(0, react.useEffect)(() => {
|
|
12567
13443
|
if (jumpSignal === 0) return;
|
|
12568
|
-
|
|
13444
|
+
jumpBlock(1);
|
|
12569
13445
|
}, [jumpSignal]);
|
|
12570
13446
|
const onScroll = () => {
|
|
12571
13447
|
const body = bodyRef.current;
|
|
@@ -12574,31 +13450,49 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
12574
13450
|
setViewportHeight(body.clientHeight);
|
|
12575
13451
|
};
|
|
12576
13452
|
(0, react.useEffect)(() => {
|
|
12577
|
-
const update = () => setSelection(rowRangeOf(window.getSelection()));
|
|
13453
|
+
const update = () => setSelection(splitView ? splitRowRangeOf(window.getSelection()) : rowRangeOf(window.getSelection()));
|
|
12578
13454
|
document.addEventListener("selectionchange", update);
|
|
12579
13455
|
update();
|
|
12580
13456
|
return () => {
|
|
12581
13457
|
document.removeEventListener("selectionchange", update);
|
|
12582
13458
|
};
|
|
12583
|
-
}, [file.id]);
|
|
13459
|
+
}, [file.id, splitView]);
|
|
12584
13460
|
const selectionReference = (() => {
|
|
12585
13461
|
if (selection === void 0) return void 0;
|
|
13462
|
+
if (splitView) {
|
|
13463
|
+
if (selection.side === void 0 || splitPairs === null) return void 0;
|
|
13464
|
+
const lineNumbers = [];
|
|
13465
|
+
for (let index = selection.start; index <= selection.end; index++) {
|
|
13466
|
+
const pair = splitPairs[index];
|
|
13467
|
+
if (pair === void 0) continue;
|
|
13468
|
+
const line = selection.side === "old" ? pair.left?.line : pair.right?.line;
|
|
13469
|
+
if (line !== void 0) lineNumbers.push(line);
|
|
13470
|
+
}
|
|
13471
|
+
if (lineNumbers.length === 0) return void 0;
|
|
13472
|
+
return referenceOf(file.path, workspacePath, Math.min(...lineNumbers), Math.max(...lineNumbers));
|
|
13473
|
+
}
|
|
12586
13474
|
const lineNumbers = model.diff.rows.slice(selection.start, selection.end + 1).map((row) => row.newLine).filter((number) => number !== void 0);
|
|
12587
13475
|
if (lineNumbers.length === 0) return void 0;
|
|
12588
13476
|
return referenceOf(file.path, workspacePath, Math.min(...lineNumbers), Math.max(...lineNumbers));
|
|
12589
13477
|
})();
|
|
12590
13478
|
const copySelection = (0, react.useCallback)(async () => {
|
|
12591
13479
|
if (selectionReference === void 0) return;
|
|
13480
|
+
if (pasteOnCopyEnabled()) {
|
|
13481
|
+
onPasteReference(file.sessionId, selectionReference);
|
|
13482
|
+
return;
|
|
13483
|
+
}
|
|
12592
13484
|
if (!await (0, _deepseek_ai_dsh_client_ui_primitives.writeClipboard)(selectionReference)) return;
|
|
12593
13485
|
setCopied(true);
|
|
12594
|
-
|
|
13486
|
+
onToast(t("action.copied"));
|
|
12595
13487
|
window.setTimeout(() => {
|
|
12596
13488
|
setCopied(false);
|
|
12597
13489
|
}, 1500);
|
|
12598
13490
|
}, [
|
|
12599
13491
|
file.sessionId,
|
|
12600
13492
|
onPasteReference,
|
|
12601
|
-
|
|
13493
|
+
onToast,
|
|
13494
|
+
selectionReference,
|
|
13495
|
+
t
|
|
12602
13496
|
]);
|
|
12603
13497
|
(0, react.useEffect)(() => {
|
|
12604
13498
|
const onKeyDown = (event) => {
|
|
@@ -12618,6 +13512,10 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
12618
13512
|
if (!(event.ctrlKey || event.metaKey) || event.altKey || event.shiftKey) return;
|
|
12619
13513
|
if (event.key.toLowerCase() !== "f") return;
|
|
12620
13514
|
event.preventDefault();
|
|
13515
|
+
if (splitView) {
|
|
13516
|
+
splitDiffRef.current?.openSearch();
|
|
13517
|
+
return;
|
|
13518
|
+
}
|
|
12621
13519
|
setSearchOpen(true);
|
|
12622
13520
|
searchInputRef.current?.focus();
|
|
12623
13521
|
searchInputRef.current?.select();
|
|
@@ -12627,8 +13525,8 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
12627
13525
|
window.removeEventListener("keydown", onKeyDown, true);
|
|
12628
13526
|
};
|
|
12629
13527
|
}, []);
|
|
12630
|
-
const jumpRef = (0, react.useRef)(
|
|
12631
|
-
jumpRef.current =
|
|
13528
|
+
const jumpRef = (0, react.useRef)(jumpBlock);
|
|
13529
|
+
jumpRef.current = jumpBlock;
|
|
12632
13530
|
(0, react.useEffect)(() => {
|
|
12633
13531
|
const onKeyDown = (event) => {
|
|
12634
13532
|
if (!(event.ctrlKey || event.metaKey) || event.altKey || event.shiftKey) return;
|
|
@@ -12714,7 +13612,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
12714
13612
|
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconListPenOutline16, { size: 14 })
|
|
12715
13613
|
})
|
|
12716
13614
|
}),
|
|
12717
|
-
(0, react_jsx_runtime.jsx)("span", {
|
|
13615
|
+
(model.diff.added !== 0 || model.diff.removed !== 0) && (0, react_jsx_runtime.jsx)("span", {
|
|
12718
13616
|
className: PendingPanel_module_css_default.diffStats,
|
|
12719
13617
|
children: t("panel.stats", {
|
|
12720
13618
|
added: model.diff.added,
|
|
@@ -12736,7 +13634,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
12736
13634
|
"aria-label": t("action.prevDiff"),
|
|
12737
13635
|
disabled: busy,
|
|
12738
13636
|
onClick: () => {
|
|
12739
|
-
|
|
13637
|
+
jumpBlock(-1);
|
|
12740
13638
|
},
|
|
12741
13639
|
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronUpOutline14, { size: 14 })
|
|
12742
13640
|
})
|
|
@@ -12751,7 +13649,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
12751
13649
|
"aria-label": t("action.nextDiff"),
|
|
12752
13650
|
disabled: busy,
|
|
12753
13651
|
onClick: () => {
|
|
12754
|
-
|
|
13652
|
+
jumpBlock(1);
|
|
12755
13653
|
},
|
|
12756
13654
|
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, { size: 14 })
|
|
12757
13655
|
})
|
|
@@ -12801,7 +13699,18 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
12801
13699
|
className: PendingPanel_module_css_default.missingHint,
|
|
12802
13700
|
children: t("panel.missingHint")
|
|
12803
13701
|
}),
|
|
12804
|
-
(0, react_jsx_runtime.
|
|
13702
|
+
splitView ? (0, react_jsx_runtime.jsx)(SplitDiff, {
|
|
13703
|
+
ref: splitDiffRef,
|
|
13704
|
+
file,
|
|
13705
|
+
model,
|
|
13706
|
+
runs,
|
|
13707
|
+
langWrap,
|
|
13708
|
+
tabWidthSpaces,
|
|
13709
|
+
busy,
|
|
13710
|
+
t,
|
|
13711
|
+
onBlockKeep,
|
|
13712
|
+
onBlockRevert
|
|
13713
|
+
}) : (0, react_jsx_runtime.jsxs)("div", {
|
|
12805
13714
|
className: PendingPanel_module_css_default.diffBodyWrap,
|
|
12806
13715
|
children: [
|
|
12807
13716
|
(0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -12845,7 +13754,30 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
12845
13754
|
"aria-hidden": "true"
|
|
12846
13755
|
})
|
|
12847
13756
|
]
|
|
12848
|
-
}),
|
|
13757
|
+
}), selectionRange !== void 0 ? (0, react_jsx_runtime.jsxs)("div", {
|
|
13758
|
+
className: PendingPanel_module_css_default.blockActions,
|
|
13759
|
+
"data-diff-selection-actions": true,
|
|
13760
|
+
style: { top: selectionActionsTop },
|
|
13761
|
+
children: [(0, react_jsx_runtime.jsx)("button", {
|
|
13762
|
+
type: "button",
|
|
13763
|
+
className: `${PendingPanel_module_css_default.action} ${PendingPanel_module_css_default.actionPrimary}`,
|
|
13764
|
+
"data-diff-selection-keep": true,
|
|
13765
|
+
disabled: busy,
|
|
13766
|
+
onClick: () => {
|
|
13767
|
+
handleSelectionAction("keep");
|
|
13768
|
+
},
|
|
13769
|
+
children: t("action.keep")
|
|
13770
|
+
}), (0, react_jsx_runtime.jsx)("button", {
|
|
13771
|
+
type: "button",
|
|
13772
|
+
className: PendingPanel_module_css_default.action,
|
|
13773
|
+
"data-diff-selection-revert": true,
|
|
13774
|
+
disabled: busy,
|
|
13775
|
+
onClick: () => {
|
|
13776
|
+
handleSelectionAction("revert");
|
|
13777
|
+
},
|
|
13778
|
+
children: t("action.revert")
|
|
13779
|
+
})]
|
|
13780
|
+
}) : hoveredBlock !== void 0 && model.blocks[hoveredBlock] !== void 0 ? (0, react_jsx_runtime.jsxs)("div", {
|
|
12849
13781
|
className: PendingPanel_module_css_default.blockActions,
|
|
12850
13782
|
"data-diff-block-actions": true,
|
|
12851
13783
|
style: { top: blockActionsTop },
|
|
@@ -12886,7 +13818,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
12886
13818
|
"data-diff-block-keep": true,
|
|
12887
13819
|
disabled: busy,
|
|
12888
13820
|
onClick: () => {
|
|
12889
|
-
|
|
13821
|
+
handleBlockAction("keep");
|
|
12890
13822
|
},
|
|
12891
13823
|
children: t("action.keep")
|
|
12892
13824
|
}),
|
|
@@ -12896,12 +13828,12 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
12896
13828
|
"data-diff-block-revert": true,
|
|
12897
13829
|
disabled: busy,
|
|
12898
13830
|
onClick: () => {
|
|
12899
|
-
|
|
13831
|
+
handleBlockAction("revert");
|
|
12900
13832
|
},
|
|
12901
13833
|
children: t("action.revert")
|
|
12902
13834
|
})
|
|
12903
13835
|
]
|
|
12904
|
-
})]
|
|
13836
|
+
}) : null]
|
|
12905
13837
|
}),
|
|
12906
13838
|
focusedBlock !== void 0 && flashKey > 0 && (0, react_jsx_runtime.jsx)("div", {
|
|
12907
13839
|
className: PendingPanel_module_css_default.blockFlash,
|
|
@@ -13063,7 +13995,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
13063
13995
|
});
|
|
13064
13996
|
}
|
|
13065
13997
|
/** Render the pending-edit review panel and its unified footer action. */
|
|
13066
|
-
function PendingPanel({ wide, useSessions, usePending, onRefresh, onKeep, onRevert, onBlockKeep, onBlockRevert, onOpen, onPasteReference, onUndo, onRedo, onImportVcs, onAckRedoCleared, t }) {
|
|
13998
|
+
function PendingPanel({ wide, useSessions, usePending, onRefresh, onKeep, onRevert, onBlockKeep, onBlockRevert, onOpen, onPasteReference, onUndo, onRedo, onImportVcs, onAckRedoCleared, onAckJustResolved, collapseSidebar, t }) {
|
|
13067
13999
|
const current = useSessions((state) => state.current);
|
|
13068
14000
|
const currentBlank = useSessions((state) => {
|
|
13069
14001
|
const id = state.current;
|
|
@@ -13091,8 +14023,12 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
13091
14023
|
const [importToast, setImportToast] = (0, react.useState)(null);
|
|
13092
14024
|
/** A transient banner for a keep/revert failure. */
|
|
13093
14025
|
const [actionToast, setActionToast] = (0, react.useState)(null);
|
|
14026
|
+
/** A transient banner confirming a reference was copied to the clipboard. */
|
|
14027
|
+
const [copyToast, setCopyToast] = (0, react.useState)(null);
|
|
13094
14028
|
/** Whether the redo-cleared notice is showing (bottom-right, OK to dismiss). */
|
|
13095
14029
|
const [redoClearedNotice, setRedoClearedNotice] = (0, react.useState)(false);
|
|
14030
|
+
/** A file whose last block just resolved, pending a remove-or-keep choice. */
|
|
14031
|
+
const [confirmDismiss, setConfirmDismiss] = (0, react.useState)(null);
|
|
13096
14032
|
/** Bottom offset tracking the chat composer's top edge so the input stays visible. */
|
|
13097
14033
|
const [bottomPx, setBottomPx] = (0, react.useState)(FALLBACK_BOTTOM_PX);
|
|
13098
14034
|
/** Fullscreen expanded: the panel bottom pins to the window edge, ignoring the composer offset. */
|
|
@@ -13105,6 +14041,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
13105
14041
|
/** The review panel's width, measured so the file list can collapse when it
|
|
13106
14042
|
* would take more than a third of it (browser zoom / window resize). */
|
|
13107
14043
|
const [panelWidth, setPanelWidth] = (0, react.useState)(0);
|
|
14044
|
+
const [viewportWidth, setViewportWidth] = (0, react.useState)(() => window.innerWidth);
|
|
13108
14045
|
const panelRef = (0, react.useRef)(null);
|
|
13109
14046
|
const splitRef = (0, react.useRef)(null);
|
|
13110
14047
|
/** The code scroll box's bounds within the split, so the floating card is
|
|
@@ -13132,10 +14069,19 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
13132
14069
|
observer?.disconnect();
|
|
13133
14070
|
};
|
|
13134
14071
|
}, [open]);
|
|
13135
|
-
const floatMode =
|
|
14072
|
+
const floatMode = viewportWidth < SIDEBAR_AUTO_COLLAPSE_PX;
|
|
13136
14073
|
const toggleFileList = () => {
|
|
13137
14074
|
setFloatOpen((value) => !value);
|
|
13138
14075
|
};
|
|
14076
|
+
(0, react.useEffect)(() => {
|
|
14077
|
+
const onResize = () => {
|
|
14078
|
+
setViewportWidth(window.innerWidth);
|
|
14079
|
+
};
|
|
14080
|
+
window.addEventListener("resize", onResize);
|
|
14081
|
+
return () => {
|
|
14082
|
+
window.removeEventListener("resize", onResize);
|
|
14083
|
+
};
|
|
14084
|
+
}, []);
|
|
13139
14085
|
(0, react.useEffect)(() => {
|
|
13140
14086
|
if (!floatMode || !floatOpen) return;
|
|
13141
14087
|
const el = panelRef.current;
|
|
@@ -13237,6 +14183,11 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
13237
14183
|
if (fresh.length > 0) setActionToast(fresh[0][1]);
|
|
13238
14184
|
failedRef.current = current;
|
|
13239
14185
|
}, [snapshot.failed]);
|
|
14186
|
+
(0, react.useEffect)(() => {
|
|
14187
|
+
if (snapshot.justResolved === void 0) return;
|
|
14188
|
+
setConfirmDismiss(snapshot.justResolved);
|
|
14189
|
+
onAckJustResolved();
|
|
14190
|
+
}, [snapshot.justResolved, onAckJustResolved]);
|
|
13240
14191
|
(0, react.useEffect)(() => {
|
|
13241
14192
|
if (!open) return;
|
|
13242
14193
|
if (selected !== "" && files.some((file) => file.id === selected)) return;
|
|
@@ -13267,6 +14218,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
13267
14218
|
}
|
|
13268
14219
|
};
|
|
13269
14220
|
const toggleOpen = () => {
|
|
14221
|
+
if (!open) collapseSidebar();
|
|
13270
14222
|
setOpen((value) => !value);
|
|
13271
14223
|
};
|
|
13272
14224
|
(0, react.useEffect)(() => {
|
|
@@ -13293,6 +14245,8 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
13293
14245
|
}
|
|
13294
14246
|
}, entry.id);
|
|
13295
14247
|
const selectedFile = files.find((file) => file.id === selected);
|
|
14248
|
+
/** The file whose removal is being confirmed, if any. */
|
|
14249
|
+
const confirmFile = confirmDismiss === null ? void 0 : files.find((file) => file.id === confirmDismiss);
|
|
13296
14250
|
const fileListBody = (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)("div", {
|
|
13297
14251
|
className: PendingPanel_module_css_default.listScroll,
|
|
13298
14252
|
children: files.length > 0 && (0, react_jsx_runtime.jsxs)("section", { children: [(0, react_jsx_runtime.jsx)("h3", {
|
|
@@ -13360,6 +14314,58 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
13360
14314
|
handleUndo,
|
|
13361
14315
|
handleRedo
|
|
13362
14316
|
]);
|
|
14317
|
+
(0, react.useEffect)(() => {
|
|
14318
|
+
if (!open || current === void 0) return;
|
|
14319
|
+
const onKeyDown = (event) => {
|
|
14320
|
+
if (!(event.ctrlKey || event.metaKey) || event.altKey) return;
|
|
14321
|
+
if (event.key.toLowerCase() !== "tab") return;
|
|
14322
|
+
const target = event.target;
|
|
14323
|
+
if (target instanceof Element && target.closest("input, textarea, [contenteditable=\"true\"]") !== null) return;
|
|
14324
|
+
if (files.length === 0) return;
|
|
14325
|
+
event.preventDefault();
|
|
14326
|
+
const index = files.findIndex((file) => file.id === selected);
|
|
14327
|
+
const direction = event.shiftKey ? -1 : 1;
|
|
14328
|
+
const next = files[(index + direction + files.length) % files.length];
|
|
14329
|
+
if (next !== void 0) setSelected(next.id);
|
|
14330
|
+
};
|
|
14331
|
+
window.addEventListener("keydown", onKeyDown, true);
|
|
14332
|
+
return () => {
|
|
14333
|
+
window.removeEventListener("keydown", onKeyDown, true);
|
|
14334
|
+
};
|
|
14335
|
+
}, [
|
|
14336
|
+
open,
|
|
14337
|
+
current,
|
|
14338
|
+
files,
|
|
14339
|
+
selected
|
|
14340
|
+
]);
|
|
14341
|
+
(0, react.useEffect)(() => {
|
|
14342
|
+
const onKeyDown = (event) => {
|
|
14343
|
+
if (!matchesShortcut(event, quickSummonKey())) return;
|
|
14344
|
+
const target = event.target;
|
|
14345
|
+
if (target instanceof Element && target.closest("input, textarea, [contenteditable=\"true\"]") !== null) return;
|
|
14346
|
+
event.preventDefault();
|
|
14347
|
+
if (!open) collapseSidebar();
|
|
14348
|
+
setOpen((value) => !value);
|
|
14349
|
+
};
|
|
14350
|
+
window.addEventListener("keydown", onKeyDown, true);
|
|
14351
|
+
return () => {
|
|
14352
|
+
window.removeEventListener("keydown", onKeyDown, true);
|
|
14353
|
+
};
|
|
14354
|
+
}, [open, collapseSidebar]);
|
|
14355
|
+
(0, react.useEffect)(() => {
|
|
14356
|
+
if (!open) return;
|
|
14357
|
+
const onKeyDown = (event) => {
|
|
14358
|
+
if (event.key !== "Escape") return;
|
|
14359
|
+
const target = event.target;
|
|
14360
|
+
if (target instanceof Element && target.closest("input, textarea, [contenteditable=\"true\"]") !== null) return;
|
|
14361
|
+
event.preventDefault();
|
|
14362
|
+
setOpen(false);
|
|
14363
|
+
};
|
|
14364
|
+
window.addEventListener("keydown", onKeyDown, true);
|
|
14365
|
+
return () => {
|
|
14366
|
+
window.removeEventListener("keydown", onKeyDown, true);
|
|
14367
|
+
};
|
|
14368
|
+
}, [open]);
|
|
13363
14369
|
/** Drag the list/detail divider; width follows the pointer within its bounds. */
|
|
13364
14370
|
const startResize = (event) => {
|
|
13365
14371
|
if (event.button !== 0) return;
|
|
@@ -13401,162 +14407,207 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
13401
14407
|
setActionToast(null);
|
|
13402
14408
|
}
|
|
13403
14409
|
}),
|
|
13404
|
-
|
|
14410
|
+
copyToast !== null && (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Toast, {
|
|
14411
|
+
text: copyToast,
|
|
14412
|
+
onDone: () => {
|
|
14413
|
+
setCopyToast(null);
|
|
14414
|
+
}
|
|
14415
|
+
}),
|
|
14416
|
+
open && (0, react_dom.createPortal)((0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [expanded && (0, react_jsx_runtime.jsx)("div", {
|
|
13405
14417
|
className: PendingPanel_module_css_default.fullscreenBackdrop,
|
|
13406
14418
|
"data-diff-fullscreen-backdrop": true
|
|
13407
|
-
}),
|
|
13408
|
-
open && (0, react_jsx_runtime.jsxs)("section", {
|
|
14419
|
+
}), (0, react_jsx_runtime.jsxs)("section", {
|
|
13409
14420
|
className: PendingPanel_module_css_default.panel,
|
|
13410
14421
|
ref: panelRef,
|
|
13411
14422
|
style: { bottom: expanded ? PANEL_INSET_PX : bottomPx },
|
|
13412
14423
|
"data-diff-approval-panel": true,
|
|
13413
14424
|
"aria-label": t("panel.title"),
|
|
13414
|
-
children: [
|
|
13415
|
-
|
|
13416
|
-
|
|
13417
|
-
|
|
13418
|
-
|
|
13419
|
-
|
|
13420
|
-
|
|
13421
|
-
|
|
13422
|
-
|
|
13423
|
-
|
|
13424
|
-
|
|
13425
|
-
|
|
13426
|
-
|
|
13427
|
-
|
|
13428
|
-
|
|
13429
|
-
|
|
13430
|
-
|
|
13431
|
-
|
|
13432
|
-
|
|
13433
|
-
|
|
13434
|
-
|
|
13435
|
-
|
|
14425
|
+
children: [
|
|
14426
|
+
(0, react_jsx_runtime.jsxs)("header", {
|
|
14427
|
+
className: PendingPanel_module_css_default.header,
|
|
14428
|
+
children: [(0, react_jsx_runtime.jsx)("span", {
|
|
14429
|
+
className: PendingPanel_module_css_default.title,
|
|
14430
|
+
children: t("panel.title")
|
|
14431
|
+
}), (0, react_jsx_runtime.jsxs)("div", {
|
|
14432
|
+
className: PendingPanel_module_css_default.headerActions,
|
|
14433
|
+
children: [
|
|
14434
|
+
(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
14435
|
+
label: t("action.settings"),
|
|
14436
|
+
side: "bottom",
|
|
14437
|
+
delayMs: 500,
|
|
14438
|
+
children: (0, react_jsx_runtime.jsx)("button", {
|
|
14439
|
+
type: "button",
|
|
14440
|
+
className: PendingPanel_module_css_default.expand,
|
|
14441
|
+
"data-diff-approval-settings": true,
|
|
14442
|
+
"aria-label": t("action.settings"),
|
|
14443
|
+
onClick: () => {
|
|
14444
|
+
setOpen(false);
|
|
14445
|
+
openSettingsSection(t("settings.tabLabel"));
|
|
14446
|
+
},
|
|
14447
|
+
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconSettingsOutline16, { size: 14 })
|
|
14448
|
+
})
|
|
14449
|
+
}),
|
|
14450
|
+
(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
14451
|
+
label: t(expanded ? "action.exitFullscreen" : "action.expand"),
|
|
14452
|
+
side: "bottom",
|
|
14453
|
+
delayMs: 500,
|
|
14454
|
+
children: (0, react_jsx_runtime.jsx)("button", {
|
|
14455
|
+
type: "button",
|
|
14456
|
+
className: expanded ? `${PendingPanel_module_css_default.expand} ${PendingPanel_module_css_default.expandExpanded}` : PendingPanel_module_css_default.expand,
|
|
14457
|
+
"data-diff-approval-expand": true,
|
|
14458
|
+
"aria-label": t(expanded ? "action.exitFullscreen" : "action.expand"),
|
|
14459
|
+
onClick: () => {
|
|
14460
|
+
if (!expanded) collapseSidebar();
|
|
14461
|
+
setExpanded((value) => !value);
|
|
14462
|
+
},
|
|
14463
|
+
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconFullscreenOutline16, { size: 14 })
|
|
14464
|
+
})
|
|
14465
|
+
}),
|
|
14466
|
+
(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
14467
|
+
label: t("action.close"),
|
|
14468
|
+
side: "bottom",
|
|
14469
|
+
delayMs: 500,
|
|
14470
|
+
children: (0, react_jsx_runtime.jsx)("button", {
|
|
14471
|
+
type: "button",
|
|
14472
|
+
className: PendingPanel_module_css_default.close,
|
|
14473
|
+
"data-diff-approval-close": true,
|
|
14474
|
+
"aria-label": t("action.close"),
|
|
14475
|
+
onClick: () => {
|
|
14476
|
+
setOpen(false);
|
|
14477
|
+
},
|
|
14478
|
+
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCloseOutline16, { size: 14 })
|
|
14479
|
+
})
|
|
13436
14480
|
})
|
|
14481
|
+
]
|
|
14482
|
+
})]
|
|
14483
|
+
}),
|
|
14484
|
+
snapshot.error !== void 0 || !snapshot.read || files.length === 0 ? (0, react_jsx_runtime.jsxs)("div", {
|
|
14485
|
+
className: PendingPanel_module_css_default.states,
|
|
14486
|
+
children: [
|
|
14487
|
+
snapshot.error !== void 0 && (0, react_jsx_runtime.jsx)("p", {
|
|
14488
|
+
className: PendingPanel_module_css_default.readError,
|
|
14489
|
+
role: "alert",
|
|
14490
|
+
children: t("panel.readFailed", { message: snapshot.error })
|
|
13437
14491
|
}),
|
|
13438
|
-
(0, react_jsx_runtime.jsx)(
|
|
13439
|
-
|
|
13440
|
-
|
|
13441
|
-
|
|
13442
|
-
|
|
13443
|
-
|
|
13444
|
-
|
|
13445
|
-
"
|
|
13446
|
-
|
|
13447
|
-
|
|
13448
|
-
|
|
14492
|
+
!snapshot.read && snapshot.error === void 0 && (0, react_jsx_runtime.jsx)("p", {
|
|
14493
|
+
className: PendingPanel_module_css_default.note,
|
|
14494
|
+
children: t("panel.loading")
|
|
14495
|
+
}),
|
|
14496
|
+
snapshot.read && snapshot.error === void 0 && files.length === 0 && (0, react_jsx_runtime.jsxs)("div", {
|
|
14497
|
+
className: PendingPanel_module_css_default.emptyState,
|
|
14498
|
+
children: [
|
|
14499
|
+
(0, react_jsx_runtime.jsx)("p", {
|
|
14500
|
+
className: `${PendingPanel_module_css_default.note} ${PendingPanel_module_css_default.noteCentered}`,
|
|
14501
|
+
children: t("panel.empty")
|
|
14502
|
+
}),
|
|
14503
|
+
(0, react_jsx_runtime.jsx)("button", {
|
|
14504
|
+
type: "button",
|
|
14505
|
+
className: PendingPanel_module_css_default.importButton,
|
|
14506
|
+
"data-diff-import-vcs": true,
|
|
14507
|
+
disabled: importBusy,
|
|
14508
|
+
onClick: () => {
|
|
14509
|
+
runImportVcs();
|
|
14510
|
+
},
|
|
14511
|
+
children: importBusy ? t("action.importVcsBusy") : t("action.importVcs")
|
|
14512
|
+
}),
|
|
14513
|
+
importNote !== void 0 && (0, react_jsx_runtime.jsx)("p", {
|
|
14514
|
+
className: PendingPanel_module_css_default.importNote,
|
|
14515
|
+
role: importFailed ? "alert" : void 0,
|
|
14516
|
+
children: importNote
|
|
14517
|
+
})
|
|
14518
|
+
]
|
|
14519
|
+
})
|
|
14520
|
+
]
|
|
14521
|
+
}) : (0, react_jsx_runtime.jsxs)("div", {
|
|
14522
|
+
className: PendingPanel_module_css_default.split,
|
|
14523
|
+
ref: splitRef,
|
|
14524
|
+
children: [
|
|
14525
|
+
!floatMode && (0, react_jsx_runtime.jsx)("nav", {
|
|
14526
|
+
className: PendingPanel_module_css_default.fileList,
|
|
14527
|
+
style: { width: listWidth },
|
|
14528
|
+
"data-diff-approval-file-list": true,
|
|
14529
|
+
children: fileListBody
|
|
14530
|
+
}),
|
|
14531
|
+
!floatMode && (0, react_jsx_runtime.jsx)("div", {
|
|
14532
|
+
className: PendingPanel_module_css_default.resizeHandle,
|
|
14533
|
+
"data-diff-resize": true,
|
|
14534
|
+
onMouseDown: startResize
|
|
14535
|
+
}),
|
|
14536
|
+
(0, react_jsx_runtime.jsx)("div", {
|
|
14537
|
+
className: PendingPanel_module_css_default.detail,
|
|
14538
|
+
children: selectedFile === void 0 ? (0, react_jsx_runtime.jsx)("p", {
|
|
14539
|
+
className: PendingPanel_module_css_default.detailEmpty,
|
|
14540
|
+
children: t("panel.selectHint")
|
|
14541
|
+
}) : (0, react_jsx_runtime.jsx)(PendingDiff, {
|
|
14542
|
+
file: selectedFile,
|
|
14543
|
+
busy: snapshot.busy.has(selectedFile.id),
|
|
14544
|
+
workspacePath: snapshot.workspacePath,
|
|
14545
|
+
jumpSignal,
|
|
14546
|
+
undoFlash,
|
|
14547
|
+
failedMessage: failed.get(selectedFile.id),
|
|
14548
|
+
onPasteReference,
|
|
14549
|
+
onToast: (text) => {
|
|
14550
|
+
setCopyToast(text);
|
|
13449
14551
|
},
|
|
13450
|
-
|
|
14552
|
+
t,
|
|
14553
|
+
onKeep,
|
|
14554
|
+
onRevert,
|
|
14555
|
+
onBlockKeep,
|
|
14556
|
+
onBlockRevert,
|
|
14557
|
+
onOpen,
|
|
14558
|
+
floatMode,
|
|
14559
|
+
floatOpen,
|
|
14560
|
+
onToggleFileList: toggleFileList
|
|
13451
14561
|
})
|
|
13452
14562
|
}),
|
|
13453
|
-
(0, react_jsx_runtime.jsx)(
|
|
13454
|
-
|
|
13455
|
-
|
|
13456
|
-
|
|
13457
|
-
|
|
14563
|
+
floatMode && floatOpen && files.length > 0 && floatBox !== null && (0, react_jsx_runtime.jsx)("div", {
|
|
14564
|
+
className: PendingPanel_module_css_default.fileListFloat,
|
|
14565
|
+
style: {
|
|
14566
|
+
left: floatBox.left + FLOAT_LIST_MARGIN_PX,
|
|
14567
|
+
top: floatBox.top + FLOAT_LIST_MARGIN_PX,
|
|
14568
|
+
width: Math.min(listWidth, Math.max(0, floatBox.width - 24)),
|
|
14569
|
+
height: Math.max(0, floatBox.height - 24)
|
|
14570
|
+
},
|
|
14571
|
+
"data-diff-floating-file-list": true,
|
|
14572
|
+
children: fileListBody
|
|
14573
|
+
})
|
|
14574
|
+
]
|
|
14575
|
+
}),
|
|
14576
|
+
confirmFile !== void 0 && (0, react_jsx_runtime.jsx)("div", {
|
|
14577
|
+
className: PendingPanel_module_css_default.confirmBackdrop,
|
|
14578
|
+
"data-diff-confirm": true,
|
|
14579
|
+
children: (0, react_jsx_runtime.jsxs)("div", {
|
|
14580
|
+
className: PendingPanel_module_css_default.confirmCard,
|
|
14581
|
+
role: "dialog",
|
|
14582
|
+
"aria-modal": "true",
|
|
14583
|
+
children: [(0, react_jsx_runtime.jsx)("p", {
|
|
14584
|
+
className: PendingPanel_module_css_default.confirmText,
|
|
14585
|
+
children: t("panel.resolvedAsk", { file: basenameOf(confirmFile.path) })
|
|
14586
|
+
}), (0, react_jsx_runtime.jsxs)("div", {
|
|
14587
|
+
className: PendingPanel_module_css_default.confirmActions,
|
|
14588
|
+
children: [(0, react_jsx_runtime.jsx)("button", {
|
|
13458
14589
|
type: "button",
|
|
13459
|
-
className: PendingPanel_module_css_default.
|
|
13460
|
-
"data-diff-
|
|
13461
|
-
"aria-label": t("action.close"),
|
|
14590
|
+
className: `${PendingPanel_module_css_default.action} ${PendingPanel_module_css_default.actionPrimary}`,
|
|
14591
|
+
"data-diff-confirm-remove": true,
|
|
13462
14592
|
onClick: () => {
|
|
13463
|
-
|
|
14593
|
+
setConfirmDismiss(null);
|
|
14594
|
+
onKeep(confirmFile.sessionId, confirmFile.id);
|
|
13464
14595
|
},
|
|
13465
|
-
children: (
|
|
13466
|
-
})
|
|
13467
|
-
})
|
|
13468
|
-
]
|
|
13469
|
-
})]
|
|
13470
|
-
}), snapshot.error !== void 0 || !snapshot.read || files.length === 0 ? (0, react_jsx_runtime.jsxs)("div", {
|
|
13471
|
-
className: PendingPanel_module_css_default.states,
|
|
13472
|
-
children: [
|
|
13473
|
-
snapshot.error !== void 0 && (0, react_jsx_runtime.jsx)("p", {
|
|
13474
|
-
className: PendingPanel_module_css_default.readError,
|
|
13475
|
-
role: "alert",
|
|
13476
|
-
children: t("panel.readFailed", { message: snapshot.error })
|
|
13477
|
-
}),
|
|
13478
|
-
!snapshot.read && snapshot.error === void 0 && (0, react_jsx_runtime.jsx)("p", {
|
|
13479
|
-
className: PendingPanel_module_css_default.note,
|
|
13480
|
-
children: t("panel.loading")
|
|
13481
|
-
}),
|
|
13482
|
-
snapshot.read && snapshot.error === void 0 && files.length === 0 && (0, react_jsx_runtime.jsxs)("div", {
|
|
13483
|
-
className: PendingPanel_module_css_default.emptyState,
|
|
13484
|
-
children: [
|
|
13485
|
-
(0, react_jsx_runtime.jsx)("p", {
|
|
13486
|
-
className: `${PendingPanel_module_css_default.note} ${PendingPanel_module_css_default.noteCentered}`,
|
|
13487
|
-
children: t("panel.empty")
|
|
13488
|
-
}),
|
|
13489
|
-
(0, react_jsx_runtime.jsx)("button", {
|
|
14596
|
+
children: t("row.dismiss")
|
|
14597
|
+
}), (0, react_jsx_runtime.jsx)("button", {
|
|
13490
14598
|
type: "button",
|
|
13491
|
-
className: PendingPanel_module_css_default.
|
|
13492
|
-
"data-diff-
|
|
13493
|
-
disabled: importBusy,
|
|
14599
|
+
className: PendingPanel_module_css_default.action,
|
|
14600
|
+
"data-diff-confirm-keep": true,
|
|
13494
14601
|
onClick: () => {
|
|
13495
|
-
|
|
14602
|
+
setConfirmDismiss(null);
|
|
13496
14603
|
},
|
|
13497
|
-
children:
|
|
13498
|
-
})
|
|
13499
|
-
|
|
13500
|
-
className: PendingPanel_module_css_default.importNote,
|
|
13501
|
-
role: importFailed ? "alert" : void 0,
|
|
13502
|
-
children: importNote
|
|
13503
|
-
})
|
|
13504
|
-
]
|
|
14604
|
+
children: t("panel.keepInList")
|
|
14605
|
+
})]
|
|
14606
|
+
})]
|
|
13505
14607
|
})
|
|
13506
|
-
|
|
13507
|
-
|
|
13508
|
-
|
|
13509
|
-
ref: splitRef,
|
|
13510
|
-
children: [
|
|
13511
|
-
!floatMode && (0, react_jsx_runtime.jsx)("nav", {
|
|
13512
|
-
className: PendingPanel_module_css_default.fileList,
|
|
13513
|
-
style: { width: listWidth },
|
|
13514
|
-
"data-diff-approval-file-list": true,
|
|
13515
|
-
children: fileListBody
|
|
13516
|
-
}),
|
|
13517
|
-
!floatMode && (0, react_jsx_runtime.jsx)("div", {
|
|
13518
|
-
className: PendingPanel_module_css_default.resizeHandle,
|
|
13519
|
-
"data-diff-resize": true,
|
|
13520
|
-
onMouseDown: startResize
|
|
13521
|
-
}),
|
|
13522
|
-
(0, react_jsx_runtime.jsx)("div", {
|
|
13523
|
-
className: PendingPanel_module_css_default.detail,
|
|
13524
|
-
children: selectedFile === void 0 ? (0, react_jsx_runtime.jsx)("p", {
|
|
13525
|
-
className: PendingPanel_module_css_default.detailEmpty,
|
|
13526
|
-
children: t("panel.selectHint")
|
|
13527
|
-
}) : (0, react_jsx_runtime.jsx)(PendingDiff, {
|
|
13528
|
-
file: selectedFile,
|
|
13529
|
-
busy: snapshot.busy.has(selectedFile.id),
|
|
13530
|
-
workspacePath: snapshot.workspacePath,
|
|
13531
|
-
jumpSignal,
|
|
13532
|
-
undoFlash,
|
|
13533
|
-
failedMessage: failed.get(selectedFile.id),
|
|
13534
|
-
onPasteReference,
|
|
13535
|
-
t,
|
|
13536
|
-
onKeep,
|
|
13537
|
-
onRevert,
|
|
13538
|
-
onBlockKeep,
|
|
13539
|
-
onBlockRevert,
|
|
13540
|
-
onOpen,
|
|
13541
|
-
floatMode,
|
|
13542
|
-
floatOpen,
|
|
13543
|
-
onToggleFileList: toggleFileList
|
|
13544
|
-
})
|
|
13545
|
-
}),
|
|
13546
|
-
floatMode && floatOpen && files.length > 0 && floatBox !== null && (0, react_jsx_runtime.jsx)("div", {
|
|
13547
|
-
className: PendingPanel_module_css_default.fileListFloat,
|
|
13548
|
-
style: {
|
|
13549
|
-
left: floatBox.left + FLOAT_LIST_MARGIN_PX,
|
|
13550
|
-
top: floatBox.top + FLOAT_LIST_MARGIN_PX,
|
|
13551
|
-
width: Math.min(listWidth, Math.max(0, floatBox.width - 24)),
|
|
13552
|
-
height: Math.max(0, floatBox.height - 24)
|
|
13553
|
-
},
|
|
13554
|
-
"data-diff-floating-file-list": true,
|
|
13555
|
-
children: fileListBody
|
|
13556
|
-
})
|
|
13557
|
-
]
|
|
13558
|
-
})]
|
|
13559
|
-
}),
|
|
14608
|
+
})
|
|
14609
|
+
]
|
|
14610
|
+
})] }), document.body),
|
|
13560
14611
|
(0, react_jsx_runtime.jsx)("div", {
|
|
13561
14612
|
className: PendingPanel_module_css_default.footerButtons,
|
|
13562
14613
|
children: (0, react_jsx_runtime.jsxs)("button", {
|
|
@@ -13719,6 +14770,69 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
13719
14770
|
})]
|
|
13720
14771
|
});
|
|
13721
14772
|
}
|
|
14773
|
+
/** Build the `Modifier+...+Key` chord label from a keydown event; a bare
|
|
14774
|
+
* modifier key alone returns undefined (wait for the full combo). */
|
|
14775
|
+
function chordLabel(event) {
|
|
14776
|
+
const key = event.key;
|
|
14777
|
+
if (key === "Control" || key === "Shift" || key === "Alt" || key === "Meta") return void 0;
|
|
14778
|
+
const parts = [];
|
|
14779
|
+
if (event.ctrlKey) parts.push("Ctrl");
|
|
14780
|
+
if (event.altKey) parts.push("Alt");
|
|
14781
|
+
if (event.shiftKey) parts.push("Shift");
|
|
14782
|
+
if (event.metaKey) parts.push("Meta");
|
|
14783
|
+
parts.push(key.length === 1 ? key.toUpperCase() : key);
|
|
14784
|
+
return parts.join("+");
|
|
14785
|
+
}
|
|
14786
|
+
/** A button that records the next key chord (modifiers + key) as the shortcut. */
|
|
14787
|
+
function ShortcutRecorder({ value, onChange, dataAttribute, placeholder }) {
|
|
14788
|
+
const [recording, setRecording] = (0, react.useState)(false);
|
|
14789
|
+
return (0, react_jsx_runtime.jsxs)("button", {
|
|
14790
|
+
type: "button",
|
|
14791
|
+
className: PendingPanel_module_css_default.settingsSelector,
|
|
14792
|
+
onClick: () => {
|
|
14793
|
+
setRecording(true);
|
|
14794
|
+
},
|
|
14795
|
+
onBlur: () => {
|
|
14796
|
+
setRecording(false);
|
|
14797
|
+
},
|
|
14798
|
+
onKeyDown: recording ? (event) => {
|
|
14799
|
+
event.preventDefault();
|
|
14800
|
+
event.stopPropagation();
|
|
14801
|
+
if (event.key === "Escape") {
|
|
14802
|
+
setRecording(false);
|
|
14803
|
+
return;
|
|
14804
|
+
}
|
|
14805
|
+
const chord = chordLabel(event);
|
|
14806
|
+
if (chord !== void 0) {
|
|
14807
|
+
onChange(chord);
|
|
14808
|
+
setRecording(false);
|
|
14809
|
+
}
|
|
14810
|
+
} : void 0,
|
|
14811
|
+
[dataAttribute]: true,
|
|
14812
|
+
children: [recording ? placeholder : value, (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, { className: PendingPanel_module_css_default.settingsSelectorChevron })]
|
|
14813
|
+
});
|
|
14814
|
+
}
|
|
14815
|
+
/** One shortcut row: title + description, chord recorder right. */
|
|
14816
|
+
function ShortcutRow({ title, description, value, onChange, dataAttribute, placeholder }) {
|
|
14817
|
+
return (0, react_jsx_runtime.jsxs)("div", {
|
|
14818
|
+
className: PendingPanel_module_css_default.settingsRow,
|
|
14819
|
+
children: [(0, react_jsx_runtime.jsxs)("div", {
|
|
14820
|
+
className: PendingPanel_module_css_default.settingsRowText,
|
|
14821
|
+
children: [(0, react_jsx_runtime.jsx)("div", {
|
|
14822
|
+
className: PendingPanel_module_css_default.settingsRowTitle,
|
|
14823
|
+
children: title
|
|
14824
|
+
}), (0, react_jsx_runtime.jsx)("div", {
|
|
14825
|
+
className: PendingPanel_module_css_default.settingsRowDesc,
|
|
14826
|
+
children: description
|
|
14827
|
+
})]
|
|
14828
|
+
}), (0, react_jsx_runtime.jsx)(ShortcutRecorder, {
|
|
14829
|
+
value,
|
|
14830
|
+
onChange,
|
|
14831
|
+
dataAttribute,
|
|
14832
|
+
placeholder
|
|
14833
|
+
})]
|
|
14834
|
+
});
|
|
14835
|
+
}
|
|
13722
14836
|
/**
|
|
13723
14837
|
* The plugin's preferences: auto-paste a copied reference into the input,
|
|
13724
14838
|
* whether importing workspace VCS changes includes untracked files, and the
|
|
@@ -13733,6 +14847,13 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
13733
14847
|
const [includeUntrackedOpen, setIncludeUntrackedOpen] = (0, react.useState)(false);
|
|
13734
14848
|
const [tab, setTabState] = (0, react.useState)(tabWidth);
|
|
13735
14849
|
const [tabOpen, setTabOpen] = (0, react.useState)(false);
|
|
14850
|
+
const [split, setSplitState] = (0, react.useState)(splitMode);
|
|
14851
|
+
const [splitOpen, setSplitOpen] = (0, react.useState)(false);
|
|
14852
|
+
const [summon, setSummonState] = (0, react.useState)(quickSummonKey);
|
|
14853
|
+
const setSummon = (value) => {
|
|
14854
|
+
setSummonState(value);
|
|
14855
|
+
setQuickSummonKey(value);
|
|
14856
|
+
};
|
|
13736
14857
|
const setPasteOnCopy = (value) => {
|
|
13737
14858
|
setPasteOnCopyState(value);
|
|
13738
14859
|
setPasteOnCopyEnabled(value);
|
|
@@ -13745,6 +14866,10 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
13745
14866
|
setTabState(value);
|
|
13746
14867
|
setTabWidth(value);
|
|
13747
14868
|
};
|
|
14869
|
+
const setSplit = (value) => {
|
|
14870
|
+
setSplitState(value);
|
|
14871
|
+
setSplitMode(value);
|
|
14872
|
+
};
|
|
13748
14873
|
return (0, react_jsx_runtime.jsxs)("div", {
|
|
13749
14874
|
className: PendingPanel_module_css_default.settingsPage,
|
|
13750
14875
|
"data-diff-settings": true,
|
|
@@ -13777,6 +14902,24 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
13777
14902
|
onOpenChange: setTabOpen,
|
|
13778
14903
|
onSelect: setTab,
|
|
13779
14904
|
dataAttribute: "data-diff-tab-width-select"
|
|
14905
|
+
}),
|
|
14906
|
+
(0, react_jsx_runtime.jsx)(PreferenceRow, {
|
|
14907
|
+
title: t("panel.splitMode"),
|
|
14908
|
+
description: t("panel.splitModeDesc"),
|
|
14909
|
+
value: split,
|
|
14910
|
+
open: splitOpen,
|
|
14911
|
+
onOpenChange: setSplitOpen,
|
|
14912
|
+
onSelect: setSplit,
|
|
14913
|
+
dataAttribute: "data-diff-split-mode-select",
|
|
14914
|
+
t
|
|
14915
|
+
}),
|
|
14916
|
+
(0, react_jsx_runtime.jsx)(ShortcutRow, {
|
|
14917
|
+
title: t("panel.quickSummon"),
|
|
14918
|
+
description: t("panel.quickSummonDesc"),
|
|
14919
|
+
value: summon,
|
|
14920
|
+
onChange: setSummon,
|
|
14921
|
+
dataAttribute: "data-diff-quick-summon-key",
|
|
14922
|
+
placeholder: t("panel.recordShortcut")
|
|
13780
14923
|
})
|
|
13781
14924
|
]
|
|
13782
14925
|
});
|
|
@@ -13900,9 +15043,14 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
13900
15043
|
const outcome = value.outcome;
|
|
13901
15044
|
if (outcome !== "kept" && outcome !== "reverted" && outcome !== "missing" && outcome !== "undone" && outcome !== "redone" && outcome !== "nothing") throw new Error("the action returned a malformed outcome");
|
|
13902
15045
|
const id = value.id;
|
|
13903
|
-
|
|
15046
|
+
const entryId = typeof id === "string" && id.length > 0 ? id : void 0;
|
|
15047
|
+
return value.resolved === true ? {
|
|
13904
15048
|
outcome,
|
|
13905
|
-
id:
|
|
15049
|
+
id: entryId,
|
|
15050
|
+
resolved: true
|
|
15051
|
+
} : {
|
|
15052
|
+
outcome,
|
|
15053
|
+
id: entryId
|
|
13906
15054
|
};
|
|
13907
15055
|
}
|
|
13908
15056
|
/** Narrow the vcs-import endpoint's value; a malformed wire value is a failure. */
|
|
@@ -13955,11 +15103,18 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
13955
15103
|
};
|
|
13956
15104
|
const listeners = /* @__PURE__ */ new Set();
|
|
13957
15105
|
let redoCleared = false;
|
|
15106
|
+
let justResolved;
|
|
13958
15107
|
const publish = (next) => {
|
|
13959
|
-
|
|
13960
|
-
|
|
15108
|
+
let result = next;
|
|
15109
|
+
if (redoCleared) result = {
|
|
15110
|
+
...result,
|
|
13961
15111
|
redoCleared: true
|
|
13962
|
-
}
|
|
15112
|
+
};
|
|
15113
|
+
if (justResolved !== void 0) result = {
|
|
15114
|
+
...result,
|
|
15115
|
+
justResolved
|
|
15116
|
+
};
|
|
15117
|
+
snapshot = result;
|
|
13963
15118
|
for (const listener of [...listeners]) listener();
|
|
13964
15119
|
};
|
|
13965
15120
|
const failedOf = (value) => value.failed ?? EMPTY_FAILED;
|
|
@@ -14068,8 +15223,9 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
14068
15223
|
...base,
|
|
14069
15224
|
busy: /* @__PURE__ */ new Set([...snapshot.busy, id])
|
|
14070
15225
|
});
|
|
15226
|
+
let value;
|
|
14071
15227
|
try {
|
|
14072
|
-
await port.blockKeep(sessionId, id, block);
|
|
15228
|
+
value = await port.blockKeep(sessionId, id, block);
|
|
14073
15229
|
} catch (error) {
|
|
14074
15230
|
markFailed(id, error instanceof Error ? error.message : String(error));
|
|
14075
15231
|
publish({
|
|
@@ -14079,6 +15235,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
14079
15235
|
return;
|
|
14080
15236
|
}
|
|
14081
15237
|
clearFailed(id);
|
|
15238
|
+
if (value.resolved === true) justResolved = id;
|
|
14082
15239
|
await this.refresh(sessionId);
|
|
14083
15240
|
},
|
|
14084
15241
|
async blockRevert(sessionId, id, block) {
|
|
@@ -14087,8 +15244,9 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
14087
15244
|
...base,
|
|
14088
15245
|
busy: /* @__PURE__ */ new Set([...snapshot.busy, id])
|
|
14089
15246
|
});
|
|
15247
|
+
let value;
|
|
14090
15248
|
try {
|
|
14091
|
-
await port.blockRevert(sessionId, id, block);
|
|
15249
|
+
value = await port.blockRevert(sessionId, id, block);
|
|
14092
15250
|
} catch (error) {
|
|
14093
15251
|
markFailed(id, error instanceof Error ? error.message : String(error));
|
|
14094
15252
|
publish({
|
|
@@ -14098,6 +15256,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
14098
15256
|
return;
|
|
14099
15257
|
}
|
|
14100
15258
|
clearFailed(id);
|
|
15259
|
+
if (value.resolved === true) justResolved = id;
|
|
14101
15260
|
await this.refresh(sessionId);
|
|
14102
15261
|
},
|
|
14103
15262
|
async undo(sessionId) {
|
|
@@ -14136,6 +15295,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
14136
15295
|
}
|
|
14137
15296
|
},
|
|
14138
15297
|
reset() {
|
|
15298
|
+
justResolved = void 0;
|
|
14139
15299
|
publish({
|
|
14140
15300
|
read: false,
|
|
14141
15301
|
files: [],
|
|
@@ -14146,6 +15306,131 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
14146
15306
|
redoCleared = false;
|
|
14147
15307
|
const { redoCleared: _omit, ...rest } = snapshot;
|
|
14148
15308
|
publish(rest);
|
|
15309
|
+
},
|
|
15310
|
+
clearJustResolved() {
|
|
15311
|
+
justResolved = void 0;
|
|
15312
|
+
const { justResolved: _omit, ...rest } = snapshot;
|
|
15313
|
+
publish(rest);
|
|
15314
|
+
}
|
|
15315
|
+
};
|
|
15316
|
+
}
|
|
15317
|
+
//#endregion
|
|
15318
|
+
//#region lib/types/client/remap-sync.js
|
|
15319
|
+
/**
|
|
15320
|
+
* Reference remapping sync: track each pending file's last-seen content and,
|
|
15321
|
+
* when it changes (an agent edit, a block revert, or an external adoption),
|
|
15322
|
+
* rewrite stale references in the current composer draft and the pending queue.
|
|
15323
|
+
* Pure orchestration over the pending store; the actual line remapping lives in
|
|
15324
|
+
* `reference.ts`.
|
|
15325
|
+
* @module dsh-diff-approval/client/remap-sync
|
|
15326
|
+
*/
|
|
15327
|
+
/**
|
|
15328
|
+
* Subscribe to the pending store and remap references as each file's content
|
|
15329
|
+
* changes. The first observation of a file only seeds the baseline; a later
|
|
15330
|
+
* `newText` change remaps (draft + queue) and advances the baseline. Files that
|
|
15331
|
+
* leave the list drop their baseline so a future re-appearance re-baselines.
|
|
15332
|
+
* @param opts - the sync inputs.
|
|
15333
|
+
* @returns an unsubscribe function plus a direct remap verb for whole-file reverts.
|
|
15334
|
+
*/
|
|
15335
|
+
function attachReferenceRemap(opts) {
|
|
15336
|
+
const { store, readDraft, writeDraft, readQueue, writeQueue } = opts;
|
|
15337
|
+
const lastContent = /* @__PURE__ */ new Map();
|
|
15338
|
+
const remap = (path, oldText, newText, workspacePath) => {
|
|
15339
|
+
const referencePath = referencePathOf(path, workspacePath);
|
|
15340
|
+
const draft = readDraft();
|
|
15341
|
+
if (draft !== void 0 && draft !== "") {
|
|
15342
|
+
const rewritten = remapReferences(draft, referencePath, oldText, newText);
|
|
15343
|
+
if (rewritten !== draft) writeDraft(rewritten);
|
|
15344
|
+
}
|
|
15345
|
+
for (const message of readQueue()) {
|
|
15346
|
+
let changed = false;
|
|
15347
|
+
const content = message.content.map((block) => {
|
|
15348
|
+
if (block.type !== "text" || block.text === void 0) return block;
|
|
15349
|
+
const rewritten = remapReferences(block.text, referencePath, oldText, newText);
|
|
15350
|
+
if (rewritten !== block.text) {
|
|
15351
|
+
changed = true;
|
|
15352
|
+
return {
|
|
15353
|
+
...block,
|
|
15354
|
+
text: rewritten
|
|
15355
|
+
};
|
|
15356
|
+
}
|
|
15357
|
+
return block;
|
|
15358
|
+
});
|
|
15359
|
+
if (changed) writeQueue(message.id, content);
|
|
15360
|
+
}
|
|
15361
|
+
};
|
|
15362
|
+
const observe = () => {
|
|
15363
|
+
const snapshot = store.getSnapshot();
|
|
15364
|
+
for (const file of snapshot.files) {
|
|
15365
|
+
const previous = lastContent.get(file.path);
|
|
15366
|
+
if (previous === void 0) lastContent.set(file.path, file.newText);
|
|
15367
|
+
else if (previous !== file.newText) {
|
|
15368
|
+
remap(file.path, previous, file.newText, snapshot.workspacePath);
|
|
15369
|
+
lastContent.set(file.path, file.newText);
|
|
15370
|
+
}
|
|
15371
|
+
}
|
|
15372
|
+
for (const path of [...lastContent.keys()]) if (!snapshot.files.some((file) => file.path === path)) lastContent.delete(path);
|
|
15373
|
+
};
|
|
15374
|
+
const unsubscribe = store.subscribe(observe);
|
|
15375
|
+
observe();
|
|
15376
|
+
return {
|
|
15377
|
+
unsubscribe,
|
|
15378
|
+
remapFile: (path, oldText, newText) => {
|
|
15379
|
+
remap(path, oldText, newText, store.getSnapshot().workspacePath);
|
|
15380
|
+
}
|
|
15381
|
+
};
|
|
15382
|
+
}
|
|
15383
|
+
//#endregion
|
|
15384
|
+
//#region lib/types/client/conversation-access.js
|
|
15385
|
+
/**
|
|
15386
|
+
* Scope-addressed access to the session's composer draft and pending queue.
|
|
15387
|
+
* Extracted from the plugin entry so the wiring (not just the remap math) is
|
|
15388
|
+
* unit-testable against a faithful fake of the DSH conversation service.
|
|
15389
|
+
* @module dsh-diff-approval/client/conversation-access
|
|
15390
|
+
*/
|
|
15391
|
+
/**
|
|
15392
|
+
* Resolve the current session's scoped conversation face. The scoped context
|
|
15393
|
+
* (`sessions.scope(id)`) carries no `conversation` inject of its own, so reading
|
|
15394
|
+
* it as a property (`actx.conversation`) trips Cordis's inject check and throws
|
|
15395
|
+
* `cannot get property "conversation" without inject`. `actx.get('conversation')`
|
|
15396
|
+
* bypasses that check yet still returns the traceable bound to `actx`, so its
|
|
15397
|
+
* verbs (e.g. `updateQueue`) resolve the right session scope.
|
|
15398
|
+
*/
|
|
15399
|
+
function resolveConversation(ctx, sessionId) {
|
|
15400
|
+
if (sessionId === void 0) return void 0;
|
|
15401
|
+
const actx = ctx.get("sessions")?.scope(sessionId);
|
|
15402
|
+
if (actx === void 0) return void 0;
|
|
15403
|
+
const conversation = actx.get("conversation");
|
|
15404
|
+
if (conversation?.input === void 0) return void 0;
|
|
15405
|
+
return {
|
|
15406
|
+
actx,
|
|
15407
|
+
conversation
|
|
15408
|
+
};
|
|
15409
|
+
}
|
|
15410
|
+
/**
|
|
15411
|
+
* Build the per-call-resolved draft/queue accessor. `sessionId` is a function
|
|
15412
|
+
* so the accessor always addresses the *current* session, even though it is
|
|
15413
|
+
* handed to the remap sync once.
|
|
15414
|
+
*/
|
|
15415
|
+
function conversationAccess(ctx, sessionId) {
|
|
15416
|
+
return {
|
|
15417
|
+
writeDraft: (text) => {
|
|
15418
|
+
const scoped = resolveConversation(ctx, sessionId());
|
|
15419
|
+
if (scoped === void 0) return;
|
|
15420
|
+
scoped.conversation.input.for(scoped.actx).setDraft(text);
|
|
15421
|
+
},
|
|
15422
|
+
readQueue: () => {
|
|
15423
|
+
const scoped = resolveConversation(ctx, sessionId());
|
|
15424
|
+
if (scoped === void 0) return [];
|
|
15425
|
+
return scoped.conversation.input.for(scoped.actx).state.getSnapshot().queue.filter((item) => item.placement === "queued");
|
|
15426
|
+
},
|
|
15427
|
+
writeQueue: (itemId, content) => {
|
|
15428
|
+
const scoped = resolveConversation(ctx, sessionId());
|
|
15429
|
+
if (scoped === void 0) return;
|
|
15430
|
+
scoped.conversation.updateQueue(itemId, {
|
|
15431
|
+
kind: "edit",
|
|
15432
|
+
content
|
|
15433
|
+
});
|
|
14149
15434
|
}
|
|
14150
15435
|
};
|
|
14151
15436
|
}
|
|
@@ -14175,11 +15460,17 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
14175
15460
|
"panel.pasteOnCopyDesc": "开启后,复制的引用会自动填入消息输入框,输入框会获得焦点。",
|
|
14176
15461
|
"panel.importUntracked": "导入未跟踪的文件改动",
|
|
14177
15462
|
"panel.importUntrackedDesc": "开启后,导入工作区改动会包含未跟踪/未版本化的新增文件(Git 未跟踪、SVN 未版本化、Perforce 未版本化)。收集改动需要扫描整个工作区,文件数量大时可能较慢。",
|
|
14178
|
-
"panel.tabWidth": "
|
|
14179
|
-
"panel.tabWidthDesc": "
|
|
15463
|
+
"panel.tabWidth": "制表符宽度",
|
|
15464
|
+
"panel.tabWidthDesc": "差异中制表符的缩进宽度,可选 2 / 4 / 8 个空格(默认 4),同时作用于行宽折行测量。",
|
|
15465
|
+
"panel.splitMode": "双栏对比",
|
|
15466
|
+
"panel.splitModeDesc": "开启后整文件差异用左「改前」|右「当前」双栏、逐行对齐的视图展示,默认关闭使用单栏(合并)视图。",
|
|
15467
|
+
"panel.quickSummon": "快速呼出",
|
|
15468
|
+
"panel.quickSummonDesc": "用键盘快捷键打开/关闭差异面板(默认 Ctrl+D)。点击右侧按钮后按下新的按键组合即可修改,Esc 取消。",
|
|
15469
|
+
"panel.recordShortcut": "按下快捷键…",
|
|
14180
15470
|
"settings.tabLabel": "改动审批",
|
|
14181
15471
|
"row.create": "新增文件",
|
|
14182
15472
|
"row.failed": "失败",
|
|
15473
|
+
"row.dismiss": "移除",
|
|
14183
15474
|
"row.added": "+{added}",
|
|
14184
15475
|
"row.removed": "-{removed}",
|
|
14185
15476
|
"action.keep": "保留",
|
|
@@ -14214,7 +15505,9 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
14214
15505
|
"action.close": "关闭",
|
|
14215
15506
|
"status.kept": "已保留",
|
|
14216
15507
|
"status.reverted": "已回退",
|
|
14217
|
-
"status.missing": "该改动已不存在"
|
|
15508
|
+
"status.missing": "该改动已不存在",
|
|
15509
|
+
"panel.resolvedAsk": "「{file}」的所有改动都已处理,是否从列表移除?",
|
|
15510
|
+
"panel.keepInList": "保留在列表"
|
|
14218
15511
|
};
|
|
14219
15512
|
/** English pending-edit review messages. */
|
|
14220
15513
|
const en = {
|
|
@@ -14239,10 +15532,16 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
14239
15532
|
"panel.importUntracked": "Import changes to untracked files",
|
|
14240
15533
|
"panel.importUntrackedDesc": "When enabled, importing workspace changes includes new/untracked files (Git-untracked, SVN-unversioned, Perforce-unversioned). Collecting changes scans the whole workspace, which can be slow on large trees.",
|
|
14241
15534
|
"panel.tabWidth": "Tab width",
|
|
14242
|
-
"panel.tabWidthDesc": "The indent width of a tab character in the
|
|
15535
|
+
"panel.tabWidthDesc": "The indent width of a tab character in the differences, as 2 / 4 / 8 spaces (default 4). Also drives the wrapped-line measurement.",
|
|
15536
|
+
"panel.splitMode": "Side-by-side view",
|
|
15537
|
+
"panel.splitModeDesc": "When on, the whole-file differences render as a side-by-side (left \"before\" | right \"current\") line-aligned view. Off (default) uses the single-column unified view.",
|
|
15538
|
+
"panel.quickSummon": "Quick summon",
|
|
15539
|
+
"panel.quickSummonDesc": "Open or close the diff panel with a keyboard shortcut (default Ctrl+D). Click the button and press a new chord to change it; Esc cancels.",
|
|
15540
|
+
"panel.recordShortcut": "Press keys…",
|
|
14243
15541
|
"settings.tabLabel": "Diff Approval",
|
|
14244
15542
|
"row.create": "New file",
|
|
14245
15543
|
"row.failed": "Failed",
|
|
15544
|
+
"row.dismiss": "Remove",
|
|
14246
15545
|
"row.added": "+{added}",
|
|
14247
15546
|
"row.removed": "-{removed}",
|
|
14248
15547
|
"action.keep": "Keep",
|
|
@@ -14277,17 +15576,21 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
14277
15576
|
"action.close": "Close",
|
|
14278
15577
|
"status.kept": "Kept",
|
|
14279
15578
|
"status.reverted": "Reverted",
|
|
14280
|
-
"status.missing": "This change no longer exists"
|
|
15579
|
+
"status.missing": "This change no longer exists",
|
|
15580
|
+
"panel.resolvedAsk": "All changes in \"{file}\" are resolved. Remove it from the list?",
|
|
15581
|
+
"panel.keepInList": "Keep in list"
|
|
14281
15582
|
};
|
|
14282
15583
|
//#endregion
|
|
14283
15584
|
//#region lib/types/client/index.js
|
|
14284
15585
|
/** Pending-edit review panel, browser half: footer action, pending list, and whole-file diff viewer. */
|
|
14285
|
-
/** Required services: locale, slots, the wire channel,
|
|
15586
|
+
/** Required services: locale, slots, the wire channel, the current session, and
|
|
15587
|
+
* the layout controller (this plugin collapses the sidebar before its modal opens). */
|
|
14286
15588
|
const inject = [
|
|
14287
15589
|
"slots",
|
|
14288
15590
|
"locale",
|
|
14289
15591
|
"connection",
|
|
14290
|
-
"sessions"
|
|
15592
|
+
"sessions",
|
|
15593
|
+
"layout"
|
|
14291
15594
|
];
|
|
14292
15595
|
/**
|
|
14293
15596
|
* The dsh web-react renderer gives `div[data-slot="sidebar.footer.action"]` an
|
|
@@ -14354,6 +15657,29 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
14354
15657
|
}), "ui-diff-approval: dictionaries");
|
|
14355
15658
|
const t = ctx.locale.bind(NS);
|
|
14356
15659
|
const store = createPendingDiffStore(createDiffApprovalPort(ctx.get("connection").rpc));
|
|
15660
|
+
let currentSessionId;
|
|
15661
|
+
let remapFile = () => {};
|
|
15662
|
+
const access = conversationAccess(ctx, () => currentSessionId);
|
|
15663
|
+
ctx.effect(() => {
|
|
15664
|
+
const attached = attachReferenceRemap({
|
|
15665
|
+
store,
|
|
15666
|
+
readDraft: () => document.querySelector("[data-composer-card] textarea")?.value,
|
|
15667
|
+
writeDraft: access.writeDraft,
|
|
15668
|
+
readQueue: access.readQueue,
|
|
15669
|
+
writeQueue: access.writeQueue
|
|
15670
|
+
});
|
|
15671
|
+
remapFile = attached.remapFile;
|
|
15672
|
+
return attached.unsubscribe;
|
|
15673
|
+
}, "diff-approval: reference remap");
|
|
15674
|
+
const collapseSidebar = () => {
|
|
15675
|
+
if (window.innerWidth >= 1024) return;
|
|
15676
|
+
const ctxLayout = ctx.layout;
|
|
15677
|
+
if (ctxLayout === void 0) return;
|
|
15678
|
+
if (document.querySelector("[data-sidebar-collapsed]") !== null) return;
|
|
15679
|
+
try {
|
|
15680
|
+
ctxLayout.toggleSidebar();
|
|
15681
|
+
} catch {}
|
|
15682
|
+
};
|
|
14357
15683
|
ctx.on("connection/reset", () => {
|
|
14358
15684
|
store.reset();
|
|
14359
15685
|
});
|
|
@@ -14364,10 +15690,19 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
14364
15690
|
inject: () => ({
|
|
14365
15691
|
hooks: { pending: store },
|
|
14366
15692
|
onRefresh: (sessionId) => {
|
|
15693
|
+
currentSessionId = sessionId;
|
|
14367
15694
|
store.refresh(sessionId);
|
|
14368
15695
|
},
|
|
14369
15696
|
onKeep: (sessionId, path) => store.keep(sessionId, path),
|
|
14370
|
-
onRevert: (sessionId, path) =>
|
|
15697
|
+
onRevert: (sessionId, path) => {
|
|
15698
|
+
const entry = store.getSnapshot().files.find((file) => file.id === path);
|
|
15699
|
+
const before = entry?.newText;
|
|
15700
|
+
const after = entry?.oldText;
|
|
15701
|
+
const filePath = entry?.path;
|
|
15702
|
+
return store.revert(sessionId, path).then(() => {
|
|
15703
|
+
if (filePath !== void 0 && before !== void 0 && after !== void 0) remapFile(filePath, before, after);
|
|
15704
|
+
});
|
|
15705
|
+
},
|
|
14371
15706
|
onBlockKeep: (sessionId, id, block) => store.blockKeep(sessionId, id, block),
|
|
14372
15707
|
onBlockRevert: (sessionId, id, block) => store.blockRevert(sessionId, id, block),
|
|
14373
15708
|
onOpen: (sessionId, id, action) => store.open(sessionId, id, action),
|
|
@@ -14375,6 +15710,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
14375
15710
|
onRedo: (sessionId) => store.redo(sessionId),
|
|
14376
15711
|
onImportVcs: (sessionId, includeUntracked) => store.importVcs(sessionId, includeUntracked),
|
|
14377
15712
|
onAckRedoCleared: () => store.clearRedoCleared(),
|
|
15713
|
+
onAckJustResolved: () => store.clearJustResolved(),
|
|
14378
15714
|
onPasteReference: (sessionId, reference) => {
|
|
14379
15715
|
const actx = ctx.get("sessions")?.scope(sessionId);
|
|
14380
15716
|
if (actx === void 0) return;
|
|
@@ -14384,7 +15720,8 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
14384
15720
|
const base = textarea?.value ?? "";
|
|
14385
15721
|
conversation.input.for(actx).setDraft(base === "" ? reference : `${base} ${reference}`);
|
|
14386
15722
|
textarea?.focus();
|
|
14387
|
-
}
|
|
15723
|
+
},
|
|
15724
|
+
collapseSidebar
|
|
14388
15725
|
})
|
|
14389
15726
|
}, PendingPanel));
|
|
14390
15727
|
ctx.slots.inject("settings.section", () => ctx.slots.register({
|