dsh-diff-approval 0.10.0 → 0.12.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/lib/client.js +930 -95
- package/lib/index.js +39 -8
- package/lib/types/client/PendingPanel.d.ts +42 -0
- package/lib/types/client/locales.d.ts +4 -0
- package/lib/types/client/settings.d.ts +9 -0
- package/lib/types/client/split-diff.d.ts +51 -0
- package/lib/types/client/whole-file-diff.d.ts +4 -1
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -643,15 +643,21 @@ window.__ModuleLoader__.load({
|
|
|
643
643
|
* Compute the whole-file view between two contents. The context budget is the
|
|
644
644
|
* side lengths, so every hunk covers the file and unchanged lines survive as
|
|
645
645
|
* context rows; the `` patch marker is annotation
|
|
646
|
-
* and never becomes a row.
|
|
646
|
+
* and never becomes a row. Both sides are line-ending normalized (`\r\n?` →
|
|
647
|
+
* `\n`) first, so a repo baseline stored with one EOL and a worktree with
|
|
648
|
+
* another never show as a whole-file delete+add — the diff is about approved
|
|
649
|
+
* content, not line-ending noise.
|
|
647
650
|
* @param oldText - the file content before the pending change.
|
|
648
651
|
* @param newText - the file content after the pending change.
|
|
649
652
|
* @returns the complete row list with totals.
|
|
650
653
|
*/
|
|
651
654
|
function computeWholeFileDiff(oldText, newText) {
|
|
655
|
+
oldText = oldText.replace(/\r\n?/g, "\n");
|
|
656
|
+
newText = newText.replace(/\r\n?/g, "\n");
|
|
652
657
|
const oldLines = contentLines(oldText);
|
|
653
658
|
const newLines = contentLines(newText);
|
|
654
|
-
const
|
|
659
|
+
const context = Math.max(1, oldLines.length, newLines.length);
|
|
660
|
+
const patch = structuredPatch("", "", oldText, newText, void 0, void 0, { context });
|
|
655
661
|
const rows = [];
|
|
656
662
|
let removed = 0;
|
|
657
663
|
let added = 0;
|
|
@@ -706,6 +712,111 @@ window.__ModuleLoader__.load({
|
|
|
706
712
|
return (text.endsWith("\n") ? text.slice(0, -1) : text).split("\n");
|
|
707
713
|
}
|
|
708
714
|
//#endregion
|
|
715
|
+
//#region lib/types/client/split-diff.js
|
|
716
|
+
/**
|
|
717
|
+
* Side-by-side (split) diff model: the unified whole-file diff rows are
|
|
718
|
+
* regrouped into line-aligned pairs for a two-column "before | current" view.
|
|
719
|
+
* Pure derivation; the view owns rendering. A context row becomes a pair with
|
|
720
|
+
* both sides, a deletion a left-only pair, an addition a right-only pair, and
|
|
721
|
+
* an adjacent deletion/addition run is paired line-by-line into a single
|
|
722
|
+
* replacement pair so the two columns line up.
|
|
723
|
+
* @module dsh-diff-approval/client/split-diff
|
|
724
|
+
*/
|
|
725
|
+
/** One whole-file diff row → its visible left side text. */
|
|
726
|
+
function leftSideOf(row) {
|
|
727
|
+
if (row.kind === "add") return void 0;
|
|
728
|
+
return {
|
|
729
|
+
text: row.text,
|
|
730
|
+
line: row.oldLine
|
|
731
|
+
};
|
|
732
|
+
}
|
|
733
|
+
/** One whole-file diff row → its visible right side text. */
|
|
734
|
+
function rightSideOf(row) {
|
|
735
|
+
if (row.kind === "del") return void 0;
|
|
736
|
+
return {
|
|
737
|
+
text: row.text,
|
|
738
|
+
line: row.newLine
|
|
739
|
+
};
|
|
740
|
+
}
|
|
741
|
+
/**
|
|
742
|
+
* Regroup the whole-file rows into aligned split pairs, pairing a deletion run
|
|
743
|
+
* with a following addition run line-by-line (so a replaced line is one pair),
|
|
744
|
+
* and leaving a stray deletion or addition as a one-sided pair.
|
|
745
|
+
* @param rows - the whole-file diff rows.
|
|
746
|
+
* @returns the split pairs plus the row→pair index map.
|
|
747
|
+
*/
|
|
748
|
+
function computeSideBySideDiff(rows) {
|
|
749
|
+
const pairs = [];
|
|
750
|
+
const pairOfRow = /* @__PURE__ */ new Map();
|
|
751
|
+
const pendingDel = [];
|
|
752
|
+
const push = (pair, rows) => {
|
|
753
|
+
const index = pairs.length;
|
|
754
|
+
pairs.push(pair);
|
|
755
|
+
for (const r of rows) pairOfRow.set(r, index);
|
|
756
|
+
};
|
|
757
|
+
const flushPending = () => {
|
|
758
|
+
for (const { index, row } of pendingDel) push({
|
|
759
|
+
kind: "del",
|
|
760
|
+
left: leftSideOf(row),
|
|
761
|
+
right: void 0
|
|
762
|
+
}, [index]);
|
|
763
|
+
pendingDel.length = 0;
|
|
764
|
+
};
|
|
765
|
+
for (let i = 0; i < rows.length; i++) {
|
|
766
|
+
const row = rows[i];
|
|
767
|
+
if (row.kind === "context") {
|
|
768
|
+
flushPending();
|
|
769
|
+
push({
|
|
770
|
+
kind: "context",
|
|
771
|
+
left: leftSideOf(row),
|
|
772
|
+
right: rightSideOf(row)
|
|
773
|
+
}, [i]);
|
|
774
|
+
} else if (row.kind === "del") pendingDel.push({
|
|
775
|
+
index: i,
|
|
776
|
+
row
|
|
777
|
+
});
|
|
778
|
+
else {
|
|
779
|
+
const del = pendingDel.shift();
|
|
780
|
+
if (del !== void 0) push({
|
|
781
|
+
kind: "replace",
|
|
782
|
+
left: leftSideOf(del.row),
|
|
783
|
+
right: rightSideOf(row)
|
|
784
|
+
}, [del.index, i]);
|
|
785
|
+
else push({
|
|
786
|
+
kind: "add",
|
|
787
|
+
left: void 0,
|
|
788
|
+
right: rightSideOf(row)
|
|
789
|
+
}, [i]);
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
flushPending();
|
|
793
|
+
return {
|
|
794
|
+
pairs,
|
|
795
|
+
pairOfRow
|
|
796
|
+
};
|
|
797
|
+
}
|
|
798
|
+
/**
|
|
799
|
+
* Pair indices whose left or right text contains the query (case-insensitive).
|
|
800
|
+
* A pair counts once however many times the query appears, so split search
|
|
801
|
+
* highlights the whole pair on both columns and a single "current" pair is
|
|
802
|
+
* stepped through — not each individual left/right occurrence.
|
|
803
|
+
* @param pairs - the split pairs.
|
|
804
|
+
* @param query - the query text; an empty query matches nothing.
|
|
805
|
+
* @returns matching pair indices, in file order.
|
|
806
|
+
*/
|
|
807
|
+
function searchPairs(pairs, query) {
|
|
808
|
+
if (query === "") return [];
|
|
809
|
+
const lower = query.toLowerCase();
|
|
810
|
+
const matches = [];
|
|
811
|
+
for (let index = 0; index < pairs.length; index++) {
|
|
812
|
+
const p = pairs[index];
|
|
813
|
+
if (p === void 0) continue;
|
|
814
|
+
if (p.left !== void 0 && p.left.text.toLowerCase().includes(lower)) matches.push(index);
|
|
815
|
+
else if (p.right !== void 0 && p.right.text.toLowerCase().includes(lower)) matches.push(index);
|
|
816
|
+
}
|
|
817
|
+
return matches;
|
|
818
|
+
}
|
|
819
|
+
//#endregion
|
|
709
820
|
//#region node_modules/.pnpm/@shikijs+types@4.4.3/node_modules/@shikijs/types/dist/index.mjs
|
|
710
821
|
var ShikiError = class extends Error {
|
|
711
822
|
constructor(message) {
|
|
@@ -11625,6 +11736,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
11625
11736
|
const PASTE_ON_COPY_KEY = "diff-approval:paste-on-copy";
|
|
11626
11737
|
const IMPORT_UNTRACKED_KEY = "diff-approval:import-untracked";
|
|
11627
11738
|
const TAB_WIDTH_KEY = "diff-approval:tab-size";
|
|
11739
|
+
const SPLIT_MODE_KEY = "diff-approval:split-mode";
|
|
11628
11740
|
const WRAP_PREFIX = "diff-approval:wrap:";
|
|
11629
11741
|
/**
|
|
11630
11742
|
* Whether copying a reference should also paste it into the chat input and
|
|
@@ -11680,9 +11792,22 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
11680
11792
|
function setTabWidth(value) {
|
|
11681
11793
|
localStorage.setItem(TAB_WIDTH_KEY, String(value));
|
|
11682
11794
|
}
|
|
11795
|
+
/**
|
|
11796
|
+
* Whether the whole-file diff view uses the two-column (side-by-side) layout.
|
|
11797
|
+
* Default off (single column): the unified diff. Only an explicit `'1'` enables
|
|
11798
|
+
* split mode.
|
|
11799
|
+
* @returns whether the split (two-column) diff view is used.
|
|
11800
|
+
*/
|
|
11801
|
+
function splitMode() {
|
|
11802
|
+
return localStorage.getItem(SPLIT_MODE_KEY) === "1";
|
|
11803
|
+
}
|
|
11804
|
+
/** Persist the split-view preference. */
|
|
11805
|
+
function setSplitMode(value) {
|
|
11806
|
+
localStorage.setItem(SPLIT_MODE_KEY, value ? "1" : "0");
|
|
11807
|
+
}
|
|
11683
11808
|
//#endregion
|
|
11684
11809
|
//#region \0dsh-css:/home/runner/work/dsh-diff-approval/dsh-diff-approval/src/client/PendingPanel.module.css.mjs
|
|
11685
|
-
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))}";
|
|
11810
|
+
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))}.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)}";
|
|
11686
11811
|
const tagId = "dsh-diff-approval/PendingPanel.module.css";
|
|
11687
11812
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
|
|
11688
11813
|
const tag = document.createElement("style");
|
|
@@ -11692,99 +11817,111 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
11692
11817
|
document.head.appendChild(tag);
|
|
11693
11818
|
}
|
|
11694
11819
|
var PendingPanel_module_css_default = {
|
|
11695
|
-
"
|
|
11696
|
-
"
|
|
11697
|
-
"settingsRow": "F1KBNa_settingsRow",
|
|
11698
|
-
"searchBar": "F1KBNa_searchBar",
|
|
11699
|
-
"detail": "F1KBNa_detail",
|
|
11700
|
-
"diffActions": "F1KBNa_diffActions",
|
|
11701
|
-
"markerAdd": "F1KBNa_markerAdd",
|
|
11702
|
-
"diff": "F1KBNa_diff",
|
|
11703
|
-
"gutter": "F1KBNa_gutter",
|
|
11704
|
-
"missingHint": "F1KBNa_missingHint",
|
|
11705
|
-
"wrap": "F1KBNa_wrap",
|
|
11706
|
-
"diffHeader": "F1KBNa_diffHeader",
|
|
11707
|
-
"context": "F1KBNa_context",
|
|
11708
|
-
"importNote": "F1KBNa_importNote",
|
|
11709
|
-
"divergedHint": "F1KBNa_divergedHint",
|
|
11710
|
-
"listScroll": "F1KBNa_listScroll",
|
|
11711
|
-
"statusBar": "F1KBNa_statusBar",
|
|
11712
|
-
"readError": "F1KBNa_readError",
|
|
11713
|
-
"settingsRowDesc": "F1KBNa_settingsRowDesc",
|
|
11714
|
-
"settingsRowText": "F1KBNa_settingsRowText",
|
|
11715
|
-
"emptyState": "F1KBNa_emptyState",
|
|
11716
|
-
"langLabel": "F1KBNa_langLabel",
|
|
11717
|
-
"expandExpanded": "F1KBNa_expandExpanded",
|
|
11718
|
-
"noticeText": "F1KBNa_noticeText",
|
|
11719
|
-
"addCount": "F1KBNa_addCount",
|
|
11720
|
-
"subline": "F1KBNa_subline",
|
|
11721
|
-
"badgeCount": "F1KBNa_badgeCount",
|
|
11722
|
-
"line": "F1KBNa_line",
|
|
11820
|
+
"layer": "F1KBNa_layer",
|
|
11821
|
+
"rowHead": "F1KBNa_rowHead",
|
|
11723
11822
|
"header": "F1KBNa_header",
|
|
11724
|
-
"
|
|
11823
|
+
"searchCount": "F1KBNa_searchCount",
|
|
11824
|
+
"splitRdel": "F1KBNa_splitRdel",
|
|
11725
11825
|
"blockActions": "F1KBNa_blockActions",
|
|
11726
|
-
"
|
|
11826
|
+
"badge": "F1KBNa_badge",
|
|
11827
|
+
"fileList": "F1KBNa_fileList",
|
|
11828
|
+
"badgeLabel": "F1KBNa_badgeLabel",
|
|
11727
11829
|
"rows": "F1KBNa_rows",
|
|
11728
|
-
"
|
|
11729
|
-
"searchCount": "F1KBNa_searchCount",
|
|
11730
|
-
"wrapActive": "F1KBNa_wrapActive",
|
|
11830
|
+
"settingsRowTitle": "F1KBNa_settingsRowTitle",
|
|
11731
11831
|
"rail": "F1KBNa_rail",
|
|
11732
|
-
"
|
|
11733
|
-
"
|
|
11734
|
-
"
|
|
11832
|
+
"settingsPage": "F1KBNa_settingsPage",
|
|
11833
|
+
"add": "F1KBNa_add",
|
|
11834
|
+
"splitCols": "F1KBNa_splitCols",
|
|
11835
|
+
"splitHScrollRow": "F1KBNa_splitHScrollRow",
|
|
11836
|
+
"splitHScroll": "F1KBNa_splitHScroll",
|
|
11837
|
+
"diffPath": "F1KBNa_diffPath",
|
|
11838
|
+
"settingsRowText": "F1KBNa_settingsRowText",
|
|
11839
|
+
"detailEmpty": "F1KBNa_detailEmpty",
|
|
11840
|
+
"action": "F1KBNa_action",
|
|
11841
|
+
"notice": "F1KBNa_notice",
|
|
11842
|
+
"splitHScrollFill": "F1KBNa_splitHScrollFill",
|
|
11843
|
+
"splitLdel": "F1KBNa_splitLdel",
|
|
11844
|
+
"diffBodySplit": "F1KBNa_diffBodySplit",
|
|
11845
|
+
"wrap": "F1KBNa_wrap",
|
|
11846
|
+
"row": "F1KBNa_row",
|
|
11847
|
+
"expand": "F1KBNa_expand",
|
|
11848
|
+
"blockFlash": "F1KBNa_blockFlash",
|
|
11849
|
+
"importButton": "F1KBNa_importButton",
|
|
11850
|
+
"del": "F1KBNa_del",
|
|
11851
|
+
"splitRoot": "F1KBNa_splitRoot",
|
|
11852
|
+
"states": "F1KBNa_states",
|
|
11853
|
+
"note": "F1KBNa_note",
|
|
11854
|
+
"panel": "F1KBNa_panel",
|
|
11735
11855
|
"title": "F1KBNa_title",
|
|
11736
|
-
"
|
|
11856
|
+
"settingsSelector": "F1KBNa_settingsSelector",
|
|
11857
|
+
"diffBodyWrap": "F1KBNa_diffBodyWrap",
|
|
11858
|
+
"diffBody": "F1KBNa_diffBody",
|
|
11859
|
+
"readError": "F1KBNa_readError",
|
|
11860
|
+
"bulkActions": "F1KBNa_bulkActions",
|
|
11861
|
+
"delCount": "F1KBNa_delCount",
|
|
11862
|
+
"addCount": "F1KBNa_addCount",
|
|
11737
11863
|
"diffStats": "F1KBNa_diffStats",
|
|
11864
|
+
"close": "F1KBNa_close",
|
|
11865
|
+
"kindHint": "F1KBNa_kindHint",
|
|
11738
11866
|
"blockPosition": "F1KBNa_blockPosition",
|
|
11739
11867
|
"searchInput": "F1KBNa_searchInput",
|
|
11740
|
-
"
|
|
11741
|
-
"
|
|
11742
|
-
"
|
|
11743
|
-
"
|
|
11744
|
-
"
|
|
11745
|
-
"
|
|
11746
|
-
"
|
|
11747
|
-
"
|
|
11868
|
+
"overviewRuler": "F1KBNa_overviewRuler",
|
|
11869
|
+
"vSpacer": "F1KBNa_vSpacer",
|
|
11870
|
+
"context": "F1KBNa_context",
|
|
11871
|
+
"fullscreenBackdrop": "F1KBNa_fullscreenBackdrop",
|
|
11872
|
+
"gutter": "F1KBNa_gutter",
|
|
11873
|
+
"iconAction": "F1KBNa_iconAction",
|
|
11874
|
+
"statusBar": "F1KBNa_statusBar",
|
|
11875
|
+
"noticeText": "F1KBNa_noticeText",
|
|
11876
|
+
"wrapActive": "F1KBNa_wrapActive",
|
|
11877
|
+
"splitRadd": "F1KBNa_splitRadd",
|
|
11878
|
+
"rowMeta": "F1KBNa_rowMeta",
|
|
11879
|
+
"diffHeader": "F1KBNa_diffHeader",
|
|
11748
11880
|
"footerButtons": "F1KBNa_footerButtons",
|
|
11881
|
+
"headerActions": "F1KBNa_headerActions",
|
|
11882
|
+
"actionQuietDisabled": "F1KBNa_actionQuietDisabled",
|
|
11883
|
+
"detail": "F1KBNa_detail",
|
|
11749
11884
|
"missing": "F1KBNa_missing",
|
|
11750
|
-
"
|
|
11751
|
-
"
|
|
11752
|
-
"
|
|
11753
|
-
"
|
|
11754
|
-
"
|
|
11755
|
-
"
|
|
11885
|
+
"markerDel": "F1KBNa_markerDel",
|
|
11886
|
+
"settingsRowDesc": "F1KBNa_settingsRowDesc",
|
|
11887
|
+
"langSelect": "F1KBNa_langSelect",
|
|
11888
|
+
"importNote": "F1KBNa_importNote",
|
|
11889
|
+
"listScroll": "F1KBNa_listScroll",
|
|
11890
|
+
"group": "F1KBNa_group",
|
|
11891
|
+
"divergedHint": "F1KBNa_divergedHint",
|
|
11892
|
+
"splitCol": "F1KBNa_splitCol",
|
|
11756
11893
|
"resizeHandle": "F1KBNa_resizeHandle",
|
|
11757
|
-
"
|
|
11758
|
-
"
|
|
11759
|
-
"
|
|
11760
|
-
"expand": "F1KBNa_expand",
|
|
11761
|
-
"diffBodyWrap": "F1KBNa_diffBodyWrap",
|
|
11762
|
-
"lines": "F1KBNa_lines",
|
|
11763
|
-
"action": "F1KBNa_action",
|
|
11894
|
+
"actionPrimary": "F1KBNa_actionPrimary",
|
|
11895
|
+
"diffFlash": "F1KBNa_diffFlash",
|
|
11896
|
+
"subline": "F1KBNa_subline",
|
|
11764
11897
|
"statusAction": "F1KBNa_statusAction",
|
|
11765
|
-
"
|
|
11766
|
-
"
|
|
11898
|
+
"noteCentered": "F1KBNa_noteCentered",
|
|
11899
|
+
"actionError": "F1KBNa_actionError",
|
|
11900
|
+
"lines": "F1KBNa_lines",
|
|
11901
|
+
"hint": "F1KBNa_hint",
|
|
11902
|
+
"diff": "F1KBNa_diff",
|
|
11903
|
+
"markerAdd": "F1KBNa_markerAdd",
|
|
11904
|
+
"code": "F1KBNa_code",
|
|
11905
|
+
"emptyState": "F1KBNa_emptyState",
|
|
11906
|
+
"expandExpanded": "F1KBNa_expandExpanded",
|
|
11907
|
+
"langLabel": "F1KBNa_langLabel",
|
|
11908
|
+
"searchBar": "F1KBNa_searchBar",
|
|
11909
|
+
"badgeCount": "F1KBNa_badgeCount",
|
|
11910
|
+
"diffActions": "F1KBNa_diffActions",
|
|
11911
|
+
"splitDivider": "F1KBNa_splitDivider",
|
|
11912
|
+
"settingsRow": "F1KBNa_settingsRow",
|
|
11913
|
+
"noticeButton": "F1KBNa_noticeButton",
|
|
11914
|
+
"splitLadd": "F1KBNa_splitLadd",
|
|
11767
11915
|
"settingsSelectorChevron": "F1KBNa_settingsSelectorChevron",
|
|
11768
|
-
"delCount": "F1KBNa_delCount",
|
|
11769
|
-
"flexSpacer": "F1KBNa_flexSpacer",
|
|
11770
|
-
"markerDel": "F1KBNa_markerDel",
|
|
11771
|
-
"note": "F1KBNa_note",
|
|
11772
|
-
"states": "F1KBNa_states",
|
|
11773
|
-
"rowFailed": "F1KBNa_rowFailed",
|
|
11774
|
-
"settingsPage": "F1KBNa_settingsPage",
|
|
11775
|
-
"rowHead": "F1KBNa_rowHead",
|
|
11776
|
-
"headerActions": "F1KBNa_headerActions",
|
|
11777
|
-
"group": "F1KBNa_group",
|
|
11778
|
-
"settingsRowTitle": "F1KBNa_settingsRowTitle",
|
|
11779
11916
|
"rowPath": "F1KBNa_rowPath",
|
|
11780
|
-
"
|
|
11781
|
-
"
|
|
11782
|
-
"
|
|
11783
|
-
"
|
|
11784
|
-
"
|
|
11785
|
-
"
|
|
11786
|
-
"
|
|
11787
|
-
"
|
|
11917
|
+
"fileListFloat": "F1KBNa_fileListFloat",
|
|
11918
|
+
"missingHint": "F1KBNa_missingHint",
|
|
11919
|
+
"rowFailed": "F1KBNa_rowFailed",
|
|
11920
|
+
"overviewMarker": "F1KBNa_overviewMarker",
|
|
11921
|
+
"kindTag": "F1KBNa_kindTag",
|
|
11922
|
+
"flexSpacer": "F1KBNa_flexSpacer",
|
|
11923
|
+
"split": "F1KBNa_split",
|
|
11924
|
+
"line": "F1KBNa_line"
|
|
11788
11925
|
};
|
|
11789
11926
|
//#endregion
|
|
11790
11927
|
//#region lib/types/client/PendingPanel.js
|
|
@@ -12149,6 +12286,541 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
12149
12286
|
});
|
|
12150
12287
|
return blocks;
|
|
12151
12288
|
}
|
|
12289
|
+
/** One side's line-content for the split view: the highlighted runs or plain text. */
|
|
12290
|
+
function splitSideContent(side, wrapped, runs) {
|
|
12291
|
+
if (side === void 0) return "";
|
|
12292
|
+
const highlighted = runs !== void 0 && runs.length > 0;
|
|
12293
|
+
if (wrapped === void 0) return highlighted ? runs.map((span, i) => (0, react_jsx_runtime.jsx)("span", {
|
|
12294
|
+
style: span.style,
|
|
12295
|
+
children: span.text
|
|
12296
|
+
}, i)) : side.text === "" ? "\xA0" : side.text;
|
|
12297
|
+
let offset = 0;
|
|
12298
|
+
return wrapped.map((line, i) => {
|
|
12299
|
+
const start = offset;
|
|
12300
|
+
offset += line.length;
|
|
12301
|
+
const content = highlighted ? clipRuns(runs, start, offset) : line === "" ? "\xA0" : line;
|
|
12302
|
+
return (0, react_jsx_runtime.jsx)("div", {
|
|
12303
|
+
className: PendingPanel_module_css_default.subline,
|
|
12304
|
+
children: content
|
|
12305
|
+
}, i);
|
|
12306
|
+
});
|
|
12307
|
+
}
|
|
12308
|
+
/**
|
|
12309
|
+
* One side of a split pair row, rendered inside its own column. The two columns
|
|
12310
|
+
* are drawn by two independent `.splitCol` scrollers (each with its own
|
|
12311
|
+
* horizontal scrollbar) that share one vertical scroller, and each row gets the
|
|
12312
|
+
* same fixed `height` (the pair's max of the two sides' wrapped sub-line
|
|
12313
|
+
* counts) so the left/right halves always align on the same Y — no jump when
|
|
12314
|
+
* one side is longer. The gutter and code are top-aligned so sub-lines line up
|
|
12315
|
+
* across the divider.
|
|
12316
|
+
*/
|
|
12317
|
+
function SplitSideRow({ index, side, wrapped, runs, kind, isLeft, height, focused, searchHit, searchCurrent, onHover }) {
|
|
12318
|
+
const tint = isLeft ? kind === "del" || kind === "replace" ? PendingPanel_module_css_default.splitLdel : "" : kind === "add" || kind === "replace" ? PendingPanel_module_css_default.splitRadd : "";
|
|
12319
|
+
return (0, react_jsx_runtime.jsxs)("div", {
|
|
12320
|
+
className: PendingPanel_module_css_default.line,
|
|
12321
|
+
style: { height },
|
|
12322
|
+
"data-diff-split-row": true,
|
|
12323
|
+
"data-diff-split-index": index,
|
|
12324
|
+
"data-diff-split-side": isLeft ? "left" : "right",
|
|
12325
|
+
"data-diff-focused": focused ? "" : void 0,
|
|
12326
|
+
"data-diff-search": searchHit ? searchCurrent ? "current" : "hit" : void 0,
|
|
12327
|
+
onMouseEnter: onHover,
|
|
12328
|
+
children: [(0, react_jsx_runtime.jsx)("span", {
|
|
12329
|
+
className: PendingPanel_module_css_default.gutter,
|
|
12330
|
+
children: side?.line ?? ""
|
|
12331
|
+
}), (0, react_jsx_runtime.jsx)("span", {
|
|
12332
|
+
className: `${PendingPanel_module_css_default.code} ${tint}`,
|
|
12333
|
+
"data-diff-code": true,
|
|
12334
|
+
children: splitSideContent(side, wrapped, runs)
|
|
12335
|
+
})]
|
|
12336
|
+
});
|
|
12337
|
+
}
|
|
12338
|
+
/** The two-column (side-by-side) whole-file diff view. */
|
|
12339
|
+
const SplitDiff = (0, react.forwardRef)(function SplitDiff({ file, model, runs, langWrap, tabWidthSpaces, busy, t, onBlockKeep, onBlockRevert }, ref) {
|
|
12340
|
+
const { pairs, pairOfRow } = (0, react.useMemo)(() => computeSideBySideDiff(model.diff.rows), [model]);
|
|
12341
|
+
const pairCount = pairs.length;
|
|
12342
|
+
const bodyRef = (0, react.useRef)(null);
|
|
12343
|
+
const [scrollTop, setScrollTop] = (0, react.useState)(0);
|
|
12344
|
+
const [viewportH, setViewportH] = (0, react.useState)(0);
|
|
12345
|
+
const [bodyWidth, setBodyWidth] = (0, react.useState)(0);
|
|
12346
|
+
const [hoveredBlock, setHoveredBlock] = (0, react.useState)(void 0);
|
|
12347
|
+
const [focus, setFocus] = (0, react.useState)(0);
|
|
12348
|
+
const [flashKey, setFlashKey] = (0, react.useState)(0);
|
|
12349
|
+
const hoveredBlockRef = (0, react.useRef)(void 0);
|
|
12350
|
+
const leftColRef = (0, react.useRef)(null);
|
|
12351
|
+
const rightColRef = (0, react.useRef)(null);
|
|
12352
|
+
const leftHScrollRef = (0, react.useRef)(null);
|
|
12353
|
+
const rightHScrollRef = (0, react.useRef)(null);
|
|
12354
|
+
const [fillWidth, setFillWidth] = (0, react.useState)({
|
|
12355
|
+
left: 0,
|
|
12356
|
+
right: 0
|
|
12357
|
+
});
|
|
12358
|
+
const [searchOpen, setSearchOpen] = (0, react.useState)(false);
|
|
12359
|
+
const [searchQuery, setSearchQuery] = (0, react.useState)("");
|
|
12360
|
+
const [searchIndex, setSearchIndex] = (0, react.useState)(0);
|
|
12361
|
+
const searchInputRef = (0, react.useRef)(null);
|
|
12362
|
+
(0, react.useEffect)(() => {
|
|
12363
|
+
setFocus(0);
|
|
12364
|
+
bodyRef.current?.focus();
|
|
12365
|
+
setFlashKey((k) => k + 1);
|
|
12366
|
+
setHoveredBlock(void 0);
|
|
12367
|
+
setSearchOpen(false);
|
|
12368
|
+
setSearchQuery("");
|
|
12369
|
+
setSearchIndex(0);
|
|
12370
|
+
}, [file.id]);
|
|
12371
|
+
(0, react.useEffect)(() => {
|
|
12372
|
+
const body = bodyRef.current;
|
|
12373
|
+
if (body === null) return;
|
|
12374
|
+
const measure = () => {
|
|
12375
|
+
setViewportH(body.clientHeight);
|
|
12376
|
+
setBodyWidth(body.clientWidth);
|
|
12377
|
+
};
|
|
12378
|
+
measure();
|
|
12379
|
+
const observer = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(measure);
|
|
12380
|
+
observer?.observe(body);
|
|
12381
|
+
return () => {
|
|
12382
|
+
observer?.disconnect();
|
|
12383
|
+
};
|
|
12384
|
+
}, [file.id]);
|
|
12385
|
+
const blockOfPair = (0, react.useMemo)(() => model.blocks.map((block) => ({
|
|
12386
|
+
start: pairOfRow.get(block.start) ?? 0,
|
|
12387
|
+
end: pairOfRow.get(block.end) ?? 0
|
|
12388
|
+
})), [model, pairOfRow]);
|
|
12389
|
+
const blockIndexByPair = (0, react.useMemo)(() => {
|
|
12390
|
+
const map = /* @__PURE__ */ new Map();
|
|
12391
|
+
blockOfPair.forEach((block, bi) => {
|
|
12392
|
+
for (let k = block.start; k <= block.end; k++) map.set(k, bi);
|
|
12393
|
+
});
|
|
12394
|
+
return map;
|
|
12395
|
+
}, [blockOfPair]);
|
|
12396
|
+
const onPairHover = (0, react.useCallback)((k) => {
|
|
12397
|
+
const bi = blockIndexByPair.get(k);
|
|
12398
|
+
setHoveredBlock(bi);
|
|
12399
|
+
if (bi !== void 0) hoveredBlockRef.current = bi;
|
|
12400
|
+
}, [blockIndexByPair]);
|
|
12401
|
+
const colWidth = Math.max(0, (bodyWidth - 1) / 2);
|
|
12402
|
+
const pairWrapped = (0, react.useMemo)(() => {
|
|
12403
|
+
if (!langWrap || bodyWidth === 0) return null;
|
|
12404
|
+
const measure = makeMeasurer(codeFontOf());
|
|
12405
|
+
if (measure === void 0) return null;
|
|
12406
|
+
const charWidth = measure("0");
|
|
12407
|
+
const wrapW = colWidth - WRAP_GUTTERS_PX / 2 - charWidth;
|
|
12408
|
+
const tabPx = tabWidthSpaces * measure(" ");
|
|
12409
|
+
return pairs.map((p) => ({
|
|
12410
|
+
left: p.left === void 0 ? void 0 : wrapInto(p.left.text, wrapW, measure, tabPx),
|
|
12411
|
+
right: p.right === void 0 ? void 0 : wrapInto(p.right.text, wrapW, measure, tabPx)
|
|
12412
|
+
}));
|
|
12413
|
+
}, [
|
|
12414
|
+
pairs,
|
|
12415
|
+
langWrap,
|
|
12416
|
+
bodyWidth,
|
|
12417
|
+
colWidth,
|
|
12418
|
+
tabWidthSpaces
|
|
12419
|
+
]);
|
|
12420
|
+
const pairHeights = (0, react.useMemo)(() => {
|
|
12421
|
+
if (pairWrapped === null) return null;
|
|
12422
|
+
return pairWrapped.map((w) => Math.max(w.left?.length ?? 1, w.right?.length ?? 1) * ROW_HEIGHT_PX);
|
|
12423
|
+
}, [pairWrapped]);
|
|
12424
|
+
const pairOffsets = (0, react.useMemo)(() => {
|
|
12425
|
+
if (pairHeights === null) return null;
|
|
12426
|
+
const offs = new Array(pairHeights.length + 1);
|
|
12427
|
+
offs[0] = 0;
|
|
12428
|
+
for (let i = 0; i < pairHeights.length; i++) offs[i + 1] = offs[i] + pairHeights[i];
|
|
12429
|
+
return offs;
|
|
12430
|
+
}, [pairHeights]);
|
|
12431
|
+
const totalHeight = pairOffsets === null ? pairCount * ROW_HEIGHT_PX : pairOffsets[pairCount] ?? 0;
|
|
12432
|
+
const off = (k) => pairOffsets === null ? k * ROW_HEIGHT_PX : pairOffsets[Math.max(0, Math.min(k, pairCount))] ?? 0;
|
|
12433
|
+
const pairHeightAt = (k) => pairHeights === null ? ROW_HEIGHT_PX : pairHeights[k] ?? ROW_HEIGHT_PX;
|
|
12434
|
+
const widestSide = (0, react.useMemo)(() => {
|
|
12435
|
+
let left = 0;
|
|
12436
|
+
let right = 0;
|
|
12437
|
+
for (const p of pairs) {
|
|
12438
|
+
if (p.left !== void 0) left = Math.max(left, p.left.text.length);
|
|
12439
|
+
if (p.right !== void 0) right = Math.max(right, p.right.text.length);
|
|
12440
|
+
}
|
|
12441
|
+
return {
|
|
12442
|
+
left,
|
|
12443
|
+
right
|
|
12444
|
+
};
|
|
12445
|
+
}, [pairs]);
|
|
12446
|
+
(0, react.useLayoutEffect)(() => {
|
|
12447
|
+
const sync = (side) => {
|
|
12448
|
+
const col = side === "left" ? leftColRef.current : rightColRef.current;
|
|
12449
|
+
const strip = side === "left" ? leftHScrollRef.current : rightHScrollRef.current;
|
|
12450
|
+
if (col === null || strip === null) return;
|
|
12451
|
+
const width = col.scrollWidth;
|
|
12452
|
+
setFillWidth((prev) => prev[side] === width ? prev : {
|
|
12453
|
+
...prev,
|
|
12454
|
+
[side]: width
|
|
12455
|
+
});
|
|
12456
|
+
strip.scrollLeft = col.scrollLeft;
|
|
12457
|
+
};
|
|
12458
|
+
sync("left");
|
|
12459
|
+
sync("right");
|
|
12460
|
+
}, [
|
|
12461
|
+
pairs,
|
|
12462
|
+
langWrap,
|
|
12463
|
+
tabWidthSpaces,
|
|
12464
|
+
bodyWidth
|
|
12465
|
+
]);
|
|
12466
|
+
const onHScroll = (0, react.useCallback)((side) => {
|
|
12467
|
+
const strip = side === "left" ? leftHScrollRef.current : rightHScrollRef.current;
|
|
12468
|
+
const col = side === "left" ? leftColRef.current : rightColRef.current;
|
|
12469
|
+
if (strip === null || col === null) return;
|
|
12470
|
+
col.scrollLeft = strip.scrollLeft;
|
|
12471
|
+
}, []);
|
|
12472
|
+
const searchMatches = (0, react.useMemo)(() => searchPairs(pairs, searchQuery), [pairs, searchQuery]);
|
|
12473
|
+
const searchHitSet = (0, react.useMemo)(() => new Set(searchMatches), [searchMatches]);
|
|
12474
|
+
const currentSearchPair = searchMatches.length === 0 ? void 0 : searchMatches[searchIndex % searchMatches.length];
|
|
12475
|
+
const closeSearch = () => {
|
|
12476
|
+
setSearchOpen(false);
|
|
12477
|
+
setSearchQuery("");
|
|
12478
|
+
setSearchIndex(0);
|
|
12479
|
+
bodyRef.current?.focus();
|
|
12480
|
+
};
|
|
12481
|
+
const openSearch = () => {
|
|
12482
|
+
setSearchOpen(true);
|
|
12483
|
+
requestAnimationFrame(() => {
|
|
12484
|
+
searchInputRef.current?.focus();
|
|
12485
|
+
searchInputRef.current?.select();
|
|
12486
|
+
});
|
|
12487
|
+
};
|
|
12488
|
+
const goSearch = (direction) => {
|
|
12489
|
+
const len = searchMatches.length;
|
|
12490
|
+
if (len === 0) return;
|
|
12491
|
+
const next = (searchIndex + direction + len) % len;
|
|
12492
|
+
setSearchIndex(next);
|
|
12493
|
+
const pairIndex = searchMatches[next];
|
|
12494
|
+
if (pairIndex === void 0) return;
|
|
12495
|
+
const body = bodyRef.current;
|
|
12496
|
+
if (body === null) return;
|
|
12497
|
+
const target = Math.max(0, off(pairIndex) - 44);
|
|
12498
|
+
const clamped = Math.max(0, Math.min(target, body.scrollHeight - body.clientHeight));
|
|
12499
|
+
if (body.scrollTop !== clamped) body.scrollTop = clamped;
|
|
12500
|
+
setScrollTop(clamped);
|
|
12501
|
+
};
|
|
12502
|
+
const handleBlockAction = async (action) => {
|
|
12503
|
+
const operated = hoveredBlock ?? hoveredBlockRef.current;
|
|
12504
|
+
if (operated === void 0) return;
|
|
12505
|
+
const range = blockRanges[operated];
|
|
12506
|
+
if (range === void 0) return;
|
|
12507
|
+
await (action === "keep" ? onBlockKeep(file.sessionId, file.id, range) : onBlockRevert(file.sessionId, file.id, range));
|
|
12508
|
+
const count = model.blocks.length;
|
|
12509
|
+
if (count === 0) return;
|
|
12510
|
+
const next = Math.max(0, Math.min(operated, count - 1));
|
|
12511
|
+
setFocus(next);
|
|
12512
|
+
setHoveredBlock(void 0);
|
|
12513
|
+
setFlashKey((key) => key + 1);
|
|
12514
|
+
};
|
|
12515
|
+
const pairAtY = (y) => {
|
|
12516
|
+
if (pairOffsets === null) return Math.floor(y / ROW_HEIGHT_PX);
|
|
12517
|
+
if (y <= 0) return 0;
|
|
12518
|
+
let lo = 0, hi = pairCount;
|
|
12519
|
+
while (lo < hi) {
|
|
12520
|
+
const mid = lo + hi + 1 >> 1;
|
|
12521
|
+
if ((pairOffsets[mid] ?? 0) <= y) lo = mid;
|
|
12522
|
+
else hi = mid - 1;
|
|
12523
|
+
}
|
|
12524
|
+
return lo;
|
|
12525
|
+
};
|
|
12526
|
+
const viewport = viewportH > 0 ? viewportH : totalHeight;
|
|
12527
|
+
const start = Math.max(0, pairAtY(scrollTop) - OVERSCAN_ROWS);
|
|
12528
|
+
const end = Math.min(pairCount, pairAtY(scrollTop + viewport) + OVERSCAN_ROWS);
|
|
12529
|
+
const visiblePairs = pairs.slice(start, end);
|
|
12530
|
+
const jump = (direction) => {
|
|
12531
|
+
if (blockOfPair.length === 0) return;
|
|
12532
|
+
setFocus((current) => {
|
|
12533
|
+
if (direction === -1) return (current - 1 + blockOfPair.length) % blockOfPair.length;
|
|
12534
|
+
const top = bodyRef.current?.scrollTop ?? 0;
|
|
12535
|
+
for (let index = current + 1; index < blockOfPair.length; index++) if (off(blockOfPair[index].start) >= top) return index;
|
|
12536
|
+
return 0;
|
|
12537
|
+
});
|
|
12538
|
+
setFlashKey((k) => k + 1);
|
|
12539
|
+
};
|
|
12540
|
+
(0, react.useImperativeHandle)(ref, () => ({
|
|
12541
|
+
jump,
|
|
12542
|
+
openSearch
|
|
12543
|
+
}), [jump, openSearch]);
|
|
12544
|
+
(0, react.useLayoutEffect)(() => {
|
|
12545
|
+
if (pairCount === 0) return;
|
|
12546
|
+
const block = blockOfPair[focus];
|
|
12547
|
+
if (block === void 0) return;
|
|
12548
|
+
const body = bodyRef.current;
|
|
12549
|
+
if (body === null) return;
|
|
12550
|
+
const target = off(block.start) - 44;
|
|
12551
|
+
const clamped = Math.max(0, Math.min(target, body.scrollHeight - body.clientHeight));
|
|
12552
|
+
if (body.scrollTop !== clamped) body.scrollTop = clamped;
|
|
12553
|
+
setScrollTop(clamped);
|
|
12554
|
+
}, [
|
|
12555
|
+
model,
|
|
12556
|
+
focus,
|
|
12557
|
+
flashKey,
|
|
12558
|
+
pairCount
|
|
12559
|
+
]);
|
|
12560
|
+
const onScroll = () => {
|
|
12561
|
+
setScrollTop(bodyRef.current?.scrollTop ?? 0);
|
|
12562
|
+
};
|
|
12563
|
+
const inFocused = (k) => {
|
|
12564
|
+
const block = blockOfPair[focus];
|
|
12565
|
+
return block !== void 0 && k >= block.start && k <= block.end;
|
|
12566
|
+
};
|
|
12567
|
+
const blockRanges = (0, react.useMemo)(() => model.blocks.map((block) => blockRangesOf(model.diff.rows, block)), [model]);
|
|
12568
|
+
const focusedBlock = blockOfPair[focus];
|
|
12569
|
+
const flashTop = focusedBlock === void 0 ? 0 : Math.max(0, off(focusedBlock.start) - scrollTop);
|
|
12570
|
+
const flashBottom = focusedBlock === void 0 ? 0 : Math.min(viewportH > 0 ? viewportH : Number.POSITIVE_INFINITY, off(focusedBlock.end + 1) - scrollTop);
|
|
12571
|
+
const flashHeight = Math.max(0, flashBottom - flashTop);
|
|
12572
|
+
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)));
|
|
12573
|
+
return (0, react_jsx_runtime.jsxs)("div", {
|
|
12574
|
+
className: PendingPanel_module_css_default.splitRoot,
|
|
12575
|
+
onMouseLeave: () => setHoveredBlock(void 0),
|
|
12576
|
+
children: [
|
|
12577
|
+
(0, react_jsx_runtime.jsx)("div", {
|
|
12578
|
+
className: `${PendingPanel_module_css_default.diffBody} ${PendingPanel_module_css_default.diffBodySplit}`,
|
|
12579
|
+
ref: bodyRef,
|
|
12580
|
+
tabIndex: 0,
|
|
12581
|
+
onScroll,
|
|
12582
|
+
style: { tabSize: tabWidthSpaces },
|
|
12583
|
+
"data-diff-body": true,
|
|
12584
|
+
children: (0, react_jsx_runtime.jsxs)("div", {
|
|
12585
|
+
className: PendingPanel_module_css_default.splitCols,
|
|
12586
|
+
children: [
|
|
12587
|
+
(0, react_jsx_runtime.jsx)("div", {
|
|
12588
|
+
className: PendingPanel_module_css_default.splitCol,
|
|
12589
|
+
ref: leftColRef,
|
|
12590
|
+
"data-diff-split-side": "left",
|
|
12591
|
+
children: (0, react_jsx_runtime.jsxs)("div", {
|
|
12592
|
+
className: `${PendingPanel_module_css_default.lines}${langWrap ? " " + PendingPanel_module_css_default.wrap : ""}`,
|
|
12593
|
+
style: langWrap ? void 0 : { minWidth: `max(100%, ${widestSide.left}ch)` },
|
|
12594
|
+
children: [
|
|
12595
|
+
start > 0 && (0, react_jsx_runtime.jsx)("div", {
|
|
12596
|
+
className: PendingPanel_module_css_default.vSpacer,
|
|
12597
|
+
style: { height: off(start) },
|
|
12598
|
+
"aria-hidden": "true"
|
|
12599
|
+
}),
|
|
12600
|
+
visiblePairs.map((pair, offset) => {
|
|
12601
|
+
const index = start + offset;
|
|
12602
|
+
const leftRuns = pair.left === void 0 ? void 0 : runs?.oldRuns?.[(pair.left.line ?? 0) - 1];
|
|
12603
|
+
return (0, react_jsx_runtime.jsx)(SplitSideRow, {
|
|
12604
|
+
index,
|
|
12605
|
+
side: pair.left,
|
|
12606
|
+
wrapped: pairWrapped?.[index]?.left,
|
|
12607
|
+
runs: leftRuns,
|
|
12608
|
+
kind: pair.kind,
|
|
12609
|
+
isLeft: true,
|
|
12610
|
+
height: pairHeightAt(index),
|
|
12611
|
+
focused: inFocused(index),
|
|
12612
|
+
searchHit: searchHitSet.has(index),
|
|
12613
|
+
searchCurrent: index === currentSearchPair,
|
|
12614
|
+
onHover: () => onPairHover(index)
|
|
12615
|
+
}, index);
|
|
12616
|
+
}),
|
|
12617
|
+
end < pairCount && (0, react_jsx_runtime.jsx)("div", {
|
|
12618
|
+
className: PendingPanel_module_css_default.vSpacer,
|
|
12619
|
+
style: { height: totalHeight - off(end) },
|
|
12620
|
+
"aria-hidden": "true"
|
|
12621
|
+
})
|
|
12622
|
+
]
|
|
12623
|
+
})
|
|
12624
|
+
}),
|
|
12625
|
+
(0, react_jsx_runtime.jsx)("div", { className: PendingPanel_module_css_default.splitDivider }),
|
|
12626
|
+
(0, react_jsx_runtime.jsx)("div", {
|
|
12627
|
+
className: PendingPanel_module_css_default.splitCol,
|
|
12628
|
+
ref: rightColRef,
|
|
12629
|
+
"data-diff-split-side": "right",
|
|
12630
|
+
children: (0, react_jsx_runtime.jsxs)("div", {
|
|
12631
|
+
className: `${PendingPanel_module_css_default.lines}${langWrap ? " " + PendingPanel_module_css_default.wrap : ""}`,
|
|
12632
|
+
style: langWrap ? void 0 : { minWidth: `max(100%, ${widestSide.right}ch)` },
|
|
12633
|
+
children: [
|
|
12634
|
+
start > 0 && (0, react_jsx_runtime.jsx)("div", {
|
|
12635
|
+
className: PendingPanel_module_css_default.vSpacer,
|
|
12636
|
+
style: { height: off(start) },
|
|
12637
|
+
"aria-hidden": "true"
|
|
12638
|
+
}),
|
|
12639
|
+
visiblePairs.map((pair, offset) => {
|
|
12640
|
+
const index = start + offset;
|
|
12641
|
+
const rightRuns = pair.right === void 0 ? void 0 : runs?.newRuns?.[(pair.right.line ?? 0) - 1];
|
|
12642
|
+
return (0, react_jsx_runtime.jsx)(SplitSideRow, {
|
|
12643
|
+
index,
|
|
12644
|
+
side: pair.right,
|
|
12645
|
+
wrapped: pairWrapped?.[index]?.right,
|
|
12646
|
+
runs: rightRuns,
|
|
12647
|
+
kind: pair.kind,
|
|
12648
|
+
isLeft: false,
|
|
12649
|
+
height: pairHeightAt(index),
|
|
12650
|
+
focused: inFocused(index),
|
|
12651
|
+
searchHit: searchHitSet.has(index),
|
|
12652
|
+
searchCurrent: index === currentSearchPair,
|
|
12653
|
+
onHover: () => onPairHover(index)
|
|
12654
|
+
}, index);
|
|
12655
|
+
}),
|
|
12656
|
+
end < pairCount && (0, react_jsx_runtime.jsx)("div", {
|
|
12657
|
+
className: PendingPanel_module_css_default.vSpacer,
|
|
12658
|
+
style: { height: totalHeight - off(end) },
|
|
12659
|
+
"aria-hidden": "true"
|
|
12660
|
+
})
|
|
12661
|
+
]
|
|
12662
|
+
})
|
|
12663
|
+
})
|
|
12664
|
+
]
|
|
12665
|
+
})
|
|
12666
|
+
}),
|
|
12667
|
+
(0, react_jsx_runtime.jsxs)("div", {
|
|
12668
|
+
className: PendingPanel_module_css_default.splitHScrollRow,
|
|
12669
|
+
"data-diff-hscroll-row": true,
|
|
12670
|
+
children: [
|
|
12671
|
+
(0, react_jsx_runtime.jsx)("div", {
|
|
12672
|
+
className: PendingPanel_module_css_default.splitHScroll,
|
|
12673
|
+
ref: leftHScrollRef,
|
|
12674
|
+
"data-diff-hscroll": "left",
|
|
12675
|
+
style: {
|
|
12676
|
+
width: colWidth,
|
|
12677
|
+
flex: "none"
|
|
12678
|
+
},
|
|
12679
|
+
onScroll: () => onHScroll("left"),
|
|
12680
|
+
children: (0, react_jsx_runtime.jsx)("div", {
|
|
12681
|
+
className: PendingPanel_module_css_default.splitHScrollFill,
|
|
12682
|
+
style: { width: fillWidth.left || void 0 }
|
|
12683
|
+
})
|
|
12684
|
+
}),
|
|
12685
|
+
(0, react_jsx_runtime.jsx)("div", { className: PendingPanel_module_css_default.splitDivider }),
|
|
12686
|
+
(0, react_jsx_runtime.jsx)("div", {
|
|
12687
|
+
className: PendingPanel_module_css_default.splitHScroll,
|
|
12688
|
+
ref: rightHScrollRef,
|
|
12689
|
+
"data-diff-hscroll": "right",
|
|
12690
|
+
style: {
|
|
12691
|
+
width: colWidth,
|
|
12692
|
+
flex: "none"
|
|
12693
|
+
},
|
|
12694
|
+
onScroll: () => onHScroll("right"),
|
|
12695
|
+
children: (0, react_jsx_runtime.jsx)("div", {
|
|
12696
|
+
className: PendingPanel_module_css_default.splitHScrollFill,
|
|
12697
|
+
style: { width: fillWidth.right || void 0 }
|
|
12698
|
+
})
|
|
12699
|
+
})
|
|
12700
|
+
]
|
|
12701
|
+
}),
|
|
12702
|
+
searchOpen && (0, react_jsx_runtime.jsxs)("div", {
|
|
12703
|
+
className: PendingPanel_module_css_default.searchBar,
|
|
12704
|
+
"data-diff-searchbar": true,
|
|
12705
|
+
children: [
|
|
12706
|
+
(0, react_jsx_runtime.jsx)("input", {
|
|
12707
|
+
ref: searchInputRef,
|
|
12708
|
+
className: PendingPanel_module_css_default.searchInput,
|
|
12709
|
+
"data-diff-search-input": true,
|
|
12710
|
+
value: searchQuery,
|
|
12711
|
+
placeholder: t("panel.searchPlaceholder"),
|
|
12712
|
+
onChange: (event) => {
|
|
12713
|
+
setSearchQuery(event.target.value);
|
|
12714
|
+
setSearchIndex(0);
|
|
12715
|
+
},
|
|
12716
|
+
onKeyDown: (event) => {
|
|
12717
|
+
if (event.key === "Enter") {
|
|
12718
|
+
event.preventDefault();
|
|
12719
|
+
goSearch(event.shiftKey ? -1 : 1);
|
|
12720
|
+
} else if (event.key === "Escape") closeSearch();
|
|
12721
|
+
}
|
|
12722
|
+
}),
|
|
12723
|
+
(0, react_jsx_runtime.jsx)("span", {
|
|
12724
|
+
className: PendingPanel_module_css_default.searchCount,
|
|
12725
|
+
"data-diff-search-count": true,
|
|
12726
|
+
children: searchMatches.length === 0 ? "0/0" : `${searchIndex % searchMatches.length + 1}/${searchMatches.length}`
|
|
12727
|
+
}),
|
|
12728
|
+
(0, react_jsx_runtime.jsx)("button", {
|
|
12729
|
+
type: "button",
|
|
12730
|
+
className: `${PendingPanel_module_css_default.action} ${PendingPanel_module_css_default.iconAction}`,
|
|
12731
|
+
"data-diff-search-prev": true,
|
|
12732
|
+
"aria-label": t("action.prevDiff"),
|
|
12733
|
+
disabled: searchMatches.length === 0,
|
|
12734
|
+
onClick: () => {
|
|
12735
|
+
goSearch(-1);
|
|
12736
|
+
},
|
|
12737
|
+
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronUpOutline14, { size: 14 })
|
|
12738
|
+
}),
|
|
12739
|
+
(0, react_jsx_runtime.jsx)("button", {
|
|
12740
|
+
type: "button",
|
|
12741
|
+
className: `${PendingPanel_module_css_default.action} ${PendingPanel_module_css_default.iconAction}`,
|
|
12742
|
+
"data-diff-search-next": true,
|
|
12743
|
+
"aria-label": t("action.nextDiff"),
|
|
12744
|
+
disabled: searchMatches.length === 0,
|
|
12745
|
+
onClick: () => {
|
|
12746
|
+
goSearch(1);
|
|
12747
|
+
},
|
|
12748
|
+
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, { size: 14 })
|
|
12749
|
+
}),
|
|
12750
|
+
(0, react_jsx_runtime.jsx)("button", {
|
|
12751
|
+
type: "button",
|
|
12752
|
+
className: `${PendingPanel_module_css_default.action} ${PendingPanel_module_css_default.iconAction}`,
|
|
12753
|
+
"data-diff-search-close": true,
|
|
12754
|
+
"aria-label": t("action.close"),
|
|
12755
|
+
onClick: closeSearch,
|
|
12756
|
+
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCloseOutline16, { size: 14 })
|
|
12757
|
+
})
|
|
12758
|
+
]
|
|
12759
|
+
}),
|
|
12760
|
+
focusedBlock !== void 0 && flashKey > 0 && (0, react_jsx_runtime.jsx)("div", {
|
|
12761
|
+
className: PendingPanel_module_css_default.blockFlash,
|
|
12762
|
+
"data-diff-block-flash": true,
|
|
12763
|
+
style: {
|
|
12764
|
+
top: flashTop,
|
|
12765
|
+
height: flashHeight
|
|
12766
|
+
}
|
|
12767
|
+
}, flashKey),
|
|
12768
|
+
hoveredBlock !== void 0 && blockOfPair[hoveredBlock] !== void 0 && (0, react_jsx_runtime.jsxs)("div", {
|
|
12769
|
+
className: PendingPanel_module_css_default.blockActions,
|
|
12770
|
+
"data-diff-block-actions": true,
|
|
12771
|
+
style: { top: blockActionsTop },
|
|
12772
|
+
children: [
|
|
12773
|
+
(0, react_jsx_runtime.jsx)("span", {
|
|
12774
|
+
className: PendingPanel_module_css_default.blockPosition,
|
|
12775
|
+
"data-diff-block-position": true,
|
|
12776
|
+
children: t("panel.blockPosition", {
|
|
12777
|
+
current: hoveredBlock + 1,
|
|
12778
|
+
total: blockOfPair.length
|
|
12779
|
+
})
|
|
12780
|
+
}),
|
|
12781
|
+
(0, react_jsx_runtime.jsx)("button", {
|
|
12782
|
+
type: "button",
|
|
12783
|
+
className: `${PendingPanel_module_css_default.action} ${PendingPanel_module_css_default.iconAction}`,
|
|
12784
|
+
"data-diff-block-prev": true,
|
|
12785
|
+
"aria-label": t("action.prevDiff"),
|
|
12786
|
+
disabled: busy,
|
|
12787
|
+
onClick: () => jump(-1),
|
|
12788
|
+
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronUpOutline14, { size: 14 })
|
|
12789
|
+
}),
|
|
12790
|
+
(0, react_jsx_runtime.jsx)("button", {
|
|
12791
|
+
type: "button",
|
|
12792
|
+
className: `${PendingPanel_module_css_default.action} ${PendingPanel_module_css_default.iconAction}`,
|
|
12793
|
+
"data-diff-block-next": true,
|
|
12794
|
+
"aria-label": t("action.nextDiff"),
|
|
12795
|
+
disabled: busy,
|
|
12796
|
+
onClick: () => jump(1),
|
|
12797
|
+
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, { size: 14 })
|
|
12798
|
+
}),
|
|
12799
|
+
(0, react_jsx_runtime.jsx)("button", {
|
|
12800
|
+
type: "button",
|
|
12801
|
+
className: `${PendingPanel_module_css_default.action} ${PendingPanel_module_css_default.actionPrimary}`,
|
|
12802
|
+
"data-diff-block-keep": true,
|
|
12803
|
+
disabled: busy,
|
|
12804
|
+
onClick: () => {
|
|
12805
|
+
handleBlockAction("keep");
|
|
12806
|
+
},
|
|
12807
|
+
children: t("action.keep")
|
|
12808
|
+
}),
|
|
12809
|
+
(0, react_jsx_runtime.jsx)("button", {
|
|
12810
|
+
type: "button",
|
|
12811
|
+
className: `${PendingPanel_module_css_default.action}`,
|
|
12812
|
+
"data-diff-block-revert": true,
|
|
12813
|
+
disabled: busy,
|
|
12814
|
+
onClick: () => {
|
|
12815
|
+
handleBlockAction("revert");
|
|
12816
|
+
},
|
|
12817
|
+
children: t("action.revert")
|
|
12818
|
+
})
|
|
12819
|
+
]
|
|
12820
|
+
})
|
|
12821
|
+
]
|
|
12822
|
+
});
|
|
12823
|
+
});
|
|
12152
12824
|
/** The diff-row index containing a node, or undefined. */
|
|
12153
12825
|
function rowIndexAt(node) {
|
|
12154
12826
|
if (node === null) return void 0;
|
|
@@ -12157,6 +12829,43 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
12157
12829
|
const index = Number(row.dataset.diffRow);
|
|
12158
12830
|
return Number.isFinite(index) ? index : void 0;
|
|
12159
12831
|
}
|
|
12832
|
+
/** The split pair index and which side (left=old, right=new) a node sits in, or undefined. */
|
|
12833
|
+
function splitRowInfoAt(node) {
|
|
12834
|
+
if (node === null) return void 0;
|
|
12835
|
+
const row = (node instanceof Element ? node : node.parentElement)?.closest("[data-diff-split-row]");
|
|
12836
|
+
if (row === null || row === void 0) return void 0;
|
|
12837
|
+
const side = row.dataset.diffSplitSide;
|
|
12838
|
+
if (side !== "left" && side !== "right") return void 0;
|
|
12839
|
+
const index = Number(row.dataset.diffSplitIndex);
|
|
12840
|
+
if (!Number.isFinite(index)) return void 0;
|
|
12841
|
+
return {
|
|
12842
|
+
pairIndex: index,
|
|
12843
|
+
side: side === "left" ? "old" : "new"
|
|
12844
|
+
};
|
|
12845
|
+
}
|
|
12846
|
+
/**
|
|
12847
|
+
* Derive the selected split pair range per side (a left-column selection
|
|
12848
|
+
* references the old file, a right-column selection the new file). A selection
|
|
12849
|
+
* spanning the divider (both sides) references two files, so it is rejected.
|
|
12850
|
+
*/
|
|
12851
|
+
function splitRowRangeOf(selection) {
|
|
12852
|
+
if (selection === null || selection.isCollapsed || selection.rangeCount === 0) return void 0;
|
|
12853
|
+
const range = selection.getRangeAt(0);
|
|
12854
|
+
const startInfo = splitRowInfoAt(range.startContainer);
|
|
12855
|
+
const endInfo = splitRowInfoAt(range.endContainer);
|
|
12856
|
+
if (startInfo === void 0 || endInfo === void 0) return void 0;
|
|
12857
|
+
if (startInfo.side !== endInfo.side) return void 0;
|
|
12858
|
+
let start = startInfo.pairIndex;
|
|
12859
|
+
let end = endInfo.pairIndex;
|
|
12860
|
+
if (lineOffsetAt(range.startContainer, range.startOffset) >= lineLengthAt(range.startContainer)) start += 1;
|
|
12861
|
+
if (lineOffsetAt(range.endContainer, range.endOffset) === 0) end -= 1;
|
|
12862
|
+
if (start > end) return void 0;
|
|
12863
|
+
return {
|
|
12864
|
+
start,
|
|
12865
|
+
end,
|
|
12866
|
+
side: startInfo.side
|
|
12867
|
+
};
|
|
12868
|
+
}
|
|
12160
12869
|
/** Character offset of a selection boundary within its line's code text. */
|
|
12161
12870
|
function lineOffsetAt(node, offset) {
|
|
12162
12871
|
const code = (node instanceof Element ? node : node.parentElement)?.closest("[data-diff-code]");
|
|
@@ -12282,6 +12991,8 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
12282
12991
|
setWrapEnabled(wrapKey, next);
|
|
12283
12992
|
};
|
|
12284
12993
|
const [tabWidthSpaces] = (0, react.useState)(() => tabWidth());
|
|
12994
|
+
const splitView = splitMode();
|
|
12995
|
+
const splitDiffRef = (0, react.useRef)(null);
|
|
12285
12996
|
const model = (0, react.useMemo)(() => {
|
|
12286
12997
|
const diff = computeWholeFileDiff(file.oldText, file.newText);
|
|
12287
12998
|
return {
|
|
@@ -12289,6 +13000,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
12289
13000
|
blocks: changeBlocksOf(diff)
|
|
12290
13001
|
};
|
|
12291
13002
|
}, [file.oldText, file.newText]);
|
|
13003
|
+
const splitPairs = (0, react.useMemo)(() => splitView ? computeSideBySideDiff(model.diff.rows).pairs : null, [splitView, model]);
|
|
12292
13004
|
const rulerMarkers = (0, react.useMemo)(() => {
|
|
12293
13005
|
const rows = model.diff.rows;
|
|
12294
13006
|
const total = rows.length;
|
|
@@ -12548,6 +13260,15 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
12548
13260
|
setScrollTick((tick) => tick + 1);
|
|
12549
13261
|
setFlashKey((key) => key + 1);
|
|
12550
13262
|
};
|
|
13263
|
+
const jumpBlock = (direction) => {
|
|
13264
|
+
if (splitView) {
|
|
13265
|
+
splitDiffRef.current?.jump(direction);
|
|
13266
|
+
return;
|
|
13267
|
+
}
|
|
13268
|
+
jump(direction);
|
|
13269
|
+
};
|
|
13270
|
+
const jumpBlockRef = (0, react.useRef)(jumpBlock);
|
|
13271
|
+
jumpBlockRef.current = jumpBlock;
|
|
12551
13272
|
const stepBlock = (direction) => {
|
|
12552
13273
|
const count = model.blocks.length;
|
|
12553
13274
|
if (count === 0) return;
|
|
@@ -12557,9 +13278,22 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
12557
13278
|
setScrollTick((tick) => tick + 1);
|
|
12558
13279
|
setFlashKey((key) => key + 1);
|
|
12559
13280
|
};
|
|
13281
|
+
const handleBlockAction = async (action) => {
|
|
13282
|
+
if (busy || hoveredBlock === void 0) return;
|
|
13283
|
+
const operated = hoveredBlock;
|
|
13284
|
+
const range = blockRanges[operated];
|
|
13285
|
+
await (action === "keep" ? onBlockKeep(file.sessionId, file.id, range) : onBlockRevert(file.sessionId, file.id, range));
|
|
13286
|
+
const count = model.blocks.length;
|
|
13287
|
+
if (count === 0) return;
|
|
13288
|
+
const next = Math.max(0, Math.min(operated, count - 1));
|
|
13289
|
+
setFocus(next);
|
|
13290
|
+
setHoveredBlock(void 0);
|
|
13291
|
+
setScrollTick((tick) => tick + 1);
|
|
13292
|
+
setFlashKey((key) => key + 1);
|
|
13293
|
+
};
|
|
12560
13294
|
(0, react.useEffect)(() => {
|
|
12561
13295
|
if (jumpSignal === 0) return;
|
|
12562
|
-
|
|
13296
|
+
jumpBlock(1);
|
|
12563
13297
|
}, [jumpSignal]);
|
|
12564
13298
|
const onScroll = () => {
|
|
12565
13299
|
const body = bodyRef.current;
|
|
@@ -12568,15 +13302,27 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
12568
13302
|
setViewportHeight(body.clientHeight);
|
|
12569
13303
|
};
|
|
12570
13304
|
(0, react.useEffect)(() => {
|
|
12571
|
-
const update = () => setSelection(rowRangeOf(window.getSelection()));
|
|
13305
|
+
const update = () => setSelection(splitView ? splitRowRangeOf(window.getSelection()) : rowRangeOf(window.getSelection()));
|
|
12572
13306
|
document.addEventListener("selectionchange", update);
|
|
12573
13307
|
update();
|
|
12574
13308
|
return () => {
|
|
12575
13309
|
document.removeEventListener("selectionchange", update);
|
|
12576
13310
|
};
|
|
12577
|
-
}, [file.id]);
|
|
13311
|
+
}, [file.id, splitView]);
|
|
12578
13312
|
const selectionReference = (() => {
|
|
12579
13313
|
if (selection === void 0) return void 0;
|
|
13314
|
+
if (splitView) {
|
|
13315
|
+
if (selection.side === void 0 || splitPairs === null) return void 0;
|
|
13316
|
+
const lineNumbers = [];
|
|
13317
|
+
for (let index = selection.start; index <= selection.end; index++) {
|
|
13318
|
+
const pair = splitPairs[index];
|
|
13319
|
+
if (pair === void 0) continue;
|
|
13320
|
+
const line = selection.side === "old" ? pair.left?.line : pair.right?.line;
|
|
13321
|
+
if (line !== void 0) lineNumbers.push(line);
|
|
13322
|
+
}
|
|
13323
|
+
if (lineNumbers.length === 0) return void 0;
|
|
13324
|
+
return referenceOf(file.path, workspacePath, Math.min(...lineNumbers), Math.max(...lineNumbers));
|
|
13325
|
+
}
|
|
12580
13326
|
const lineNumbers = model.diff.rows.slice(selection.start, selection.end + 1).map((row) => row.newLine).filter((number) => number !== void 0);
|
|
12581
13327
|
if (lineNumbers.length === 0) return void 0;
|
|
12582
13328
|
return referenceOf(file.path, workspacePath, Math.min(...lineNumbers), Math.max(...lineNumbers));
|
|
@@ -12612,6 +13358,10 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
12612
13358
|
if (!(event.ctrlKey || event.metaKey) || event.altKey || event.shiftKey) return;
|
|
12613
13359
|
if (event.key.toLowerCase() !== "f") return;
|
|
12614
13360
|
event.preventDefault();
|
|
13361
|
+
if (splitView) {
|
|
13362
|
+
splitDiffRef.current?.openSearch();
|
|
13363
|
+
return;
|
|
13364
|
+
}
|
|
12615
13365
|
setSearchOpen(true);
|
|
12616
13366
|
searchInputRef.current?.focus();
|
|
12617
13367
|
searchInputRef.current?.select();
|
|
@@ -12621,8 +13371,8 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
12621
13371
|
window.removeEventListener("keydown", onKeyDown, true);
|
|
12622
13372
|
};
|
|
12623
13373
|
}, []);
|
|
12624
|
-
const jumpRef = (0, react.useRef)(
|
|
12625
|
-
jumpRef.current =
|
|
13374
|
+
const jumpRef = (0, react.useRef)(jumpBlock);
|
|
13375
|
+
jumpRef.current = jumpBlock;
|
|
12626
13376
|
(0, react.useEffect)(() => {
|
|
12627
13377
|
const onKeyDown = (event) => {
|
|
12628
13378
|
if (!(event.ctrlKey || event.metaKey) || event.altKey || event.shiftKey) return;
|
|
@@ -12730,7 +13480,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
12730
13480
|
"aria-label": t("action.prevDiff"),
|
|
12731
13481
|
disabled: busy,
|
|
12732
13482
|
onClick: () => {
|
|
12733
|
-
|
|
13483
|
+
jumpBlock(-1);
|
|
12734
13484
|
},
|
|
12735
13485
|
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronUpOutline14, { size: 14 })
|
|
12736
13486
|
})
|
|
@@ -12745,7 +13495,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
12745
13495
|
"aria-label": t("action.nextDiff"),
|
|
12746
13496
|
disabled: busy,
|
|
12747
13497
|
onClick: () => {
|
|
12748
|
-
|
|
13498
|
+
jumpBlock(1);
|
|
12749
13499
|
},
|
|
12750
13500
|
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, { size: 14 })
|
|
12751
13501
|
})
|
|
@@ -12795,7 +13545,18 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
12795
13545
|
className: PendingPanel_module_css_default.missingHint,
|
|
12796
13546
|
children: t("panel.missingHint")
|
|
12797
13547
|
}),
|
|
12798
|
-
(0, react_jsx_runtime.
|
|
13548
|
+
splitView ? (0, react_jsx_runtime.jsx)(SplitDiff, {
|
|
13549
|
+
ref: splitDiffRef,
|
|
13550
|
+
file,
|
|
13551
|
+
model,
|
|
13552
|
+
runs,
|
|
13553
|
+
langWrap,
|
|
13554
|
+
tabWidthSpaces,
|
|
13555
|
+
busy,
|
|
13556
|
+
t,
|
|
13557
|
+
onBlockKeep,
|
|
13558
|
+
onBlockRevert
|
|
13559
|
+
}) : (0, react_jsx_runtime.jsxs)("div", {
|
|
12799
13560
|
className: PendingPanel_module_css_default.diffBodyWrap,
|
|
12800
13561
|
children: [
|
|
12801
13562
|
(0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -12880,7 +13641,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
12880
13641
|
"data-diff-block-keep": true,
|
|
12881
13642
|
disabled: busy,
|
|
12882
13643
|
onClick: () => {
|
|
12883
|
-
|
|
13644
|
+
handleBlockAction("keep");
|
|
12884
13645
|
},
|
|
12885
13646
|
children: t("action.keep")
|
|
12886
13647
|
}),
|
|
@@ -12890,7 +13651,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
12890
13651
|
"data-diff-block-revert": true,
|
|
12891
13652
|
disabled: busy,
|
|
12892
13653
|
onClick: () => {
|
|
12893
|
-
|
|
13654
|
+
handleBlockAction("revert");
|
|
12894
13655
|
},
|
|
12895
13656
|
children: t("action.revert")
|
|
12896
13657
|
})
|
|
@@ -13727,6 +14488,8 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
13727
14488
|
const [includeUntrackedOpen, setIncludeUntrackedOpen] = (0, react.useState)(false);
|
|
13728
14489
|
const [tab, setTabState] = (0, react.useState)(tabWidth);
|
|
13729
14490
|
const [tabOpen, setTabOpen] = (0, react.useState)(false);
|
|
14491
|
+
const [split, setSplitState] = (0, react.useState)(splitMode);
|
|
14492
|
+
const [splitOpen, setSplitOpen] = (0, react.useState)(false);
|
|
13730
14493
|
const setPasteOnCopy = (value) => {
|
|
13731
14494
|
setPasteOnCopyState(value);
|
|
13732
14495
|
setPasteOnCopyEnabled(value);
|
|
@@ -13739,6 +14502,10 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
13739
14502
|
setTabState(value);
|
|
13740
14503
|
setTabWidth(value);
|
|
13741
14504
|
};
|
|
14505
|
+
const setSplit = (value) => {
|
|
14506
|
+
setSplitState(value);
|
|
14507
|
+
setSplitMode(value);
|
|
14508
|
+
};
|
|
13742
14509
|
return (0, react_jsx_runtime.jsxs)("div", {
|
|
13743
14510
|
className: PendingPanel_module_css_default.settingsPage,
|
|
13744
14511
|
"data-diff-settings": true,
|
|
@@ -13771,6 +14538,16 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
13771
14538
|
onOpenChange: setTabOpen,
|
|
13772
14539
|
onSelect: setTab,
|
|
13773
14540
|
dataAttribute: "data-diff-tab-width-select"
|
|
14541
|
+
}),
|
|
14542
|
+
(0, react_jsx_runtime.jsx)(PreferenceRow, {
|
|
14543
|
+
title: t("panel.splitMode"),
|
|
14544
|
+
description: t("panel.splitModeDesc"),
|
|
14545
|
+
value: split,
|
|
14546
|
+
open: splitOpen,
|
|
14547
|
+
onOpenChange: setSplitOpen,
|
|
14548
|
+
onSelect: setSplit,
|
|
14549
|
+
dataAttribute: "data-diff-split-mode-select",
|
|
14550
|
+
t
|
|
13774
14551
|
})
|
|
13775
14552
|
]
|
|
13776
14553
|
});
|
|
@@ -14169,8 +14946,10 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
14169
14946
|
"panel.pasteOnCopyDesc": "开启后,复制的引用会自动填入消息输入框,输入框会获得焦点。",
|
|
14170
14947
|
"panel.importUntracked": "导入未跟踪的文件改动",
|
|
14171
14948
|
"panel.importUntrackedDesc": "开启后,导入工作区改动会包含未跟踪/未版本化的新增文件(Git 未跟踪、SVN 未版本化、Perforce 未版本化)。收集改动需要扫描整个工作区,文件数量大时可能较慢。",
|
|
14172
|
-
"panel.tabWidth": "
|
|
14173
|
-
"panel.tabWidthDesc": "
|
|
14949
|
+
"panel.tabWidth": "制表符宽度",
|
|
14950
|
+
"panel.tabWidthDesc": "差异中制表符的缩进宽度,可选 2 / 4 / 8 个空格(默认 4),同时作用于行宽折行测量。",
|
|
14951
|
+
"panel.splitMode": "双栏对比",
|
|
14952
|
+
"panel.splitModeDesc": "开启后整文件差异用左「改前」|右「当前」双栏、逐行对齐的视图展示,默认关闭使用单栏(合并)视图。",
|
|
14174
14953
|
"settings.tabLabel": "改动审批",
|
|
14175
14954
|
"row.create": "新增文件",
|
|
14176
14955
|
"row.failed": "失败",
|
|
@@ -14233,7 +15012,9 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
14233
15012
|
"panel.importUntracked": "Import changes to untracked files",
|
|
14234
15013
|
"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.",
|
|
14235
15014
|
"panel.tabWidth": "Tab width",
|
|
14236
|
-
"panel.tabWidthDesc": "The indent width of a tab character in the
|
|
15015
|
+
"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.",
|
|
15016
|
+
"panel.splitMode": "Side-by-side view",
|
|
15017
|
+
"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.",
|
|
14237
15018
|
"settings.tabLabel": "Diff Approval",
|
|
14238
15019
|
"row.create": "New file",
|
|
14239
15020
|
"row.failed": "Failed",
|
|
@@ -14284,10 +15065,64 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
14284
15065
|
"sessions"
|
|
14285
15066
|
];
|
|
14286
15067
|
/**
|
|
15068
|
+
* The dsh web-react renderer gives `div[data-slot="sidebar.footer.action"]` an
|
|
15069
|
+
* inline `display: contents`, so every plugin's footer entry root participates
|
|
15070
|
+
* directly in the `.footerActions` flex row — several plugins (e.g. this one
|
|
15071
|
+
* plus a file-browser) get crammed into one row and a full-width badge
|
|
15072
|
+
* overflows. Stack the slot entries vertically instead (the same fix the
|
|
15073
|
+
* dsh-footer-order plugin injects), so each plugin gets its own row.
|
|
15074
|
+
*
|
|
15075
|
+
* We defer to the dedicated dsh-footer-order plugin when it is present, so the
|
|
15076
|
+
* two never fight.
|
|
15077
|
+
*
|
|
15078
|
+
* Why we detect it by its injected stylesheet's `--dsh-footer-order-gap` custom
|
|
15079
|
+
* property rather than by `ctx.slots.entriesOfSlot('settings.plugin.item')`
|
|
15080
|
+
* (where footer-order registers its settings card, id `footer-order`): that slot
|
|
15081
|
+
* key is not in our SlotMap type (would need a cast) and, more importantly, it
|
|
15082
|
+
* may not be an active slot in this dsh version at all — footer-order targets a
|
|
15083
|
+
* newer dsh settings API, so the detection could miss footer-order and we would
|
|
15084
|
+
* then both inject, fighting over the same `!important` rule. The CSS marker is
|
|
15085
|
+
* footer-order's own, and its presence is exactly "footer-order's rule is really
|
|
15086
|
+
* applied", which is what decides the layout. So only when the marker is absent
|
|
15087
|
+
* do we inject our own rule.
|
|
15088
|
+
*/
|
|
15089
|
+
function injectFooterStackStyle() {
|
|
15090
|
+
if (typeof document === "undefined") return;
|
|
15091
|
+
if (document.querySelector("style[data-diff-approval-footer-stack]") !== null) return;
|
|
15092
|
+
const CSS = "div[data-slot=\"sidebar.footer.action\"]{display:flex!important;flex-direction:column!important;flex:1 1 auto!important;align-items:stretch!important;}";
|
|
15093
|
+
const footerOrderInstalled = () => {
|
|
15094
|
+
for (const style of document.querySelectorAll("style")) if ((style.textContent ?? "").includes("--dsh-footer-order-gap")) return true;
|
|
15095
|
+
return false;
|
|
15096
|
+
};
|
|
15097
|
+
const inject = () => {
|
|
15098
|
+
if (document.querySelector("style[data-diff-approval-footer-stack]") !== null) return;
|
|
15099
|
+
if (footerOrderInstalled()) return;
|
|
15100
|
+
const style = document.createElement("style");
|
|
15101
|
+
style.setAttribute("data-diff-approval-footer-stack", "");
|
|
15102
|
+
style.textContent = CSS;
|
|
15103
|
+
document.head.appendChild(style);
|
|
15104
|
+
};
|
|
15105
|
+
if (document.querySelector("div[data-slot=\"sidebar.footer.action\"]") !== null) {
|
|
15106
|
+
inject();
|
|
15107
|
+
return;
|
|
15108
|
+
}
|
|
15109
|
+
const observer = new MutationObserver(() => {
|
|
15110
|
+
if (document.querySelector("div[data-slot=\"sidebar.footer.action\"]") !== null) {
|
|
15111
|
+
observer.disconnect();
|
|
15112
|
+
inject();
|
|
15113
|
+
}
|
|
15114
|
+
});
|
|
15115
|
+
observer.observe(document.documentElement, {
|
|
15116
|
+
childList: true,
|
|
15117
|
+
subtree: true
|
|
15118
|
+
});
|
|
15119
|
+
}
|
|
15120
|
+
/**
|
|
14287
15121
|
* Mount the pending-edit review panel.
|
|
14288
15122
|
* @param ctx - Client Cordis context carrying the wire and slot services.
|
|
14289
15123
|
*/
|
|
14290
15124
|
function apply(ctx) {
|
|
15125
|
+
injectFooterStackStyle();
|
|
14291
15126
|
ctx.effect(() => ctx.locale.register(NS, {
|
|
14292
15127
|
zh,
|
|
14293
15128
|
en
|