dsh-diff-approval 0.16.0 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -11
- package/README.zh.md +15 -11
- package/docs/images/file-list-collapsed.png +0 -0
- package/docs/images/file-list-collapsed.zh.png +0 -0
- package/docs/images/fullscreen.png +0 -0
- package/docs/images/fullscreen.zh.png +0 -0
- package/docs/images/pending-panel.png +0 -0
- package/docs/images/pending-panel.zh.png +0 -0
- package/lib/client.js +1046 -304
- package/lib/types/client/PendingPanel.d.ts +4 -1
- package/lib/types/client/conversation-access.d.ts +9 -0
- package/lib/types/client/locales.d.ts +34 -0
- package/lib/types/client/produced-diff.d.ts +27 -0
- package/lib/types/client/reference.d.ts +11 -0
- package/lib/types/client/settings.d.ts +7 -0
- package/lib/types/client/whole-file-diff.d.ts +9 -0
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -667,6 +667,143 @@ window.__ModuleLoader__.load({
|
|
|
667
667
|
const normalized = text.replace(/\r\n?/g, "\n");
|
|
668
668
|
return normalized.endsWith("\n") ? normalized.slice(0, -1) : normalized;
|
|
669
669
|
}
|
|
670
|
+
/** A changed middle larger than this many old×new line cells falls back to the
|
|
671
|
+
* greedy anchor alignment: the `diff` package's O((N+M)·D) Myers scan (and its
|
|
672
|
+
* high constant) would otherwise stall on a big, heavily-edited region for no
|
|
673
|
+
* benefit. */
|
|
674
|
+
const MIDDLE_CELL_CAP = 1e6;
|
|
675
|
+
/** A fast, greedy line alignment for a large changed middle. Lines present on
|
|
676
|
+
* both sides (in order) become context; a line only on one side is del/add;
|
|
677
|
+
* two unmatched side-by-side lines are a replacement. It is O(N+M) (amortized,
|
|
678
|
+
* with per-line index queues) and recognises unchanged lines as context, so a
|
|
679
|
+
* big scattered file does not get marked as all-changed — it only gives up the
|
|
680
|
+
* minimal-edit property Myers would provide. */
|
|
681
|
+
function alignLargeMiddle(midOld, midNew, start) {
|
|
682
|
+
const rows = [];
|
|
683
|
+
let removed = 0;
|
|
684
|
+
let added = 0;
|
|
685
|
+
const newPos = /* @__PURE__ */ new Map();
|
|
686
|
+
const oldPos = /* @__PURE__ */ new Map();
|
|
687
|
+
midNew.forEach((line, index) => {
|
|
688
|
+
const q = newPos.get(line);
|
|
689
|
+
if (q) q.push(index);
|
|
690
|
+
else newPos.set(line, [index]);
|
|
691
|
+
});
|
|
692
|
+
midOld.forEach((line, index) => {
|
|
693
|
+
const q = oldPos.get(line);
|
|
694
|
+
if (q) q.push(index);
|
|
695
|
+
else oldPos.set(line, [index]);
|
|
696
|
+
});
|
|
697
|
+
let oldLine = start;
|
|
698
|
+
let newLine = start;
|
|
699
|
+
let i = 0;
|
|
700
|
+
let j = 0;
|
|
701
|
+
while (i < midOld.length && j < midNew.length) {
|
|
702
|
+
if (midOld[i] === midNew[j]) {
|
|
703
|
+
rows.push({
|
|
704
|
+
kind: "context",
|
|
705
|
+
text: midOld[i],
|
|
706
|
+
oldLine: ++oldLine,
|
|
707
|
+
newLine: ++newLine
|
|
708
|
+
});
|
|
709
|
+
i++;
|
|
710
|
+
j++;
|
|
711
|
+
continue;
|
|
712
|
+
}
|
|
713
|
+
const nxtNew = newPos.get(midOld[i])?.find((index) => index >= j);
|
|
714
|
+
const nxtOld = oldPos.get(midNew[j])?.find((index) => index >= i);
|
|
715
|
+
if (nxtNew !== void 0 && (nxtOld === void 0 || nxtNew - j <= nxtOld - i)) {
|
|
716
|
+
for (let k = j; k < nxtNew; k++) {
|
|
717
|
+
rows.push({
|
|
718
|
+
kind: "add",
|
|
719
|
+
text: midNew[k],
|
|
720
|
+
oldLine: void 0,
|
|
721
|
+
newLine: ++newLine
|
|
722
|
+
});
|
|
723
|
+
added++;
|
|
724
|
+
}
|
|
725
|
+
j = nxtNew;
|
|
726
|
+
} else if (nxtOld !== void 0) {
|
|
727
|
+
for (let k = i; k < nxtOld; k++) {
|
|
728
|
+
rows.push({
|
|
729
|
+
kind: "del",
|
|
730
|
+
text: midOld[k],
|
|
731
|
+
oldLine: ++oldLine,
|
|
732
|
+
newLine: void 0
|
|
733
|
+
});
|
|
734
|
+
removed++;
|
|
735
|
+
}
|
|
736
|
+
i = nxtOld;
|
|
737
|
+
} else {
|
|
738
|
+
rows.push({
|
|
739
|
+
kind: "del",
|
|
740
|
+
text: midOld[i],
|
|
741
|
+
oldLine: ++oldLine,
|
|
742
|
+
newLine: void 0
|
|
743
|
+
});
|
|
744
|
+
removed++;
|
|
745
|
+
i++;
|
|
746
|
+
rows.push({
|
|
747
|
+
kind: "add",
|
|
748
|
+
text: midNew[j],
|
|
749
|
+
oldLine: void 0,
|
|
750
|
+
newLine: ++newLine
|
|
751
|
+
});
|
|
752
|
+
added++;
|
|
753
|
+
j++;
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
while (i < midOld.length) {
|
|
757
|
+
rows.push({
|
|
758
|
+
kind: "del",
|
|
759
|
+
text: midOld[i],
|
|
760
|
+
oldLine: ++oldLine,
|
|
761
|
+
newLine: void 0
|
|
762
|
+
});
|
|
763
|
+
removed++;
|
|
764
|
+
i++;
|
|
765
|
+
}
|
|
766
|
+
while (j < midNew.length) {
|
|
767
|
+
rows.push({
|
|
768
|
+
kind: "add",
|
|
769
|
+
text: midNew[j],
|
|
770
|
+
oldLine: void 0,
|
|
771
|
+
newLine: ++newLine
|
|
772
|
+
});
|
|
773
|
+
added++;
|
|
774
|
+
j++;
|
|
775
|
+
}
|
|
776
|
+
return {
|
|
777
|
+
rows,
|
|
778
|
+
removed,
|
|
779
|
+
added
|
|
780
|
+
};
|
|
781
|
+
}
|
|
782
|
+
/** Reorder each contiguous change run (a maximal run of non-context rows) into
|
|
783
|
+
* the standard unified-diff shape — all `del` rows first, then all `add` rows.
|
|
784
|
+
* The diff package's Myers scan can emit per-line replacements (`del add del
|
|
785
|
+
* add`) for some content, which reads as interleaved in the viewer and makes
|
|
786
|
+
* the split view pair each line separately. Reordering a run to del-block→add-
|
|
787
|
+
* block keeps each row's own line numbers and never changes the run's row
|
|
788
|
+
* boundaries, so change blocks, split alignment, and keep/revert ranges stay
|
|
789
|
+
* correct — it only tidies the display order. */
|
|
790
|
+
function normalizeChangeRuns(rows) {
|
|
791
|
+
for (let i = 0; i < rows.length;) {
|
|
792
|
+
if (rows[i].kind === "context") {
|
|
793
|
+
i++;
|
|
794
|
+
continue;
|
|
795
|
+
}
|
|
796
|
+
const start = i;
|
|
797
|
+
while (i < rows.length && rows[i].kind !== "context") i++;
|
|
798
|
+
const run = rows.slice(start, i);
|
|
799
|
+
const dels = run.filter((row) => row.kind === "del");
|
|
800
|
+
const adds = run.filter((row) => row.kind === "add");
|
|
801
|
+
if (dels.length !== 0 && adds.length !== 0) {
|
|
802
|
+
rows.splice(start, run.length);
|
|
803
|
+
rows.splice(start, 0, ...dels, ...adds);
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
}
|
|
670
807
|
function computeWholeFileDiff(oldText, newText) {
|
|
671
808
|
const oldNorm = oldText.replace(/\r\n?/g, "\n");
|
|
672
809
|
const newNorm = newText.replace(/\r\n?/g, "\n");
|
|
@@ -682,43 +819,94 @@ window.__ModuleLoader__.load({
|
|
|
682
819
|
};
|
|
683
820
|
const oldLines = contentLines(oldNorm);
|
|
684
821
|
const newLines = contentLines(newNorm);
|
|
685
|
-
|
|
822
|
+
let start = 0;
|
|
823
|
+
const minLen = Math.min(oldLines.length, newLines.length);
|
|
824
|
+
while (start < minLen && oldLines[start] === newLines[start]) start++;
|
|
825
|
+
let endOld = oldLines.length;
|
|
826
|
+
let endNew = newLines.length;
|
|
827
|
+
while (start < endOld && start < endNew && oldLines[endOld - 1] === newLines[endNew - 1]) {
|
|
828
|
+
endOld--;
|
|
829
|
+
endNew--;
|
|
830
|
+
}
|
|
686
831
|
const rows = [];
|
|
687
832
|
let removed = 0;
|
|
688
833
|
let added = 0;
|
|
689
|
-
let
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
834
|
+
for (let i = 0; i < start; i++) rows.push({
|
|
835
|
+
kind: "context",
|
|
836
|
+
text: oldLines[i],
|
|
837
|
+
oldLine: i + 1,
|
|
838
|
+
newLine: i + 1
|
|
839
|
+
});
|
|
840
|
+
const midOld = oldLines.slice(start, endOld);
|
|
841
|
+
const midNew = newLines.slice(start, endNew);
|
|
842
|
+
if (midOld.length === 0 && midNew.length === 0) {} else if (midOld.length === 0) for (let j = 0; j < midNew.length; j++) {
|
|
843
|
+
rows.push({
|
|
844
|
+
kind: "add",
|
|
845
|
+
text: midNew[j],
|
|
846
|
+
oldLine: void 0,
|
|
847
|
+
newLine: start + j + 1
|
|
848
|
+
});
|
|
849
|
+
added++;
|
|
850
|
+
}
|
|
851
|
+
else if (midNew.length === 0) for (let i = 0; i < midOld.length; i++) {
|
|
852
|
+
rows.push({
|
|
853
|
+
kind: "del",
|
|
854
|
+
text: midOld[i],
|
|
855
|
+
oldLine: start + i + 1,
|
|
856
|
+
newLine: void 0
|
|
857
|
+
});
|
|
858
|
+
removed++;
|
|
859
|
+
}
|
|
860
|
+
else if (midOld.length * midNew.length > MIDDLE_CELL_CAP) {
|
|
861
|
+
const aligned = alignLargeMiddle(midOld, midNew, start);
|
|
862
|
+
rows.push(...aligned.rows);
|
|
863
|
+
removed += aligned.removed;
|
|
864
|
+
added += aligned.added;
|
|
865
|
+
} else {
|
|
866
|
+
const context = Math.max(1, midOld.length, midNew.length);
|
|
867
|
+
const patch = structuredPatch("", "", midOld.join("\n"), midNew.join("\n"), void 0, void 0, { context });
|
|
868
|
+
let oldLine = start;
|
|
869
|
+
let newLine = start;
|
|
870
|
+
for (const hunk of patch.hunks) for (const line of hunk.lines) {
|
|
871
|
+
if (line.startsWith("\\")) continue;
|
|
872
|
+
if (line.startsWith("-")) {
|
|
873
|
+
oldLine++;
|
|
874
|
+
rows.push({
|
|
875
|
+
kind: "del",
|
|
876
|
+
text: line.slice(1),
|
|
877
|
+
oldLine,
|
|
878
|
+
newLine: void 0
|
|
879
|
+
});
|
|
880
|
+
removed++;
|
|
881
|
+
} else if (line.startsWith("+")) {
|
|
882
|
+
newLine++;
|
|
883
|
+
rows.push({
|
|
884
|
+
kind: "add",
|
|
885
|
+
text: line.slice(1),
|
|
886
|
+
oldLine: void 0,
|
|
887
|
+
newLine
|
|
888
|
+
});
|
|
889
|
+
added++;
|
|
890
|
+
} else {
|
|
891
|
+
oldLine++;
|
|
892
|
+
newLine++;
|
|
893
|
+
rows.push({
|
|
894
|
+
kind: "context",
|
|
895
|
+
text: line.slice(1),
|
|
896
|
+
oldLine,
|
|
897
|
+
newLine
|
|
898
|
+
});
|
|
899
|
+
}
|
|
720
900
|
}
|
|
721
901
|
}
|
|
902
|
+
const suffixLen = oldLines.length - endOld;
|
|
903
|
+
for (let k = 0; k < suffixLen; k++) rows.push({
|
|
904
|
+
kind: "context",
|
|
905
|
+
text: oldLines[endOld + k],
|
|
906
|
+
oldLine: endOld + k + 1,
|
|
907
|
+
newLine: endNew + k + 1
|
|
908
|
+
});
|
|
909
|
+
normalizeChangeRuns(rows);
|
|
722
910
|
return {
|
|
723
911
|
rows,
|
|
724
912
|
removed,
|
|
@@ -12015,16 +12203,17 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
12015
12203
|
return start === end ? String(start) : `${start}-${end}`;
|
|
12016
12204
|
}
|
|
12017
12205
|
/**
|
|
12018
|
-
* Build the
|
|
12019
|
-
*
|
|
12206
|
+
* Build the bare reference label for a selected line range — `path:range` with
|
|
12207
|
+
* no surrounding parentheses. Used for display (the status-bar copy control
|
|
12208
|
+
* shows the reference without the token-wrapping parens).
|
|
12020
12209
|
* @param path - the selected file's path.
|
|
12021
12210
|
* @param workspacePath - the current workspace root, or `undefined`.
|
|
12022
12211
|
* @param start - first selected line number.
|
|
12023
12212
|
* @param end - last selected line number.
|
|
12024
|
-
* @returns the `
|
|
12213
|
+
* @returns the `path:range` reference label.
|
|
12025
12214
|
*/
|
|
12026
|
-
function
|
|
12027
|
-
return
|
|
12215
|
+
function referenceLabelOf(path, workspacePath, start, end) {
|
|
12216
|
+
return `${referencePathOf(path, workspacePath)}:${lineRangeLabel(start, end)}`;
|
|
12028
12217
|
}
|
|
12029
12218
|
/**
|
|
12030
12219
|
* Map one referenced line range from `oldContent` coordinates to `newContent`
|
|
@@ -12091,6 +12280,128 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
12091
12280
|
});
|
|
12092
12281
|
}
|
|
12093
12282
|
//#endregion
|
|
12283
|
+
//#region lib/types/client/produced-diff.js
|
|
12284
|
+
/**
|
|
12285
|
+
* Inject a "查看差异" button beside each DSH produced-file chip.
|
|
12286
|
+
*
|
|
12287
|
+
* The harness's `ProducedFiles` component renders each produced file as a
|
|
12288
|
+
* `<button>` chip inside `[data-produced-files-row]`; the full path rides the
|
|
12289
|
+
* chip's `title`. This plugin wants a quick "view this file's diff" affordance
|
|
12290
|
+
* there, but the plugin should not have to touch the harness component. So this
|
|
12291
|
+
* module watches the DOM for those chips (via a MutationObserver, the same
|
|
12292
|
+
* bridge the dsh-pocket mobile fork uses to inject its 复制 buttons) and injects
|
|
12293
|
+
* a small button after each one. Clicking it dispatches a window event that the
|
|
12294
|
+
* diff-approval panel listens for (see PendingPanel), which opens the panel and
|
|
12295
|
+
* selects the file when it is still pending, or toasts otherwise.
|
|
12296
|
+
*
|
|
12297
|
+
* The injected button is intentionally self-contained (inline styles + a fixed
|
|
12298
|
+
* label) so it needs no stylesheet and no harness change.
|
|
12299
|
+
*/
|
|
12300
|
+
/** Window event dispatched by the injected button; PendingPanel listens for it. */
|
|
12301
|
+
const OPEN_FILE_EVENT = "diff-approval:open-file";
|
|
12302
|
+
/** Marker set on a produced-file chip once its diff button has been injected. */
|
|
12303
|
+
const INJECTED_ATTR = "data-diff-approval-produced-diff";
|
|
12304
|
+
/** Marker set on the injected "查看差异" button itself (so cleanup can find it). */
|
|
12305
|
+
const BUTTON_ATTR = "data-diff-approval-produced-diff-btn";
|
|
12306
|
+
/** The produced-file chip selector (the container row itself is not needed). */
|
|
12307
|
+
const CHIP_SELECTOR = "[data-produced-files-row] button";
|
|
12308
|
+
/** Read the produced-file path from a chip (`title` carries the full path). */
|
|
12309
|
+
function producedPathOf(chip) {
|
|
12310
|
+
const path = chip.getAttribute("title")?.trim();
|
|
12311
|
+
return path === void 0 || path === "" ? void 0 : path;
|
|
12312
|
+
}
|
|
12313
|
+
/** Inline layout styles matching the file chip's measured values: the chip is
|
|
12314
|
+
* 22px tall / 6px radius / 0 8px inline padding on the `--dsw-alias-bg-base`
|
|
12315
|
+
* surface. `color` is inlined (the chip's gray text) so the icon is always right;
|
|
12316
|
+
* `background` (rest + a deeper hover) stays in the injected <style> rule.
|
|
12317
|
+
* `display` is set separately (see inject) to mirror the chip's visibility. */
|
|
12318
|
+
function buttonStyle() {
|
|
12319
|
+
return {
|
|
12320
|
+
flex: "none",
|
|
12321
|
+
marginLeft: "-4px",
|
|
12322
|
+
alignItems: "center",
|
|
12323
|
+
justifyContent: "center",
|
|
12324
|
+
boxSizing: "border-box",
|
|
12325
|
+
height: "22px",
|
|
12326
|
+
padding: "0 6px",
|
|
12327
|
+
border: "none",
|
|
12328
|
+
borderRadius: "6px",
|
|
12329
|
+
cursor: "pointer",
|
|
12330
|
+
color: "var(--dsw-alias-label-secondary)"
|
|
12331
|
+
};
|
|
12332
|
+
}
|
|
12333
|
+
/** The DSH "open / jump" icon (IconRightUpOutline16): a diagonal arrow pointing
|
|
12334
|
+
* top-right, i.e. the classic "navigate to / open" affordance — a clean, already
|
|
12335
|
+
* theme-consistent glyph to use instead of hand-drawing +/-. 16×16 matches the
|
|
12336
|
+
* harness's own action glyph; `currentColor` rides the chip's gray text. */
|
|
12337
|
+
const DIFF_ICON_SVG = "<svg width=\"7\" height=\"7\" viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\" style=\"color:var(--dsw-alias-label-secondary)\"><path d=\"M13.588429 5.147807C13.588429 4.739638 13.587271 4.403003 13.582013 4.118684L1.703098 15.99968L0.85155 15.148178L0 14.294485L11.878915 2.413442C11.594721 2.408199 11.257569 2.409154 10.849776 2.409154H2.400594V0.000001H10.849776C11.644471 0.000001 12.338899 -0.001059 12.901622 0.059909C13.486363 0.123352 14.071136 0.265493 14.598303 0.648292C14.886598 0.857751 15.141981 1.110984 15.351433 1.399281C15.734578 1.926807 15.876362 2.512925 15.939743 3.098105C16.000775 3.660718 15.99968 4.353347 15.99968 5.147807V13.599133H13.588429V5.147807Z\" fill=\"currentColor\"/></svg>";
|
|
12338
|
+
/**
|
|
12339
|
+
* Start injecting diff buttons into the produced-files row.
|
|
12340
|
+
* @param label - localized "查看差异" text, used as the icon button's accessible
|
|
12341
|
+
* name + hover title (the button renders only an icon).
|
|
12342
|
+
* @param openPath - called with a produced-file path when its injected button is
|
|
12343
|
+
* clicked; the diff-approval panel decides whether that file is still pending.
|
|
12344
|
+
* @returns a cleanup that disconnects the observer and removes injected buttons.
|
|
12345
|
+
*/
|
|
12346
|
+
function startProducedDiffInjection(label, openPath) {
|
|
12347
|
+
let ownedStyleEl = null;
|
|
12348
|
+
if (document.querySelector("style[data-diff-approval-produced-diff]") === null) {
|
|
12349
|
+
ownedStyleEl = document.createElement("style");
|
|
12350
|
+
ownedStyleEl.setAttribute("data-diff-approval-produced-diff", "");
|
|
12351
|
+
ownedStyleEl.textContent = `[${BUTTON_ATTR}]{background:rgba(38, 49, 72, 0.06);}[${BUTTON_ATTR}]:hover{background:rgba(38, 49, 72, 0.14);}`;
|
|
12352
|
+
document.head.appendChild(ownedStyleEl);
|
|
12353
|
+
}
|
|
12354
|
+
const inject = () => {
|
|
12355
|
+
const chips = [...document.querySelectorAll(CHIP_SELECTOR)];
|
|
12356
|
+
for (const chip of chips) {
|
|
12357
|
+
const path = producedPathOf(chip);
|
|
12358
|
+
if (path === void 0) continue;
|
|
12359
|
+
if (chip.getAttribute(INJECTED_ATTR) === "1") continue;
|
|
12360
|
+
if ([...document.querySelectorAll(`[${BUTTON_ATTR}]`)].some((btn) => btn.getAttribute("data-path") === path)) {
|
|
12361
|
+
chip.setAttribute(INJECTED_ATTR, "1");
|
|
12362
|
+
continue;
|
|
12363
|
+
}
|
|
12364
|
+
chip.setAttribute(INJECTED_ATTR, "1");
|
|
12365
|
+
const btn = document.createElement("span");
|
|
12366
|
+
btn.setAttribute("role", "button");
|
|
12367
|
+
btn.setAttribute("tabindex", "0");
|
|
12368
|
+
btn.setAttribute(BUTTON_ATTR, "1");
|
|
12369
|
+
btn.setAttribute("data-path", path);
|
|
12370
|
+
btn.setAttribute("aria-label", `${label}: ${path}`);
|
|
12371
|
+
btn.setAttribute("title", `${label}: ${path}`);
|
|
12372
|
+
Object.assign(btn.style, buttonStyle());
|
|
12373
|
+
btn.innerHTML = DIFF_ICON_SVG;
|
|
12374
|
+
btn.addEventListener("click", (event) => {
|
|
12375
|
+
event.preventDefault();
|
|
12376
|
+
event.stopImmediatePropagation();
|
|
12377
|
+
openPath(path);
|
|
12378
|
+
});
|
|
12379
|
+
btn.addEventListener("keydown", (event) => {
|
|
12380
|
+
if (event.key === "Enter" || event.key === " ") {
|
|
12381
|
+
event.preventDefault();
|
|
12382
|
+
openPath(path);
|
|
12383
|
+
}
|
|
12384
|
+
});
|
|
12385
|
+
chip.insertAdjacentElement("afterend", btn);
|
|
12386
|
+
}
|
|
12387
|
+
for (const btn of document.querySelectorAll(`[${BUTTON_ATTR}]`)) btn.style.display = "inline-flex";
|
|
12388
|
+
};
|
|
12389
|
+
inject();
|
|
12390
|
+
const observer = new MutationObserver(() => inject());
|
|
12391
|
+
observer.observe(document.body, {
|
|
12392
|
+
childList: true,
|
|
12393
|
+
subtree: true
|
|
12394
|
+
});
|
|
12395
|
+
const onResize = () => inject();
|
|
12396
|
+
window.addEventListener("resize", onResize);
|
|
12397
|
+
return () => {
|
|
12398
|
+
observer.disconnect();
|
|
12399
|
+
window.removeEventListener("resize", onResize);
|
|
12400
|
+
document.querySelectorAll(`[${BUTTON_ATTR}]`).forEach((el) => el.remove());
|
|
12401
|
+
if (ownedStyleEl !== null) ownedStyleEl.remove();
|
|
12402
|
+
};
|
|
12403
|
+
}
|
|
12404
|
+
//#endregion
|
|
12094
12405
|
//#region lib/types/client/settings.js
|
|
12095
12406
|
/** Client preferences for the review panel, persisted in localStorage. */
|
|
12096
12407
|
const PASTE_ON_COPY_KEY = "diff-approval:paste-on-copy";
|
|
@@ -12194,6 +12505,29 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
12194
12505
|
function setQuickSummonKey(value) {
|
|
12195
12506
|
localStorage.setItem(QUICK_SUMMON_KEY, value);
|
|
12196
12507
|
}
|
|
12508
|
+
const KEY_PREFIX = "diff-approval:key:";
|
|
12509
|
+
/** Default chord for each configurable action (every supported key except the
|
|
12510
|
+
* panel's own ESC-to-close, which is intentionally not remapped). */
|
|
12511
|
+
const DEFAULT_KEYBINDINGS = {
|
|
12512
|
+
jumpUp: "Ctrl+ArrowUp",
|
|
12513
|
+
jumpDown: "Ctrl+ArrowDown",
|
|
12514
|
+
copyRef: "Ctrl+L",
|
|
12515
|
+
openSearch: "Ctrl+F",
|
|
12516
|
+
searchNext: "F3",
|
|
12517
|
+
searchPrev: "Shift+F3",
|
|
12518
|
+
undo: "Ctrl+Z",
|
|
12519
|
+
redo: "Ctrl+Shift+Z",
|
|
12520
|
+
cycleNext: "Ctrl+Tab",
|
|
12521
|
+
cyclePrev: "Ctrl+Shift+Tab"
|
|
12522
|
+
};
|
|
12523
|
+
/** The currently configured chord for one action; falls back to its default. */
|
|
12524
|
+
function keybindingOf(action) {
|
|
12525
|
+
return localStorage.getItem(`${KEY_PREFIX}${action}`) ?? DEFAULT_KEYBINDINGS[action] ?? "";
|
|
12526
|
+
}
|
|
12527
|
+
/** Persist one action's chord. */
|
|
12528
|
+
function setKeybinding(action, chord) {
|
|
12529
|
+
localStorage.setItem(`${KEY_PREFIX}${action}`, chord);
|
|
12530
|
+
}
|
|
12197
12531
|
/**
|
|
12198
12532
|
* Whether a keyboard event matches a chord string like `Ctrl+D`. Modifier
|
|
12199
12533
|
* names are matched case-insensitively (`Ctrl`/`Control`, `Alt`/`Option`,
|
|
@@ -12216,7 +12550,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
12216
12550
|
}
|
|
12217
12551
|
//#endregion
|
|
12218
12552
|
//#region \0dsh-css:/home/runner/work/dsh-diff-approval/dsh-diff-approval/src/client/PendingPanel.module.css.mjs
|
|
12219
|
-
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_toggle{border:1px solid var(--dsw-alias-border-l2);cursor:pointer;background:0 0;border-radius:18px;flex:none;width:60px;height:36px;padding:0;position:relative}.F1KBNa_toggle:hover{background:var(--dsw-alias-interactive-bg-hover)}.F1KBNa_toggle:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:2px}.F1KBNa_toggleOn{border-color:var(--dsw-alias-state-business-primary);background:var(--dsw-alias-state-business-primary)}.F1KBNa_toggleOn:hover{background:var(--dsw-alias-state-business-primary)}.F1KBNa_toggleThumb{background:var(--dsw-alias-label-primary);border-radius:50%;width:28px;height:28px;transition:left .12s;position:absolute;top:3px;left:3px}.F1KBNa_toggleOn .F1KBNa_toggleThumb{background:#fff;left:27px}.F1KBNa_stepper{align-items:center;gap:6px;display:inline-flex}.F1KBNa_stepperButton{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-module-platform);width:32px;height:32px;color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;border-radius:8px;font-size:16px;line-height:1}.F1KBNa_stepperButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.F1KBNa_stepperButton:disabled{color:var(--dsw-alias-label-tertiary);cursor:default}.F1KBNa_stepperValue{text-align:center;min-width:2ch;color:var(--dsw-alias-label-primary);font-variant-numeric:tabular-nums;font-size:14px;line-height:22px}.F1KBNa_states{flex-direction:column;flex:1;min-height:0;padding:4px 12px 12px;display:flex;overflow-y:auto}.F1KBNa_split{flex:1;align-items:stretch;min-height:0;display:flex;position:relative}.F1KBNa_fileListFloat{z-index:40;box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-base);box-shadow:var(--dsw-shadow-lv2);border-radius:12px;flex-direction:column;padding:6px 8px 10px;display:flex;position:absolute;overflow:hidden}.F1KBNa_fileList{box-sizing:border-box;border-right:1px solid var(--dsw-alias-border-l2);flex-direction:column;flex:none;width:240px;min-height:0;padding:4px 8px 12px;display:flex}.F1KBNa_listScroll{flex:1;min-height:0;overflow-y:auto}.F1KBNa_bulkActions{flex:none;gap:6px;padding-top:8px;display:flex}.F1KBNa_bulkActions .F1KBNa_action{text-align:center;flex:1}.F1KBNa_resizeHandle{cursor:col-resize;background:0 0;flex:none;width:5px;margin:0 -2px}.F1KBNa_resizeHandle:hover{background:var(--dsw-alias-border-l2)}.F1KBNa_detail{flex-direction:column;flex:1;min-width:0;min-height:0;display:flex}.F1KBNa_detailEmpty{color:var(--dsw-alias-label-tertiary);text-align:center;flex:1;justify-content:center;align-items:center;margin:0;padding:24px;font-size:12px;line-height:18px;display:flex}.F1KBNa_confirmBackdrop{z-index:5;background:color-mix(in srgb, var(--dsw-alias-bg-base) 55%, transparent);justify-content:center;align-items:center;padding:24px;display:flex;position:absolute;inset:0}.F1KBNa_confirmCard{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-base);max-width:340px;box-shadow:var(--dsw-shadow-lv3);border-radius:12px;flex-direction:column;gap:12px;padding:16px;display:flex}.F1KBNa_confirmText{font:var(--dsw-font-caption);color:var(--dsw-alias-label-primary);overflow-wrap:anywhere;margin:0;line-height:1.5}.F1KBNa_confirmActions{justify-content:flex-end;gap:8px;display:flex}.F1KBNa_title{color:var(--dsw-alias-label-primary);font-size:13px;font-weight:500;line-height:20px}.F1KBNa_note,.F1KBNa_readError,.F1KBNa_hint{color:var(--dsw-alias-label-tertiary);margin:4px 0;font-size:12px;line-height:18px}.F1KBNa_noteCentered{text-align:center;margin:auto}.F1KBNa_emptyState{flex-direction:column;align-items:center;gap:10px;margin:auto;display:flex}.F1KBNa_importButton{border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-primary);cursor:pointer;background:0 0;border-radius:8px;padding:5px 14px;font-family:inherit;font-size:12px;line-height:18px}.F1KBNa_importButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.F1KBNa_importButton:disabled{color:var(--dsw-alias-label-tertiary);cursor:default}.F1KBNa_importNote{color:var(--dsw-alias-label-tertiary);text-align:center;margin:0;font-size:12px;line-height:18px}.F1KBNa_readError{color:var(--dsw-alias-state-error-primary)}.F1KBNa_group{color:var(--dsw-alias-label-tertiary);margin:8px 0 4px;font-size:12px;font-weight:500;line-height:16px}.F1KBNa_rows{margin:0;padding:0;list-style:none}.F1KBNa_row{border:1px solid #0000;border-radius:10px;margin:2px 0}.F1KBNa_rowHead{width:100%;color:var(--dsw-alias-label-primary);font:inherit;text-align:left;cursor:pointer;background:0 0;border:none;border-radius:10px;align-items:baseline;gap:8px;padding:6px 8px;display:flex}.F1KBNa_rowHead:hover{background:var(--dsw-alias-interactive-bg-hover-solid)}.F1KBNa_rowHead[data-selected]{background:var(--dsw-alias-interactive-bg-hover)}.F1KBNa_rowPath{text-overflow:ellipsis;white-space:nowrap;min-width:0;font:var(--dsw-font-markdown-code-block);overflow:hidden}.F1KBNa_kindTag{border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-tertiary);white-space:nowrap;border-radius:6px;flex:none;padding:1px 6px;font-size:11px;line-height:14px}.F1KBNa_kindHint{color:var(--dsw-alias-label-tertiary);flex:none;font-size:12px;line-height:16px}.F1KBNa_divergedHint,.F1KBNa_missingHint{border-bottom:1px solid var(--dsw-alias-border-l2);margin:0;padding:6px 8px;font-size:12px;line-height:18px}.F1KBNa_missing{border:1px solid var(--dsw-alias-state-warn-primary);color:var(--dsw-alias-state-warn-label,var(--dsw-alias-state-warn-primary));white-space:nowrap;border-radius:6px;flex:none;padding:1px 6px;font-size:11px;line-height:14px}.F1KBNa_rowFailed{border:1px solid var(--dsw-alias-state-error-primary);color:var(--dsw-alias-state-error-primary);white-space:nowrap;border-radius:6px;flex:none;padding:1px 6px;font-size:11px;line-height:14px}.F1KBNa_actionError{border-bottom:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-state-error-primary);overflow-wrap:anywhere;margin:0;padding:6px 8px;font-size:12px;line-height:18px}.F1KBNa_missingHint{color:var(--dsw-alias-state-warn-label,var(--dsw-alias-state-warn-primary))}.F1KBNa_rowMeta{color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;flex:none;gap:6px;margin-left:auto;font-size:12px;line-height:16px;display:inline-flex}.F1KBNa_addCount{color:color-mix(in srgb, var(--dsw-alias-state-success-primary) 60%, var(--dsw-alias-label-primary))}.F1KBNa_delCount{color:color-mix(in srgb, var(--dsw-alias-state-error-primary) 60%, var(--dsw-alias-label-primary))}.F1KBNa_diff{background:var(--dsw-alias-markdown-code-block);flex-direction:column;flex:1;min-height:0;display:flex;position:relative;overflow:hidden}.F1KBNa_diffHeader{border-bottom:1px solid var(--dsw-alias-border-l2);align-items:center;gap:8px;padding:6px 8px;display:flex}.F1KBNa_diffPath{white-space:nowrap;min-width:0;color:var(--dsw-alias-label-primary);font:var(--dsw-font-markdown-code-block);-webkit-user-select:text;user-select:text;flex:1;font-size:12px;line-height:18px;overflow:auto 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_divider{background:var(--dsw-alias-border-l1);align-self:stretch;width:1px;margin:2px 0}.F1KBNa_action{border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-primary);cursor:pointer;background:0 0;border-radius:8px;padding:3px 12px;font-family:inherit;font-size:12px;line-height:18px}.F1KBNa_action:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.F1KBNa_action:disabled{color:var(--dsw-alias-label-tertiary);cursor:default}.F1KBNa_actionPrimary{background:var(--dsw-alias-state-business-primary);border-color:var(--dsw-alias-state-business-primary);color:var(--dsw-static-neutral-bluish-50)}.F1KBNa_actionPrimary:hover:not(:disabled){background:color-mix(in srgb, var(--dsw-alias-state-business-primary) 85%, var(--dsw-static-neutral-bluish-1000))}.F1KBNa_actionPrimary:disabled{background:var(--dsw-alias-state-business-primary);color:var(--dsw-static-neutral-bluish-50);opacity:.55}.F1KBNa_actionQuietDisabled:disabled{cursor:pointer;color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-border-l1);background:0 0}.F1KBNa_actionPrimary.F1KBNa_actionQuietDisabled:disabled{background:var(--dsw-alias-state-business-primary);border-color:var(--dsw-alias-state-business-primary);color:var(--dsw-static-neutral-bluish-50);opacity:1}.F1KBNa_iconAction{justify-content:center;align-items:center;min-height:25px;padding:4px 6px;display:inline-flex}.F1KBNa_close{cursor:pointer;width:28px;height:28px;color:var(--dsw-alias-label-tertiary);background:0 0;border:none;border-radius:28px;justify-content:center;align-items:center;padding:0;display:inline-flex}.F1KBNa_close:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}.F1KBNa_expand{cursor:pointer;width:28px;height:28px;color:var(--dsw-alias-label-tertiary);background:0 0;border:none;border-radius:28px;justify-content:center;align-items:center;padding:0;display:inline-flex}.F1KBNa_expand:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}.F1KBNa_expandExpanded svg{transform:rotate(180deg)}.F1KBNa_diffBodyWrap{flex:1;min-height:0;display:flex;position:relative}.F1KBNa_diffBody{min-width:0;font:var(--dsw-font-markdown-code-block);-webkit-text-size-adjust:100%;text-size-adjust:100%;cursor:text;outline:none;flex:1;padding:0;position:relative;overflow:auto}.F1KBNa_diffBody::-webkit-scrollbar,.F1KBNa_diffBody::-webkit-scrollbar-track,.F1KBNa_diffBody::-webkit-scrollbar-thumb{cursor:default}.F1KBNa_blockActions{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-markdown-code-block);box-shadow:var(--dsw-shadow-lv1);z-index:2;border-radius:10px;align-items:center;gap:9px;padding:5px;display:flex;position:absolute;right:8px}.F1KBNa_blockPosition{color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;white-space:nowrap;margin-top:1px;margin-left:7px;font-size:12px;line-height:16px}.F1KBNa_searchBar{z-index:3;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-markdown-code-block);box-shadow:var(--dsw-shadow-lv1);border-radius:10px;align-items:center;gap:4px;padding:4px;display:flex;position:absolute;top:8px;right:8px}.F1KBNa_searchInput{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-base);width:150px;color:var(--dsw-alias-label-primary);border-radius:6px;outline:none;padding:3px 8px;font-family:inherit;font-size:12px;line-height:18px}.F1KBNa_searchInput:focus{border-color:var(--dsw-alias-state-business-primary)}.F1KBNa_searchCount{text-align:center;min-width:34px;color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;white-space:nowrap;font-size:12px;line-height:16px}.F1KBNa_diffBody [data-diff-search=hit]{background-image:linear-gradient(#facc1529,#facc1529)}.F1KBNa_diffBody [data-diff-search=current]{background-image:linear-gradient(#facc1552,#facc1552)}.F1KBNa_blockFlash{z-index:1;box-sizing:border-box;border:2px solid var(--dsw-alias-state-business-primary);pointer-events:none;border-radius:4px;animation:1s ease-out forwards F1KBNa_diffFlash;position:absolute;left:0;right:0}@keyframes F1KBNa_diffFlash{0%{opacity:1}to{opacity:0}}.F1KBNa_overviewRuler{pointer-events:none;opacity:.5;width:4px;position:absolute;top:0;bottom:0;right:0}.F1KBNa_overviewMarker{border-radius:2px;width:100%;min-height:2px;position:absolute;right:0}.F1KBNa_markerDel{background-color:var(--dsw-alias-state-error-primary)}.F1KBNa_markerAdd{background-color:var(--dsw-alias-state-success-primary)}.F1KBNa_lines{border-spacing:0;width:max-content;min-width:100%;display:table}.F1KBNa_line{-webkit-user-select:none;user-select:none;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;-webkit-user-select:none;user-select:none;cursor:default;padding-right:10px;display:table-cell}.F1KBNa_code{white-space:pre;-webkit-user-select:text;user-select:text;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)}.F1KBNa_intraDel{background-color:color-mix(in srgb, var(--dsw-alias-state-error-primary) 26%, transparent);text-decoration:line-through}.F1KBNa_intraAdd{background-color:color-mix(in srgb, var(--dsw-alias-state-success-primary) 24%, transparent)}";
|
|
12553
|
+
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_settingsGroup{border-bottom:1px solid var(--dsw-alias-border-l2);margin-top:8px}.F1KBNa_settingsGroupHeader{width:100%;color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;background:0 0;border:none;align-items:center;gap:8px;padding:12px 0;display:flex}.F1KBNa_settingsGroupTitle{font-size:14px;font-weight:400;line-height:22px}.F1KBNa_settingsGroupChevron{margin-left:auto;transition:transform .15s}.F1KBNa_settingsGroup[data-open] .F1KBNa_settingsGroupChevron{transform:rotate(180deg)}.F1KBNa_settingsGroup[data-open] .F1KBNa_settingsGroupHeader{border-bottom:1px solid var(--dsw-alias-border-l2)}.F1KBNa_settingsGroupBody{display:grid}.F1KBNa_settingsGroupBody .F1KBNa_settingsRow{padding-left:16px}.F1KBNa_settingsGroupBody .F1KBNa_settingsRow:last-child,.F1KBNa_settingsPage>.F1KBNa_settingsRow:last-child{border-bottom:none}.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_toggle{border:1px solid var(--dsw-alias-border-l2);cursor:pointer;background:0 0;border-radius:18px;flex:none;width:60px;height:36px;padding:0;position:relative}.F1KBNa_toggle:hover{background:var(--dsw-alias-interactive-bg-hover)}.F1KBNa_toggle:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:2px}.F1KBNa_toggleOn{border-color:var(--dsw-alias-state-business-primary);background:var(--dsw-alias-state-business-primary)}.F1KBNa_toggleOn:hover{background:var(--dsw-alias-state-business-primary)}.F1KBNa_toggleThumb{background:var(--dsw-alias-label-primary);border-radius:50%;width:28px;height:28px;transition:left .12s;position:absolute;top:3px;left:3px}.F1KBNa_toggleOn .F1KBNa_toggleThumb{background:#fff;left:27px}.F1KBNa_stepper{align-items:center;gap:6px;display:inline-flex}.F1KBNa_stepperButton{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-module-platform);width:32px;height:32px;color:var(--dsw-alias-label-primary);cursor:pointer;border-radius:8px;padding:0;position:relative}.F1KBNa_stepperButton:before,.F1KBNa_stepperButton:after{content:\"\";background:currentColor;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)}.F1KBNa_stepperButton:before{width:10px;height:1px}.F1KBNa_stepperButton:after{width:1px;height:10px;display:none}.F1KBNa_stepperButtonUp:after{display:block}.F1KBNa_stepperButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.F1KBNa_stepperButton:disabled{color:var(--dsw-alias-label-tertiary);cursor:default}.F1KBNa_stepperValue{text-align:center;min-width:2ch;color:var(--dsw-alias-label-primary);font-variant-numeric:tabular-nums;font-size:14px;line-height:22px}.F1KBNa_states{flex-direction:column;flex:1;min-height:0;padding:4px 12px 12px;display:flex;overflow-y:auto}.F1KBNa_split{flex:1;align-items:stretch;min-height:0;display:flex;position:relative}.F1KBNa_fileListFloat{z-index:40;box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-base);box-shadow:var(--dsw-shadow-lv2);border-radius:12px;flex-direction:column;padding:6px 8px 10px;display:flex;position:absolute;overflow:hidden}.F1KBNa_fileList{box-sizing:border-box;border-right:1px solid var(--dsw-alias-border-l2);flex-direction:column;flex:none;width:240px;min-height:0;padding:4px 8px 12px;display:flex}.F1KBNa_listScroll{flex:1;min-height:0;overflow-y:auto}.F1KBNa_bulkActions{flex:none;gap:6px;padding-top:8px;display:flex}.F1KBNa_bulkActions .F1KBNa_action{text-align:center;flex:1}.F1KBNa_resizeHandle{cursor:col-resize;background:0 0;flex:none;width:5px;margin:0 -2px}.F1KBNa_resizeHandle:hover{background:var(--dsw-alias-border-l2)}.F1KBNa_detail{flex-direction:column;flex:1;min-width:0;min-height:0;display:flex}.F1KBNa_detailEmpty{color:var(--dsw-alias-label-tertiary);text-align:center;flex:1;justify-content:center;align-items:center;margin:0;padding:24px;font-size:12px;line-height:18px;display:flex}.F1KBNa_confirmBackdrop{z-index:5;background:color-mix(in srgb, var(--dsw-alias-bg-base) 55%, transparent);justify-content:center;align-items:center;padding:24px;display:flex;position:absolute;inset:0}.F1KBNa_confirmCard{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-base);max-width:340px;box-shadow:var(--dsw-shadow-lv3);border-radius:12px;flex-direction:column;gap:12px;padding:16px;display:flex}.F1KBNa_confirmText{font:var(--dsw-font-caption);color:var(--dsw-alias-label-primary);overflow-wrap:anywhere;margin:0;line-height:1.5}.F1KBNa_confirmActions{justify-content:flex-end;gap:8px;display:flex}.F1KBNa_title{color:var(--dsw-alias-label-primary);font-size:13px;font-weight:500;line-height:20px}.F1KBNa_note,.F1KBNa_readError,.F1KBNa_hint{color:var(--dsw-alias-label-tertiary);margin:4px 0;font-size:12px;line-height:18px}.F1KBNa_noteCentered{text-align:center;margin:auto}.F1KBNa_emptyState{flex-direction:column;align-items:center;gap:10px;margin:auto;display:flex}.F1KBNa_importButton{border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-primary);cursor:pointer;background:0 0;border-radius:8px;padding:5px 14px;font-family:inherit;font-size:12px;line-height:18px}.F1KBNa_importButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.F1KBNa_importButton:disabled{color:var(--dsw-alias-label-tertiary);cursor:default}.F1KBNa_importNote{color:var(--dsw-alias-label-tertiary);text-align:center;margin:0;font-size:12px;line-height:18px}.F1KBNa_readError{color:var(--dsw-alias-state-error-primary)}.F1KBNa_group{color:var(--dsw-alias-label-tertiary);margin:8px 0 4px;font-size:12px;font-weight:500;line-height:16px}.F1KBNa_rows{margin:0;padding:0;list-style:none}.F1KBNa_row{border:1px solid #0000;border-radius:10px;margin:2px 0}.F1KBNa_rowHead{width:100%;color:var(--dsw-alias-label-primary);font:inherit;text-align:left;cursor:pointer;background:0 0;border:none;border-radius:10px;align-items:baseline;gap:8px;padding:6px 8px;display:flex}.F1KBNa_rowHead:hover{background:var(--dsw-alias-interactive-bg-hover-solid)}.F1KBNa_rowHead[data-selected]{background:var(--dsw-alias-interactive-bg-hover)}.F1KBNa_rowPath{text-overflow:ellipsis;white-space:nowrap;min-width:0;font:var(--dsw-font-markdown-code-block);overflow:hidden}.F1KBNa_kindTag{border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-tertiary);white-space:nowrap;border-radius:6px;flex:none;padding:1px 6px;font-size:11px;line-height:14px}.F1KBNa_kindHint{color:var(--dsw-alias-label-tertiary);flex:none;font-size:12px;line-height:16px}.F1KBNa_divergedHint,.F1KBNa_missingHint{border-bottom:1px solid var(--dsw-alias-border-l2);margin:0;padding:6px 8px;font-size:12px;line-height:18px}.F1KBNa_missing{border:1px solid var(--dsw-alias-state-warn-primary);color:var(--dsw-alias-state-warn-label,var(--dsw-alias-state-warn-primary));white-space:nowrap;border-radius:6px;flex:none;padding:1px 6px;font-size:11px;line-height:14px}.F1KBNa_rowFailed{border:1px solid var(--dsw-alias-state-error-primary);color:var(--dsw-alias-state-error-primary);white-space:nowrap;border-radius:6px;flex:none;padding:1px 6px;font-size:11px;line-height:14px}.F1KBNa_actionError{border-bottom:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-state-error-primary);overflow-wrap:anywhere;margin:0;padding:6px 8px;font-size:12px;line-height:18px}.F1KBNa_missingHint{color:var(--dsw-alias-state-warn-label,var(--dsw-alias-state-warn-primary))}.F1KBNa_rowMeta{color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;flex:none;gap:6px;margin-left:auto;font-size:12px;line-height:16px;display:inline-flex}.F1KBNa_addCount{color:color-mix(in srgb, var(--dsw-alias-state-success-primary) 60%, var(--dsw-alias-label-primary))}.F1KBNa_delCount{color:color-mix(in srgb, var(--dsw-alias-state-error-primary) 60%, var(--dsw-alias-label-primary))}.F1KBNa_diff{background:var(--dsw-alias-markdown-code-block);flex-direction:column;flex:1;min-height:0;display:flex;position:relative;overflow:hidden}.F1KBNa_diffHeader{border-bottom:1px solid var(--dsw-alias-border-l2);align-items:center;gap:8px;padding:6px 8px;display:flex}.F1KBNa_diffPath{white-space:nowrap;min-width:0;color:var(--dsw-alias-label-primary);font:var(--dsw-font-markdown-code-block);-webkit-user-select:text;user-select:text;flex:1;font-size:12px;line-height:18px;overflow:auto 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_divider{background:var(--dsw-alias-border-l1);align-self:stretch;width:1px;margin:2px 0}.F1KBNa_action{border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-primary);cursor:pointer;background:0 0;border-radius:8px;padding:3px 12px;font-family:inherit;font-size:12px;line-height:18px}.F1KBNa_action:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.F1KBNa_action:disabled{color:var(--dsw-alias-label-tertiary);cursor:default}.F1KBNa_actionPrimary{background:var(--dsw-alias-state-business-primary);border-color:var(--dsw-alias-state-business-primary);color:var(--dsw-static-neutral-bluish-50)}.F1KBNa_actionPrimary:hover:not(:disabled){background:color-mix(in srgb, var(--dsw-alias-state-business-primary) 85%, var(--dsw-static-neutral-bluish-1000))}.F1KBNa_actionPrimary:disabled{background:var(--dsw-alias-state-business-primary);color:var(--dsw-static-neutral-bluish-50);opacity:.55}.F1KBNa_actionQuietDisabled:disabled{cursor:pointer;color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-border-l1);background:0 0}.F1KBNa_actionPrimary.F1KBNa_actionQuietDisabled:disabled{background:var(--dsw-alias-state-business-primary);border-color:var(--dsw-alias-state-business-primary);color:var(--dsw-static-neutral-bluish-50);opacity:1}.F1KBNa_iconAction{justify-content:center;align-items:center;min-height:25px;padding:4px 6px;display:inline-flex}.F1KBNa_close{cursor:pointer;width:28px;height:28px;color:var(--dsw-alias-label-tertiary);background:0 0;border:none;border-radius:28px;justify-content:center;align-items:center;padding:0;display:inline-flex}.F1KBNa_close:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}.F1KBNa_expand{cursor:pointer;width:28px;height:28px;color:var(--dsw-alias-label-tertiary);background:0 0;border:none;border-radius:28px;justify-content:center;align-items:center;padding:0;display:inline-flex}.F1KBNa_expand:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}.F1KBNa_expandExpanded svg{transform:rotate(180deg)}.F1KBNa_diffBodyWrap{flex:1;min-height:0;display:flex;position:relative}.F1KBNa_diffBody{min-width:0;font:var(--dsw-font-markdown-code-block);-webkit-text-size-adjust:100%;text-size-adjust:100%;cursor:text;outline:none;flex:1;padding:0;position:relative;overflow:auto}.F1KBNa_diffBody::-webkit-scrollbar,.F1KBNa_diffBody::-webkit-scrollbar-track,.F1KBNa_diffBody::-webkit-scrollbar-thumb{cursor:default}.F1KBNa_blockActions{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-markdown-code-block);box-shadow:var(--dsw-shadow-lv1);z-index:2;border-radius:10px;align-items:center;gap:9px;padding:5px;display:flex;position:absolute;right:8px}.F1KBNa_blockPosition{color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;white-space:nowrap;margin-top:1px;margin-left:7px;font-size:12px;line-height:16px}.F1KBNa_searchBar{z-index:3;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-markdown-code-block);box-shadow:var(--dsw-shadow-lv1);border-radius:10px;align-items:center;gap:4px;padding:4px;display:flex;position:absolute;top:8px;right:8px}.F1KBNa_searchInput{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-base);width:150px;color:var(--dsw-alias-label-primary);border-radius:6px;outline:none;padding:3px 8px;font-family:inherit;font-size:12px;line-height:18px}.F1KBNa_searchInput:focus{border-color:var(--dsw-alias-state-business-primary)}.F1KBNa_searchCount{text-align:center;min-width:34px;color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;white-space:nowrap;font-size:12px;line-height:16px}.F1KBNa_searchMatch{background-color:#facc1542;border-radius:2px}.F1KBNa_searchMatchCurrent{background-color:#facc1599;border-radius:2px}.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_diffFlashFade;position:absolute;left:0;right:0}.F1KBNa_blockFlashShake{animation:.7s ease-out forwards F1KBNa_diffFlashShake}@keyframes F1KBNa_diffFlashFade{0%{opacity:1}to{opacity:0}}@keyframes F1KBNa_diffFlashShake{0%{opacity:1;transform:translateY(0)}10%{transform:translateY(1px)}20%{transform:translateY(-1px)}30%{transform:translateY(1px)}40%{transform:translateY(-1px)}50%{transform:translateY(1px)}60%{transform:translateY(-1px)}70%{opacity:1;transform:translateY(0)}to{opacity:0;transform:translateY(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{-webkit-user-select:none;user-select:none;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;-webkit-user-select:none;user-select:none;cursor:default;padding-right:10px;display:table-cell}.F1KBNa_code{white-space:pre;-webkit-user-select:text;user-select:text;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)}.F1KBNa_intraDel{background-color:color-mix(in srgb, var(--dsw-alias-state-error-primary) 26%, transparent);text-decoration:line-through}.F1KBNa_intraAdd{background-color:color-mix(in srgb, var(--dsw-alias-state-success-primary) 24%, transparent)}";
|
|
12220
12554
|
const tagId = "dsh-diff-approval/PendingPanel.module.css";
|
|
12221
12555
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
|
|
12222
12556
|
const tag = document.createElement("style");
|
|
@@ -12226,124 +12560,134 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
12226
12560
|
document.head.appendChild(tag);
|
|
12227
12561
|
}
|
|
12228
12562
|
var PendingPanel_module_css_default = {
|
|
12229
|
-
"
|
|
12563
|
+
"stepperButton": "F1KBNa_stepperButton",
|
|
12564
|
+
"divergedHint": "F1KBNa_divergedHint",
|
|
12565
|
+
"delCount": "F1KBNa_delCount",
|
|
12566
|
+
"expandExpanded": "F1KBNa_expandExpanded",
|
|
12567
|
+
"noticeButton": "F1KBNa_noticeButton",
|
|
12568
|
+
"detail": "F1KBNa_detail",
|
|
12569
|
+
"settingsRow": "F1KBNa_settingsRow",
|
|
12570
|
+
"diffFlashFade": "F1KBNa_diffFlashFade",
|
|
12571
|
+
"searchMatch": "F1KBNa_searchMatch",
|
|
12230
12572
|
"rail": "F1KBNa_rail",
|
|
12231
|
-
"diffBody": "F1KBNa_diffBody",
|
|
12232
|
-
"settingsSelector": "F1KBNa_settingsSelector",
|
|
12233
|
-
"badgeLabel": "F1KBNa_badgeLabel",
|
|
12234
|
-
"panel": "F1KBNa_panel",
|
|
12235
|
-
"hint": "F1KBNa_hint",
|
|
12236
|
-
"importButton": "F1KBNa_importButton",
|
|
12237
|
-
"addCount": "F1KBNa_addCount",
|
|
12238
|
-
"rowPath": "F1KBNa_rowPath",
|
|
12239
|
-
"searchInput": "F1KBNa_searchInput",
|
|
12240
12573
|
"title": "F1KBNa_title",
|
|
12241
|
-
"actionPrimary": "F1KBNa_actionPrimary",
|
|
12242
|
-
"blockActions": "F1KBNa_blockActions",
|
|
12243
|
-
"del": "F1KBNa_del",
|
|
12244
12574
|
"importNote": "F1KBNa_importNote",
|
|
12245
|
-
"
|
|
12246
|
-
"
|
|
12247
|
-
"
|
|
12248
|
-
"
|
|
12575
|
+
"diffFlashShake": "F1KBNa_diffFlashShake",
|
|
12576
|
+
"line": "F1KBNa_line",
|
|
12577
|
+
"split": "F1KBNa_split",
|
|
12578
|
+
"missing": "F1KBNa_missing",
|
|
12579
|
+
"searchBar": "F1KBNa_searchBar",
|
|
12580
|
+
"splitHScroll": "F1KBNa_splitHScroll",
|
|
12581
|
+
"splitRadd": "F1KBNa_splitRadd",
|
|
12249
12582
|
"noteCentered": "F1KBNa_noteCentered",
|
|
12250
|
-
"
|
|
12251
|
-
"
|
|
12583
|
+
"overviewRuler": "F1KBNa_overviewRuler",
|
|
12584
|
+
"actionQuietDisabled": "F1KBNa_actionQuietDisabled",
|
|
12585
|
+
"states": "F1KBNa_states",
|
|
12586
|
+
"vSpacer": "F1KBNa_vSpacer",
|
|
12587
|
+
"fullscreenBackdrop": "F1KBNa_fullscreenBackdrop",
|
|
12588
|
+
"resizeHandle": "F1KBNa_resizeHandle",
|
|
12589
|
+
"diffBodySplit": "F1KBNa_diffBodySplit",
|
|
12590
|
+
"splitCol": "F1KBNa_splitCol",
|
|
12591
|
+
"badge": "F1KBNa_badge",
|
|
12592
|
+
"settingsGroupHeader": "F1KBNa_settingsGroupHeader",
|
|
12593
|
+
"code": "F1KBNa_code",
|
|
12594
|
+
"settingsGroupTitle": "F1KBNa_settingsGroupTitle",
|
|
12252
12595
|
"toggleOn": "F1KBNa_toggleOn",
|
|
12596
|
+
"diffPath": "F1KBNa_diffPath",
|
|
12597
|
+
"confirmText": "F1KBNa_confirmText",
|
|
12598
|
+
"searchCount": "F1KBNa_searchCount",
|
|
12599
|
+
"blockFlashShake": "F1KBNa_blockFlashShake",
|
|
12600
|
+
"settingsSelectorChevron": "F1KBNa_settingsSelectorChevron",
|
|
12601
|
+
"hint": "F1KBNa_hint",
|
|
12602
|
+
"settingsGroupBody": "F1KBNa_settingsGroupBody",
|
|
12603
|
+
"group": "F1KBNa_group",
|
|
12253
12604
|
"wrap": "F1KBNa_wrap",
|
|
12254
|
-
"
|
|
12255
|
-
"
|
|
12256
|
-
"
|
|
12257
|
-
"
|
|
12258
|
-
"missingHint": "F1KBNa_missingHint",
|
|
12259
|
-
"settingsRowDesc": "F1KBNa_settingsRowDesc",
|
|
12260
|
-
"blockPosition": "F1KBNa_blockPosition",
|
|
12261
|
-
"statusBar": "F1KBNa_statusBar",
|
|
12262
|
-
"footerButtons": "F1KBNa_footerButtons",
|
|
12263
|
-
"fullscreenBackdrop": "F1KBNa_fullscreenBackdrop",
|
|
12605
|
+
"splitHScrollRow": "F1KBNa_splitHScrollRow",
|
|
12606
|
+
"header": "F1KBNa_header",
|
|
12607
|
+
"iconAction": "F1KBNa_iconAction",
|
|
12608
|
+
"gutter": "F1KBNa_gutter",
|
|
12264
12609
|
"diffActions": "F1KBNa_diffActions",
|
|
12610
|
+
"settingsPage": "F1KBNa_settingsPage",
|
|
12611
|
+
"badgeCount": "F1KBNa_badgeCount",
|
|
12612
|
+
"footerButtons": "F1KBNa_footerButtons",
|
|
12613
|
+
"noticeText": "F1KBNa_noticeText",
|
|
12614
|
+
"detailEmpty": "F1KBNa_detailEmpty",
|
|
12265
12615
|
"divider": "F1KBNa_divider",
|
|
12616
|
+
"searchMatchCurrent": "F1KBNa_searchMatchCurrent",
|
|
12617
|
+
"splitLadd": "F1KBNa_splitLadd",
|
|
12618
|
+
"actionError": "F1KBNa_actionError",
|
|
12619
|
+
"expand": "F1KBNa_expand",
|
|
12620
|
+
"blockPosition": "F1KBNa_blockPosition",
|
|
12621
|
+
"splitHScrollFill": "F1KBNa_splitHScrollFill",
|
|
12622
|
+
"badgeLabel": "F1KBNa_badgeLabel",
|
|
12623
|
+
"stepper": "F1KBNa_stepper",
|
|
12624
|
+
"statusBar": "F1KBNa_statusBar",
|
|
12625
|
+
"markerDel": "F1KBNa_markerDel",
|
|
12626
|
+
"confirmCard": "F1KBNa_confirmCard",
|
|
12627
|
+
"blockActions": "F1KBNa_blockActions",
|
|
12628
|
+
"toggle": "F1KBNa_toggle",
|
|
12629
|
+
"missingHint": "F1KBNa_missingHint",
|
|
12630
|
+
"diffHeader": "F1KBNa_diffHeader",
|
|
12266
12631
|
"splitRdel": "F1KBNa_splitRdel",
|
|
12267
|
-
"missing": "F1KBNa_missing",
|
|
12268
12632
|
"headerActions": "F1KBNa_headerActions",
|
|
12269
|
-
"
|
|
12270
|
-
"diffFlash": "F1KBNa_diffFlash",
|
|
12271
|
-
"rowMeta": "F1KBNa_rowMeta",
|
|
12272
|
-
"close": "F1KBNa_close",
|
|
12273
|
-
"diffPath": "F1KBNa_diffPath",
|
|
12274
|
-
"overviewMarker": "F1KBNa_overviewMarker",
|
|
12275
|
-
"confirmActions": "F1KBNa_confirmActions",
|
|
12276
|
-
"splitHScroll": "F1KBNa_splitHScroll",
|
|
12277
|
-
"actionError": "F1KBNa_actionError",
|
|
12278
|
-
"toggleThumb": "F1KBNa_toggleThumb",
|
|
12633
|
+
"actionPrimary": "F1KBNa_actionPrimary",
|
|
12279
12634
|
"readError": "F1KBNa_readError",
|
|
12280
|
-
"
|
|
12281
|
-
"
|
|
12282
|
-
"settingsSelectorChevron": "F1KBNa_settingsSelectorChevron",
|
|
12283
|
-
"delCount": "F1KBNa_delCount",
|
|
12284
|
-
"detailEmpty": "F1KBNa_detailEmpty",
|
|
12285
|
-
"confirmCard": "F1KBNa_confirmCard",
|
|
12286
|
-
"group": "F1KBNa_group",
|
|
12635
|
+
"splitCols": "F1KBNa_splitCols",
|
|
12636
|
+
"kindHint": "F1KBNa_kindHint",
|
|
12287
12637
|
"settingsRowText": "F1KBNa_settingsRowText",
|
|
12288
|
-
"
|
|
12289
|
-
"context": "F1KBNa_context",
|
|
12290
|
-
"expand": "F1KBNa_expand",
|
|
12291
|
-
"splitRoot": "F1KBNa_splitRoot",
|
|
12292
|
-
"rows": "F1KBNa_rows",
|
|
12293
|
-
"overviewRuler": "F1KBNa_overviewRuler",
|
|
12294
|
-
"listScroll": "F1KBNa_listScroll",
|
|
12295
|
-
"flexSpacer": "F1KBNa_flexSpacer",
|
|
12296
|
-
"states": "F1KBNa_states",
|
|
12297
|
-
"splitLdel": "F1KBNa_splitLdel",
|
|
12638
|
+
"blockFlash": "F1KBNa_blockFlash",
|
|
12298
12639
|
"langSelect": "F1KBNa_langSelect",
|
|
12640
|
+
"settingsGroup": "F1KBNa_settingsGroup",
|
|
12641
|
+
"wrapActive": "F1KBNa_wrapActive",
|
|
12642
|
+
"rowFailed": "F1KBNa_rowFailed",
|
|
12643
|
+
"del": "F1KBNa_del",
|
|
12644
|
+
"kindTag": "F1KBNa_kindTag",
|
|
12645
|
+
"diff": "F1KBNa_diff",
|
|
12646
|
+
"searchInput": "F1KBNa_searchInput",
|
|
12647
|
+
"lines": "F1KBNa_lines",
|
|
12648
|
+
"stepperButtonUp": "F1KBNa_stepperButtonUp",
|
|
12649
|
+
"confirmActions": "F1KBNa_confirmActions",
|
|
12650
|
+
"listScroll": "F1KBNa_listScroll",
|
|
12651
|
+
"notice": "F1KBNa_notice",
|
|
12652
|
+
"panel": "F1KBNa_panel",
|
|
12299
12653
|
"rowHead": "F1KBNa_rowHead",
|
|
12300
|
-
"
|
|
12301
|
-
"
|
|
12302
|
-
"
|
|
12303
|
-
"
|
|
12654
|
+
"settingsSelector": "F1KBNa_settingsSelector",
|
|
12655
|
+
"note": "F1KBNa_note",
|
|
12656
|
+
"row": "F1KBNa_row",
|
|
12657
|
+
"markerAdd": "F1KBNa_markerAdd",
|
|
12304
12658
|
"intraAdd": "F1KBNa_intraAdd",
|
|
12305
|
-
"
|
|
12306
|
-
"
|
|
12307
|
-
"fileList": "F1KBNa_fileList",
|
|
12308
|
-
"rowFailed": "F1KBNa_rowFailed",
|
|
12309
|
-
"gutter": "F1KBNa_gutter",
|
|
12310
|
-
"wrapActive": "F1KBNa_wrapActive",
|
|
12311
|
-
"blockFlash": "F1KBNa_blockFlash",
|
|
12312
|
-
"splitHScrollFill": "F1KBNa_splitHScrollFill",
|
|
12313
|
-
"noticeText": "F1KBNa_noticeText",
|
|
12314
|
-
"emptyState": "F1KBNa_emptyState",
|
|
12659
|
+
"importButton": "F1KBNa_importButton",
|
|
12660
|
+
"overviewMarker": "F1KBNa_overviewMarker",
|
|
12315
12661
|
"bulkActions": "F1KBNa_bulkActions",
|
|
12316
|
-
"
|
|
12317
|
-
"
|
|
12318
|
-
"
|
|
12319
|
-
"
|
|
12320
|
-
"header": "F1KBNa_header",
|
|
12321
|
-
"splitLadd": "F1KBNa_splitLadd",
|
|
12322
|
-
"notice": "F1KBNa_notice",
|
|
12323
|
-
"confirmText": "F1KBNa_confirmText",
|
|
12324
|
-
"diffBodyWrap": "F1KBNa_diffBodyWrap",
|
|
12325
|
-
"line": "F1KBNa_line",
|
|
12326
|
-
"noticeButton": "F1KBNa_noticeButton",
|
|
12327
|
-
"lines": "F1KBNa_lines",
|
|
12328
|
-
"confirmBackdrop": "F1KBNa_confirmBackdrop",
|
|
12329
|
-
"searchCount": "F1KBNa_searchCount",
|
|
12662
|
+
"rowPath": "F1KBNa_rowPath",
|
|
12663
|
+
"diffStats": "F1KBNa_diffStats",
|
|
12664
|
+
"emptyState": "F1KBNa_emptyState",
|
|
12665
|
+
"toggleThumb": "F1KBNa_toggleThumb",
|
|
12330
12666
|
"stepperValue": "F1KBNa_stepperValue",
|
|
12331
|
-
"
|
|
12332
|
-
"
|
|
12667
|
+
"settingsRowTitle": "F1KBNa_settingsRowTitle",
|
|
12668
|
+
"diffBody": "F1KBNa_diffBody",
|
|
12669
|
+
"subline": "F1KBNa_subline",
|
|
12670
|
+
"addCount": "F1KBNa_addCount",
|
|
12671
|
+
"layer": "F1KBNa_layer",
|
|
12672
|
+
"intraDel": "F1KBNa_intraDel",
|
|
12673
|
+
"langLabel": "F1KBNa_langLabel",
|
|
12674
|
+
"statusAction": "F1KBNa_statusAction",
|
|
12675
|
+
"action": "F1KBNa_action",
|
|
12676
|
+
"settingsRowDesc": "F1KBNa_settingsRowDesc",
|
|
12677
|
+
"rows": "F1KBNa_rows",
|
|
12678
|
+
"splitRoot": "F1KBNa_splitRoot",
|
|
12333
12679
|
"splitDivider": "F1KBNa_splitDivider",
|
|
12334
|
-
"
|
|
12335
|
-
"
|
|
12336
|
-
"
|
|
12680
|
+
"confirmBackdrop": "F1KBNa_confirmBackdrop",
|
|
12681
|
+
"diffBodyWrap": "F1KBNa_diffBodyWrap",
|
|
12682
|
+
"splitLdel": "F1KBNa_splitLdel",
|
|
12337
12683
|
"add": "F1KBNa_add",
|
|
12338
|
-
"
|
|
12339
|
-
"
|
|
12340
|
-
"
|
|
12341
|
-
"
|
|
12342
|
-
"
|
|
12343
|
-
"
|
|
12344
|
-
"
|
|
12345
|
-
"iconAction": "F1KBNa_iconAction",
|
|
12346
|
-
"actionQuietDisabled": "F1KBNa_actionQuietDisabled"
|
|
12684
|
+
"fileList": "F1KBNa_fileList",
|
|
12685
|
+
"fileListFloat": "F1KBNa_fileListFloat",
|
|
12686
|
+
"flexSpacer": "F1KBNa_flexSpacer",
|
|
12687
|
+
"rowMeta": "F1KBNa_rowMeta",
|
|
12688
|
+
"settingsGroupChevron": "F1KBNa_settingsGroupChevron",
|
|
12689
|
+
"context": "F1KBNa_context",
|
|
12690
|
+
"close": "F1KBNa_close"
|
|
12347
12691
|
};
|
|
12348
12692
|
//#endregion
|
|
12349
12693
|
//#region lib/types/client/PendingPanel.js
|
|
@@ -12687,6 +13031,53 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
12687
13031
|
}, i);
|
|
12688
13032
|
});
|
|
12689
13033
|
}
|
|
13034
|
+
/** Character ranges of each case-insensitive occurrence of `query` in `text`. */
|
|
13035
|
+
function matchRangesOf(text, query) {
|
|
13036
|
+
if (query === "") return [];
|
|
13037
|
+
const lower = text.toLowerCase();
|
|
13038
|
+
const q = query.toLowerCase();
|
|
13039
|
+
const out = [];
|
|
13040
|
+
let from = 0;
|
|
13041
|
+
for (;;) {
|
|
13042
|
+
const at = lower.indexOf(q, from);
|
|
13043
|
+
if (at === -1) return out;
|
|
13044
|
+
out.push([at, at + query.length]);
|
|
13045
|
+
from = at + query.length;
|
|
13046
|
+
}
|
|
13047
|
+
}
|
|
13048
|
+
/** Render `text[segStart, segEnd)` with every `query` match wrapped in a search
|
|
13049
|
+
* highlight, keeping the syntax highlight and intra-line chips on the non-match
|
|
13050
|
+
* parts. When no match is present it renders exactly as before (syntax / intra /
|
|
13051
|
+
* plain). `segStart`/`segEnd` let the caller render a wrapped sub-line range. */
|
|
13052
|
+
function textWithSearch(text, runs, intra, query, segStart = 0, segEnd = text.length, current = false) {
|
|
13053
|
+
const segText = text.slice(segStart, segEnd);
|
|
13054
|
+
const ranges = matchRangesOf(segText, query);
|
|
13055
|
+
if (ranges.length === 0) return intra !== void 0 && intra.length > 0 ? renderIntra(runs, intra, segStart, segEnd) : runs !== void 0 && runs.length > 0 ? clipRuns(runs, segStart, segEnd) : segText === "" ? "\xA0" : segText;
|
|
13056
|
+
const nodes = [];
|
|
13057
|
+
const push = (absStart, absEnd, isMatch) => {
|
|
13058
|
+
if (isMatch) {
|
|
13059
|
+
nodes.push((0, react_jsx_runtime.jsx)("span", {
|
|
13060
|
+
className: current ? PendingPanel_module_css_default.searchMatchCurrent : PendingPanel_module_css_default.searchMatch,
|
|
13061
|
+
"data-diff-search-match": current ? "current" : "hit",
|
|
13062
|
+
children: text.slice(absStart, absEnd)
|
|
13063
|
+
}, nodes.length));
|
|
13064
|
+
return;
|
|
13065
|
+
}
|
|
13066
|
+
if (intra !== void 0 && intra.length > 0) nodes.push((0, react_jsx_runtime.jsx)("span", { children: renderIntra(runs, intra, absStart, absEnd) }, nodes.length));
|
|
13067
|
+
else if (runs !== void 0 && runs.length > 0) nodes.push((0, react_jsx_runtime.jsx)("span", { children: clipRuns(runs, absStart, absEnd) }, nodes.length));
|
|
13068
|
+
else nodes.push((0, react_jsx_runtime.jsx)("span", { children: text.slice(absStart, absEnd) }, nodes.length));
|
|
13069
|
+
};
|
|
13070
|
+
let cursor = segStart;
|
|
13071
|
+
for (const [s, e] of ranges) {
|
|
13072
|
+
const absS = segStart + s;
|
|
13073
|
+
const absE = segStart + e;
|
|
13074
|
+
if (absS > cursor) push(cursor, absS, false);
|
|
13075
|
+
push(absS, absE, true);
|
|
13076
|
+
cursor = absE;
|
|
13077
|
+
}
|
|
13078
|
+
if (cursor < segEnd) push(cursor, segEnd, false);
|
|
13079
|
+
return nodes;
|
|
13080
|
+
}
|
|
12690
13081
|
/**
|
|
12691
13082
|
* One rendered diff row, memoized so a poll or an unrelated state change
|
|
12692
13083
|
* does not re-render rows whose content, highlight, and focus are unchanged.
|
|
@@ -12695,22 +13086,18 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
12695
13086
|
* is `wrappedLines.length * 22` by construction.
|
|
12696
13087
|
*/
|
|
12697
13088
|
const DiffRow = (0, react.memo)(function DiffRow(props) {
|
|
12698
|
-
const { index, row, runs, focused, searchHit, searchCurrent, onRowHover, wrappedLines } = props;
|
|
13089
|
+
const { index, row, runs, focused, searchHit, searchCurrent, searchQuery, onRowHover, wrappedLines } = props;
|
|
12699
13090
|
const lineNumber = row.kind === "del" ? row.oldLine : row.newLine;
|
|
12700
13091
|
const sideRuns = row.kind === "del" ? runs?.oldRuns : runs?.newRuns;
|
|
12701
13092
|
const lineRuns = lineNumber === void 0 ? void 0 : sideRuns?.[lineNumber - 1];
|
|
12702
13093
|
let code;
|
|
12703
|
-
if (wrappedLines === void 0) code = lineRuns
|
|
12704
|
-
style: span.style,
|
|
12705
|
-
children: span.text
|
|
12706
|
-
}, spanIndex)) : row.text === "" ? "\xA0" : row.text;
|
|
13094
|
+
if (wrappedLines === void 0) code = textWithSearch(row.text, lineRuns, void 0, searchQuery, 0, row.text.length, searchCurrent);
|
|
12707
13095
|
else {
|
|
12708
|
-
const highlighted = lineRuns !== void 0 && lineRuns.length > 0;
|
|
12709
13096
|
let offset = 0;
|
|
12710
13097
|
code = wrappedLines.map((line, lineIndex) => {
|
|
12711
13098
|
const start = offset;
|
|
12712
13099
|
offset += line.length;
|
|
12713
|
-
const content =
|
|
13100
|
+
const content = textWithSearch(row.text, lineRuns, void 0, searchQuery, start, offset, searchCurrent);
|
|
12714
13101
|
return (0, react_jsx_runtime.jsx)("div", {
|
|
12715
13102
|
className: PendingPanel_module_css_default.subline,
|
|
12716
13103
|
children: content
|
|
@@ -12729,10 +13116,12 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
12729
13116
|
children: [
|
|
12730
13117
|
(0, react_jsx_runtime.jsx)("span", {
|
|
12731
13118
|
className: PendingPanel_module_css_default.gutter,
|
|
13119
|
+
"data-diff-gutter": true,
|
|
12732
13120
|
children: row.oldLine ?? ""
|
|
12733
13121
|
}),
|
|
12734
13122
|
(0, react_jsx_runtime.jsx)("span", {
|
|
12735
13123
|
className: PendingPanel_module_css_default.gutter,
|
|
13124
|
+
"data-diff-gutter": true,
|
|
12736
13125
|
children: row.newLine ?? ""
|
|
12737
13126
|
}),
|
|
12738
13127
|
(0, react_jsx_runtime.jsx)("span", {
|
|
@@ -12806,20 +13195,23 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
12806
13195
|
});
|
|
12807
13196
|
return blocks;
|
|
12808
13197
|
}
|
|
13198
|
+
/** Row indices whose text contains `query` (case-insensitive); empty for ''. */
|
|
13199
|
+
function matchingRows(rows, query) {
|
|
13200
|
+
if (query === "") return [];
|
|
13201
|
+
const lower = query.toLowerCase();
|
|
13202
|
+
const out = [];
|
|
13203
|
+
for (let i = 0; i < rows.length; i++) if (rows[i].text.toLowerCase().includes(lower)) out.push(i);
|
|
13204
|
+
return out;
|
|
13205
|
+
}
|
|
12809
13206
|
/** One side's line-content for the split view: the highlighted runs or plain text. */
|
|
12810
|
-
function splitSideContent(side, wrapped, runs, intra) {
|
|
13207
|
+
function splitSideContent(side, wrapped, runs, intra, query, current) {
|
|
12811
13208
|
if (side === void 0) return "";
|
|
12812
|
-
|
|
12813
|
-
const hasIntra = intra !== void 0 && intra.length > 0;
|
|
12814
|
-
if (wrapped === void 0) return hasIntra ? renderIntra(runs, intra, 0, side.text.length) : highlighted ? runs.map((span, i) => (0, react_jsx_runtime.jsx)("span", {
|
|
12815
|
-
style: span.style,
|
|
12816
|
-
children: span.text
|
|
12817
|
-
}, i)) : side.text === "" ? "\xA0" : side.text;
|
|
13209
|
+
if (wrapped === void 0) return textWithSearch(side.text, runs, intra, query, 0, side.text.length, current);
|
|
12818
13210
|
let offset = 0;
|
|
12819
13211
|
return wrapped.map((line, i) => {
|
|
12820
13212
|
const start = offset;
|
|
12821
13213
|
offset += line.length;
|
|
12822
|
-
const content =
|
|
13214
|
+
const content = textWithSearch(side.text, runs, intra, query, start, offset, current);
|
|
12823
13215
|
return (0, react_jsx_runtime.jsx)("div", {
|
|
12824
13216
|
className: PendingPanel_module_css_default.subline,
|
|
12825
13217
|
children: content
|
|
@@ -12835,7 +13227,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
12835
13227
|
* one side is longer. The gutter and code are top-aligned so sub-lines line up
|
|
12836
13228
|
* across the divider.
|
|
12837
13229
|
*/
|
|
12838
|
-
function SplitSideRow({ index, side, wrapped, runs, kind, isLeft, height, focused, searchHit, searchCurrent, onHover, intra }) {
|
|
13230
|
+
function SplitSideRow({ index, side, wrapped, runs, kind, isLeft, height, focused, searchHit, searchCurrent, searchQuery, onHover, intra }) {
|
|
12839
13231
|
const tint = isLeft ? kind === "del" || kind === "replace" ? PendingPanel_module_css_default.splitLdel : "" : kind === "add" || kind === "replace" ? PendingPanel_module_css_default.splitRadd : "";
|
|
12840
13232
|
return (0, react_jsx_runtime.jsxs)("div", {
|
|
12841
13233
|
className: PendingPanel_module_css_default.line,
|
|
@@ -12848,16 +13240,17 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
12848
13240
|
onMouseEnter: onHover,
|
|
12849
13241
|
children: [(0, react_jsx_runtime.jsx)("span", {
|
|
12850
13242
|
className: PendingPanel_module_css_default.gutter,
|
|
13243
|
+
"data-diff-gutter": true,
|
|
12851
13244
|
children: side?.line ?? ""
|
|
12852
13245
|
}), (0, react_jsx_runtime.jsx)("span", {
|
|
12853
13246
|
className: `${PendingPanel_module_css_default.code} ${tint}`,
|
|
12854
13247
|
"data-diff-code": true,
|
|
12855
|
-
children: splitSideContent(side, wrapped, runs, intra)
|
|
13248
|
+
children: splitSideContent(side, wrapped, runs, intra, searchQuery, searchCurrent)
|
|
12856
13249
|
})]
|
|
12857
13250
|
});
|
|
12858
13251
|
}
|
|
12859
13252
|
/** The two-column (side-by-side) whole-file diff view. */
|
|
12860
|
-
const SplitDiff = (0, react.forwardRef)(function SplitDiff({ file, model, runs, langWrap, tabWidthSpaces, busy, t, selection, leadRows, onBlockKeep, onBlockRevert }, ref) {
|
|
13253
|
+
const SplitDiff = (0, react.forwardRef)(function SplitDiff({ file, model, runs, langWrap, tabWidthSpaces, busy, t, selection, leadRows, onBlockKeep, onBlockRevert, onWrapToast }, ref) {
|
|
12861
13254
|
const { pairs, pairOfRow } = (0, react.useMemo)(() => computeSideBySideDiff(model.diff.rows, true), [model]);
|
|
12862
13255
|
const pairCount = pairs.length;
|
|
12863
13256
|
const pairRowIndices = (0, react.useMemo)(() => {
|
|
@@ -12879,6 +13272,11 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
12879
13272
|
const [hoveredBlock, setHoveredBlock] = (0, react.useState)(void 0);
|
|
12880
13273
|
const [focus, setFocus] = (0, react.useState)(0);
|
|
12881
13274
|
const [flashKey, setFlashKey] = (0, react.useState)(0);
|
|
13275
|
+
const pinShakeRef = (0, react.useRef)(false);
|
|
13276
|
+
const bumpFlash = (shake) => {
|
|
13277
|
+
pinShakeRef.current = shake;
|
|
13278
|
+
setFlashKey((prev) => prev + 1);
|
|
13279
|
+
};
|
|
12882
13280
|
const hoveredBlockRef = (0, react.useRef)(void 0);
|
|
12883
13281
|
const leftColRef = (0, react.useRef)(null);
|
|
12884
13282
|
const rightColRef = (0, react.useRef)(null);
|
|
@@ -12895,7 +13293,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
12895
13293
|
(0, react.useEffect)(() => {
|
|
12896
13294
|
setFocus(0);
|
|
12897
13295
|
bodyRef.current?.focus();
|
|
12898
|
-
|
|
13296
|
+
bumpFlash(false);
|
|
12899
13297
|
setHoveredBlock(void 0);
|
|
12900
13298
|
setSearchOpen(false);
|
|
12901
13299
|
setSearchQuery("");
|
|
@@ -13025,6 +13423,30 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
13025
13423
|
bodyRef.current?.focus();
|
|
13026
13424
|
};
|
|
13027
13425
|
const openSearch = () => {
|
|
13426
|
+
if (searchOpen) {
|
|
13427
|
+
searchInputRef.current?.focus();
|
|
13428
|
+
searchInputRef.current?.select();
|
|
13429
|
+
return;
|
|
13430
|
+
}
|
|
13431
|
+
const live = window.getSelection();
|
|
13432
|
+
const liveRange = splitRowRangeOf(live);
|
|
13433
|
+
const range = liveRange !== void 0 ? liveRange : selection;
|
|
13434
|
+
const liveText = (live?.toString() ?? "").trim();
|
|
13435
|
+
const value = liveText !== "" && !liveText.includes("\n") ? liveText : "";
|
|
13436
|
+
setSearchQuery(value);
|
|
13437
|
+
const matches = value === "" ? [] : searchPairs(pairs, value);
|
|
13438
|
+
let index = 0;
|
|
13439
|
+
if (matches.length > 0) {
|
|
13440
|
+
const inSel = range === void 0 ? -1 : matches.findIndex((i) => i >= range.start && i <= range.end);
|
|
13441
|
+
if (inSel !== -1) index = inSel;
|
|
13442
|
+
else {
|
|
13443
|
+
const body = bodyRef.current;
|
|
13444
|
+
const top = body === null ? 0 : pairAtY(body.scrollTop);
|
|
13445
|
+
const at = matches.findIndex((i) => i >= top);
|
|
13446
|
+
index = at === -1 ? 0 : at;
|
|
13447
|
+
}
|
|
13448
|
+
}
|
|
13449
|
+
setSearchIndex(index);
|
|
13028
13450
|
setSearchOpen(true);
|
|
13029
13451
|
requestAnimationFrame(() => {
|
|
13030
13452
|
searchInputRef.current?.focus();
|
|
@@ -13040,7 +13462,15 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
13040
13462
|
if (pairIndex === void 0) return;
|
|
13041
13463
|
const body = bodyRef.current;
|
|
13042
13464
|
if (body === null) return;
|
|
13043
|
-
|
|
13465
|
+
if (body.clientHeight <= 0) return;
|
|
13466
|
+
const viewTop = body.scrollTop;
|
|
13467
|
+
const viewBottom = viewTop + body.clientHeight;
|
|
13468
|
+
const pairTop = off(pairIndex);
|
|
13469
|
+
const pairBottom = pairTop + ROW_HEIGHT_PX;
|
|
13470
|
+
let target;
|
|
13471
|
+
if (pairTop < viewTop) target = pairTop;
|
|
13472
|
+
else if (pairBottom > viewBottom) target = pairBottom - body.clientHeight;
|
|
13473
|
+
if (target === void 0) return;
|
|
13044
13474
|
const clamped = Math.max(0, Math.min(target, body.scrollHeight - body.clientHeight));
|
|
13045
13475
|
if (body.scrollTop !== clamped) body.scrollTop = clamped;
|
|
13046
13476
|
setScrollTop(clamped);
|
|
@@ -13056,7 +13486,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
13056
13486
|
const next = Math.max(0, Math.min(operated, count - 1));
|
|
13057
13487
|
setFocus(next);
|
|
13058
13488
|
setHoveredBlock(void 0);
|
|
13059
|
-
|
|
13489
|
+
bumpFlash(false);
|
|
13060
13490
|
};
|
|
13061
13491
|
const pairAtY = (y) => {
|
|
13062
13492
|
if (pairOffsets === null) return Math.floor(y / ROW_HEIGHT_PX);
|
|
@@ -13077,20 +13507,52 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
13077
13507
|
end = Math.max(end, selection.end + 1);
|
|
13078
13508
|
}
|
|
13079
13509
|
const visiblePairs = pairs.slice(start, end);
|
|
13080
|
-
const
|
|
13081
|
-
|
|
13510
|
+
const wrapArmedRef = (0, react.useRef)(0);
|
|
13511
|
+
const jump = (direction, wrapGuard = false) => {
|
|
13512
|
+
const count = blockOfPair.length;
|
|
13513
|
+
if (count === 0) return;
|
|
13514
|
+
if (wrapGuard) {
|
|
13515
|
+
if (count === 1) onWrapToast(t("panel.blockSingle"));
|
|
13516
|
+
else if (direction === 1 && focus === count - 1 || direction === -1 && focus === 0) {
|
|
13517
|
+
if (wrapArmedRef.current !== direction) {
|
|
13518
|
+
wrapArmedRef.current = direction;
|
|
13519
|
+
onWrapToast(t(direction === 1 ? "panel.blockAtEnd" : "panel.blockAtStart"));
|
|
13520
|
+
bumpFlash(true);
|
|
13521
|
+
return;
|
|
13522
|
+
}
|
|
13523
|
+
wrapArmedRef.current = 0;
|
|
13524
|
+
} else wrapArmedRef.current = 0;
|
|
13525
|
+
}
|
|
13082
13526
|
setFocus((current) => {
|
|
13083
|
-
if (direction === -1) return (current - 1 +
|
|
13527
|
+
if (direction === -1) return (current - 1 + count) % count;
|
|
13084
13528
|
const top = bodyRef.current?.scrollTop ?? 0;
|
|
13085
|
-
for (let index = current + 1; index <
|
|
13529
|
+
for (let index = current + 1; index < count; index++) if (off(blockOfPair[index].start) >= top) return index;
|
|
13086
13530
|
return 0;
|
|
13087
13531
|
});
|
|
13088
|
-
|
|
13532
|
+
bumpFlash(false);
|
|
13533
|
+
};
|
|
13534
|
+
const stepBlock = (direction) => {
|
|
13535
|
+
const count = blockOfPair.length;
|
|
13536
|
+
if (count === 0) return;
|
|
13537
|
+
const target = ((hoveredBlock ?? focus) + direction + count) % count;
|
|
13538
|
+
setHoveredBlock(target);
|
|
13539
|
+
setFocus(target);
|
|
13540
|
+
bumpFlash(false);
|
|
13541
|
+
};
|
|
13542
|
+
const searchNext = (direction) => {
|
|
13543
|
+
if (!searchOpen) return false;
|
|
13544
|
+
goSearch(direction);
|
|
13545
|
+
return true;
|
|
13089
13546
|
};
|
|
13090
13547
|
(0, react.useImperativeHandle)(ref, () => ({
|
|
13091
13548
|
jump,
|
|
13092
|
-
openSearch
|
|
13093
|
-
|
|
13549
|
+
openSearch,
|
|
13550
|
+
searchNext
|
|
13551
|
+
}), [
|
|
13552
|
+
jump,
|
|
13553
|
+
openSearch,
|
|
13554
|
+
searchNext
|
|
13555
|
+
]);
|
|
13094
13556
|
(0, react.useLayoutEffect)(() => {
|
|
13095
13557
|
if (pairCount === 0) return;
|
|
13096
13558
|
const block = blockOfPair[focus];
|
|
@@ -13108,9 +13570,16 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
13108
13570
|
setScrollTop(body.scrollTop);
|
|
13109
13571
|
const count = blockOfPair.length;
|
|
13110
13572
|
if (count === 0) return;
|
|
13111
|
-
const anchor = body.scrollTop + leadRows * ROW_HEIGHT_PX;
|
|
13112
13573
|
let ref = -1;
|
|
13113
|
-
|
|
13574
|
+
if (body.clientHeight > 0) {
|
|
13575
|
+
const viewportBottom = body.scrollTop + body.clientHeight;
|
|
13576
|
+
if (body.scrollTop <= NAV_ANCHOR_TOLERANCE_PX) ref = 0;
|
|
13577
|
+
else if (body.scrollHeight - viewportBottom <= NAV_ANCHOR_TOLERANCE_PX) ref = count - 1;
|
|
13578
|
+
}
|
|
13579
|
+
if (ref === -1) {
|
|
13580
|
+
const anchor = body.scrollTop + leadRows * ROW_HEIGHT_PX;
|
|
13581
|
+
for (let index = 0; index < count; index++) if (off(blockOfPair[index].start) <= anchor + NAV_ANCHOR_TOLERANCE_PX) ref = index;
|
|
13582
|
+
}
|
|
13114
13583
|
setFocus(ref === -1 ? 0 : ref);
|
|
13115
13584
|
};
|
|
13116
13585
|
const inFocused = (k) => {
|
|
@@ -13165,6 +13634,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
13165
13634
|
focused: inFocused(index),
|
|
13166
13635
|
searchHit: searchHitSet.has(index),
|
|
13167
13636
|
searchCurrent: index === currentSearchPair,
|
|
13637
|
+
searchQuery,
|
|
13168
13638
|
onHover: () => onPairHover(index),
|
|
13169
13639
|
intra: sideIndex?.left === void 0 ? void 0 : model.intra.get(sideIndex.left)
|
|
13170
13640
|
}, index);
|
|
@@ -13206,6 +13676,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
13206
13676
|
focused: inFocused(index),
|
|
13207
13677
|
searchHit: searchHitSet.has(index),
|
|
13208
13678
|
searchCurrent: index === currentSearchPair,
|
|
13679
|
+
searchQuery,
|
|
13209
13680
|
onHover: () => onPairHover(index),
|
|
13210
13681
|
intra: sideIndex?.right === void 0 ? void 0 : model.intra.get(sideIndex.right)
|
|
13211
13682
|
}, index);
|
|
@@ -13267,8 +13738,15 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
13267
13738
|
value: searchQuery,
|
|
13268
13739
|
placeholder: t("panel.searchPlaceholder"),
|
|
13269
13740
|
onChange: (event) => {
|
|
13270
|
-
|
|
13271
|
-
|
|
13741
|
+
const value = event.target.value;
|
|
13742
|
+
setSearchQuery(value);
|
|
13743
|
+
if (value !== "") {
|
|
13744
|
+
const body = bodyRef.current;
|
|
13745
|
+
const matches = searchPairs(pairs, value);
|
|
13746
|
+
const anchor = currentSearchPair ?? (body === null ? 0 : pairAtY(body.scrollTop));
|
|
13747
|
+
const at = matches.findIndex((index) => index >= anchor);
|
|
13748
|
+
setSearchIndex(at === -1 ? 0 : at);
|
|
13749
|
+
} else setSearchIndex(0);
|
|
13272
13750
|
},
|
|
13273
13751
|
onKeyDown: (event) => {
|
|
13274
13752
|
if (event.key === "Enter") {
|
|
@@ -13282,27 +13760,37 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
13282
13760
|
"data-diff-search-count": true,
|
|
13283
13761
|
children: searchMatches.length === 0 ? "0/0" : `${searchIndex % searchMatches.length + 1}/${searchMatches.length}`
|
|
13284
13762
|
}),
|
|
13285
|
-
(0, react_jsx_runtime.jsx)(
|
|
13286
|
-
|
|
13287
|
-
|
|
13288
|
-
|
|
13289
|
-
|
|
13290
|
-
|
|
13291
|
-
|
|
13292
|
-
|
|
13293
|
-
|
|
13294
|
-
|
|
13763
|
+
(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
13764
|
+
label: `${t("action.prevDiff")} (Shift+F3)`,
|
|
13765
|
+
side: "bottom",
|
|
13766
|
+
delayMs: 500,
|
|
13767
|
+
children: (0, react_jsx_runtime.jsx)("button", {
|
|
13768
|
+
type: "button",
|
|
13769
|
+
className: `${PendingPanel_module_css_default.action} ${PendingPanel_module_css_default.iconAction}`,
|
|
13770
|
+
"data-diff-search-prev": true,
|
|
13771
|
+
"aria-label": t("action.prevDiff"),
|
|
13772
|
+
disabled: searchMatches.length === 0,
|
|
13773
|
+
onClick: () => {
|
|
13774
|
+
goSearch(-1);
|
|
13775
|
+
},
|
|
13776
|
+
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronUpOutline14, { size: 14 })
|
|
13777
|
+
})
|
|
13295
13778
|
}),
|
|
13296
|
-
(0, react_jsx_runtime.jsx)(
|
|
13297
|
-
|
|
13298
|
-
|
|
13299
|
-
|
|
13300
|
-
|
|
13301
|
-
|
|
13302
|
-
|
|
13303
|
-
|
|
13304
|
-
|
|
13305
|
-
|
|
13779
|
+
(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
13780
|
+
label: `${t("action.nextDiff")} (F3)`,
|
|
13781
|
+
side: "bottom",
|
|
13782
|
+
delayMs: 500,
|
|
13783
|
+
children: (0, react_jsx_runtime.jsx)("button", {
|
|
13784
|
+
type: "button",
|
|
13785
|
+
className: `${PendingPanel_module_css_default.action} ${PendingPanel_module_css_default.iconAction}`,
|
|
13786
|
+
"data-diff-search-next": true,
|
|
13787
|
+
"aria-label": t("action.nextDiff"),
|
|
13788
|
+
disabled: searchMatches.length === 0,
|
|
13789
|
+
onClick: () => {
|
|
13790
|
+
goSearch(1);
|
|
13791
|
+
},
|
|
13792
|
+
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, { size: 14 })
|
|
13793
|
+
})
|
|
13306
13794
|
}),
|
|
13307
13795
|
(0, react_jsx_runtime.jsx)("button", {
|
|
13308
13796
|
type: "button",
|
|
@@ -13315,7 +13803,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
13315
13803
|
]
|
|
13316
13804
|
}),
|
|
13317
13805
|
focusedBlock !== void 0 && flashKey > 0 && (0, react_jsx_runtime.jsx)("div", {
|
|
13318
|
-
className: PendingPanel_module_css_default.blockFlash,
|
|
13806
|
+
className: pinShakeRef.current ? `${PendingPanel_module_css_default.blockFlash} ${PendingPanel_module_css_default.blockFlashShake}` : PendingPanel_module_css_default.blockFlash,
|
|
13319
13807
|
"data-diff-block-flash": true,
|
|
13320
13808
|
style: {
|
|
13321
13809
|
top: flashTop,
|
|
@@ -13341,7 +13829,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
13341
13829
|
"data-diff-block-prev": true,
|
|
13342
13830
|
"aria-label": t("action.prevDiff"),
|
|
13343
13831
|
disabled: busy,
|
|
13344
|
-
onClick: () =>
|
|
13832
|
+
onClick: () => stepBlock(-1),
|
|
13345
13833
|
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronUpOutline14, { size: 14 })
|
|
13346
13834
|
}),
|
|
13347
13835
|
(0, react_jsx_runtime.jsx)("button", {
|
|
@@ -13350,7 +13838,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
13350
13838
|
"data-diff-block-next": true,
|
|
13351
13839
|
"aria-label": t("action.nextDiff"),
|
|
13352
13840
|
disabled: busy,
|
|
13353
|
-
onClick: () =>
|
|
13841
|
+
onClick: () => stepBlock(1),
|
|
13354
13842
|
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, { size: 14 })
|
|
13355
13843
|
}),
|
|
13356
13844
|
(0, react_jsx_runtime.jsx)("button", {
|
|
@@ -13490,6 +13978,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
13490
13978
|
}
|
|
13491
13979
|
if (!(node instanceof Element)) return;
|
|
13492
13980
|
const el = node;
|
|
13981
|
+
if (el.dataset.diffGutter !== void 0) return;
|
|
13493
13982
|
if ((el.dataset.diffRow !== void 0 || el.dataset.diffSplitRow !== void 0 || el.dataset.diffSplitIndex !== void 0) && !atLineStart) push("\n");
|
|
13494
13983
|
for (const child of node.childNodes) walk(child);
|
|
13495
13984
|
};
|
|
@@ -13597,6 +14086,10 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
13597
14086
|
const next = !splitView;
|
|
13598
14087
|
setSplitView(next);
|
|
13599
14088
|
setSplitMode(next);
|
|
14089
|
+
if (!next) {
|
|
14090
|
+
setScrollTop(0);
|
|
14091
|
+
setScrollTick((tick) => tick + 1);
|
|
14092
|
+
}
|
|
13600
14093
|
};
|
|
13601
14094
|
const splitDiffRef = (0, react.useRef)(null);
|
|
13602
14095
|
const model = (0, react.useMemo)(() => {
|
|
@@ -13675,17 +14168,23 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
13675
14168
|
const [hScrollbarPx, setHScrollbarPx] = (0, react.useState)(0);
|
|
13676
14169
|
const [hoveredBlock, setHoveredBlock] = (0, react.useState)(void 0);
|
|
13677
14170
|
const [selection, setSelection] = (0, react.useState)(void 0);
|
|
14171
|
+
const selectionTextRef = (0, react.useRef)("");
|
|
13678
14172
|
const [copied, setCopied] = (0, react.useState)(false);
|
|
13679
14173
|
const [searchOpen, setSearchOpen] = (0, react.useState)(false);
|
|
13680
14174
|
const [searchQuery, setSearchQuery] = (0, react.useState)("");
|
|
13681
14175
|
const [searchIndex, setSearchIndex] = (0, react.useState)(0);
|
|
13682
14176
|
const searchInputRef = (0, react.useRef)(null);
|
|
13683
14177
|
const [flashKey, setFlashKey] = (0, react.useState)(0);
|
|
14178
|
+
const pinShakeRef = (0, react.useRef)(false);
|
|
14179
|
+
const bumpFlash = (shake) => {
|
|
14180
|
+
pinShakeRef.current = shake;
|
|
14181
|
+
setFlashKey((prev) => prev + 1);
|
|
14182
|
+
};
|
|
13684
14183
|
(0, react.useEffect)(() => {
|
|
13685
14184
|
setFocus(0);
|
|
13686
14185
|
setScrollTick((tick) => tick + 1);
|
|
13687
14186
|
bodyRef.current?.focus();
|
|
13688
|
-
|
|
14187
|
+
bumpFlash(false);
|
|
13689
14188
|
setHoveredBlock(void 0);
|
|
13690
14189
|
setSelection(void 0);
|
|
13691
14190
|
setLangOverride(void 0);
|
|
@@ -13699,7 +14198,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
13699
14198
|
if (undoFlash === 0) return;
|
|
13700
14199
|
setFocus(0);
|
|
13701
14200
|
setScrollTick((tick) => tick + 1);
|
|
13702
|
-
|
|
14201
|
+
bumpFlash(false);
|
|
13703
14202
|
}, [undoFlash]);
|
|
13704
14203
|
const blockRanges = (0, react.useMemo)(() => {
|
|
13705
14204
|
return model.blocks.map((block) => blockRangesOf(model.diff.rows, block));
|
|
@@ -13730,27 +14229,74 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
13730
14229
|
end: last.end
|
|
13731
14230
|
});
|
|
13732
14231
|
}, [coveredBlockIndices, model]);
|
|
13733
|
-
const searchMatches = (0, react.useMemo)(() =>
|
|
13734
|
-
if (searchQuery === "") return [];
|
|
13735
|
-
const lower = searchQuery.toLowerCase();
|
|
13736
|
-
const matches = [];
|
|
13737
|
-
for (let index = 0; index < model.diff.rows.length; index++) if (model.diff.rows[index].text.toLowerCase().includes(lower)) matches.push(index);
|
|
13738
|
-
return matches;
|
|
13739
|
-
}, [model, searchQuery]);
|
|
14232
|
+
const searchMatches = (0, react.useMemo)(() => matchingRows(model.diff.rows, searchQuery), [model, searchQuery]);
|
|
13740
14233
|
const searchHitSet = (0, react.useMemo)(() => new Set(searchMatches), [searchMatches]);
|
|
13741
14234
|
const currentSearchRow = searchMatches.length === 0 ? void 0 : searchMatches[searchIndex % searchMatches.length];
|
|
13742
14235
|
const goSearch = (direction) => {
|
|
13743
14236
|
if (searchMatches.length === 0) return;
|
|
14237
|
+
if (cursorPosRef.current !== void 0) {
|
|
14238
|
+
setSearchIndex(startIndexFor(searchQuery));
|
|
14239
|
+
return;
|
|
14240
|
+
}
|
|
13744
14241
|
setSearchIndex((current) => (current + direction + searchMatches.length) % searchMatches.length);
|
|
13745
14242
|
};
|
|
14243
|
+
/**
|
|
14244
|
+
* The search's start index for `value`. The recorded cursor (the selection, if
|
|
14245
|
+
* any) anchors ONE search, then is consumed; after that the current highlight
|
|
14246
|
+
* drives subsequent searches, and, with neither set, the viewport top is the
|
|
14247
|
+
* start (the "no cursor" fallback).
|
|
14248
|
+
*/
|
|
14249
|
+
const startIndexFor = (value) => {
|
|
14250
|
+
const matches = value === "" ? [] : matchingRows(model.diff.rows, value);
|
|
14251
|
+
const pos = cursorPosRef.current;
|
|
14252
|
+
cursorPosRef.current = void 0;
|
|
14253
|
+
if (matches.length === 0) return 0;
|
|
14254
|
+
const inPos = pos === void 0 ? -1 : matches.findIndex((i) => i >= pos.start && i <= pos.end);
|
|
14255
|
+
if (inPos !== -1) return inPos;
|
|
14256
|
+
const body = bodyRef.current;
|
|
14257
|
+
const fromRow = pos !== void 0 ? pos.start : currentSearchRow ?? (body === null ? 0 : rowAtY(body.scrollTop));
|
|
14258
|
+
const at = matches.findIndex((i) => i >= fromRow);
|
|
14259
|
+
return at === -1 ? 0 : at;
|
|
14260
|
+
};
|
|
14261
|
+
const cursorPosRef = (0, react.useRef)(void 0);
|
|
14262
|
+
const lastRecordedCursorRef = (0, react.useRef)(void 0);
|
|
14263
|
+
const openSearchWithSelection = () => {
|
|
14264
|
+
if (searchOpen) {
|
|
14265
|
+
searchInputRef.current?.focus();
|
|
14266
|
+
searchInputRef.current?.select();
|
|
14267
|
+
return;
|
|
14268
|
+
}
|
|
14269
|
+
const live = window.getSelection();
|
|
14270
|
+
const liveRange = splitView ? splitRowRangeOf(live) : rowRangeOf(live);
|
|
14271
|
+
const pos = liveRange !== void 0 ? liveRange : selection;
|
|
14272
|
+
cursorPosRef.current = pos;
|
|
14273
|
+
lastRecordedCursorRef.current = pos;
|
|
14274
|
+
const liveText = (live?.toString() ?? "").trim();
|
|
14275
|
+
const value = liveText !== "" && !liveText.includes("\n") ? liveText : selectionTextRef.current;
|
|
14276
|
+
setSearchQuery(value);
|
|
14277
|
+
setSearchIndex(startIndexFor(value));
|
|
14278
|
+
setSearchOpen(true);
|
|
14279
|
+
requestAnimationFrame(() => {
|
|
14280
|
+
searchInputRef.current?.focus();
|
|
14281
|
+
searchInputRef.current?.select();
|
|
14282
|
+
});
|
|
14283
|
+
};
|
|
14284
|
+
const openSearchRef = (0, react.useRef)(openSearchWithSelection);
|
|
14285
|
+
openSearchRef.current = openSearchWithSelection;
|
|
14286
|
+
const searchOpenRef = (0, react.useRef)(searchOpen);
|
|
14287
|
+
searchOpenRef.current = searchOpen;
|
|
13746
14288
|
const toggleSearch = () => {
|
|
13747
14289
|
if (searchOpen) {
|
|
14290
|
+
cursorPosRef.current = void 0;
|
|
14291
|
+
lastRecordedCursorRef.current = void 0;
|
|
13748
14292
|
setSearchOpen(false);
|
|
13749
14293
|
setSearchQuery("");
|
|
13750
14294
|
setSearchIndex(0);
|
|
13751
|
-
} else
|
|
14295
|
+
} else openSearchWithSelection();
|
|
13752
14296
|
};
|
|
13753
14297
|
const closeSearch = () => {
|
|
14298
|
+
cursorPosRef.current = void 0;
|
|
14299
|
+
lastRecordedCursorRef.current = void 0;
|
|
13754
14300
|
setSearchOpen(false);
|
|
13755
14301
|
setSearchQuery("");
|
|
13756
14302
|
setSearchIndex(0);
|
|
@@ -13758,15 +14304,20 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
13758
14304
|
(0, react.useEffect)(() => {
|
|
13759
14305
|
if (searchOpen) searchInputRef.current?.focus();
|
|
13760
14306
|
}, [searchOpen]);
|
|
13761
|
-
(0, react.useEffect)(() => {
|
|
13762
|
-
setSearchIndex(0);
|
|
13763
|
-
}, [searchQuery, file.id]);
|
|
13764
14307
|
(0, react.useLayoutEffect)(() => {
|
|
13765
14308
|
const row = currentSearchRow;
|
|
13766
14309
|
if (row === void 0) return;
|
|
13767
14310
|
const body = bodyRef.current;
|
|
13768
14311
|
if (body === null) return;
|
|
13769
|
-
|
|
14312
|
+
if (body.clientHeight <= 0) return;
|
|
14313
|
+
const viewTop = body.scrollTop;
|
|
14314
|
+
const viewBottom = viewTop + body.clientHeight;
|
|
14315
|
+
const rowTop = offsetOf(row);
|
|
14316
|
+
const rowBottom = rowTop + extentOf(row, row);
|
|
14317
|
+
let target;
|
|
14318
|
+
if (rowTop < viewTop) target = rowTop;
|
|
14319
|
+
else if (rowBottom > viewBottom) target = rowBottom - body.clientHeight;
|
|
14320
|
+
if (target === void 0) return;
|
|
13770
14321
|
const clamped = Math.max(0, Math.min(target, body.scrollHeight - body.clientHeight));
|
|
13771
14322
|
if (body.scrollTop !== clamped) body.scrollTop = clamped;
|
|
13772
14323
|
setScrollTop(clamped);
|
|
@@ -13838,14 +14389,14 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
13838
14389
|
}
|
|
13839
14390
|
const visibleRows = rows.slice(start, end);
|
|
13840
14391
|
const blockEnd = hoveredBlock === void 0 ? void 0 : model.blocks[hoveredBlock]?.end;
|
|
13841
|
-
const blockActionsTop = blockEnd === void 0 ? 0 : Math.max(0, Math.min(offsetOf(blockEnd + 1) - scrollTop, Math.max(0, viewportHeight - BLOCK_ACTIONS_FRAME_PX)));
|
|
14392
|
+
const blockActionsTop = blockEnd === void 0 ? 0 : Math.max(0, Math.min(offsetOf(blockEnd + 1) - scrollTop - 2, Math.max(0, viewportHeight - BLOCK_ACTIONS_FRAME_PX)));
|
|
13842
14393
|
const selectionBlockEnd = (() => {
|
|
13843
14394
|
if (coveredBlockIndices.length === 0) return void 0;
|
|
13844
14395
|
const lastIndex = coveredBlockIndices[coveredBlockIndices.length - 1];
|
|
13845
14396
|
if (lastIndex === void 0) return void 0;
|
|
13846
14397
|
return model.blocks[lastIndex]?.end;
|
|
13847
14398
|
})();
|
|
13848
|
-
const selectionActionsTop = selectionBlockEnd === void 0 ? 0 : Math.max(0, Math.min(offsetOf(selectionBlockEnd + 1) - scrollTop, Math.max(0, viewportHeight - BLOCK_ACTIONS_FRAME_PX)));
|
|
14399
|
+
const selectionActionsTop = selectionBlockEnd === void 0 ? 0 : Math.max(0, Math.min(offsetOf(selectionBlockEnd + 1) - scrollTop - 2, Math.max(0, viewportHeight - BLOCK_ACTIONS_FRAME_PX)));
|
|
13849
14400
|
const widestLine = (0, react.useMemo)(() => {
|
|
13850
14401
|
let widest = 0;
|
|
13851
14402
|
for (const row of model.diff.rows) if (row.text.length > widest) widest = row.text.length;
|
|
@@ -13863,7 +14414,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
13863
14414
|
return () => {
|
|
13864
14415
|
observer?.disconnect();
|
|
13865
14416
|
};
|
|
13866
|
-
}, [file.id]);
|
|
14417
|
+
}, [file.id, splitView]);
|
|
13867
14418
|
(0, react.useEffect)(() => {
|
|
13868
14419
|
const body = bodyRef.current;
|
|
13869
14420
|
if (body === null) return;
|
|
@@ -13889,12 +14440,27 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
13889
14440
|
if (body.scrollTop !== clamped) body.scrollTop = clamped;
|
|
13890
14441
|
setScrollTop(clamped);
|
|
13891
14442
|
}, [scrollTick, rowOffsets === null]);
|
|
13892
|
-
const
|
|
14443
|
+
const wrapArmedRef = (0, react.useRef)(0);
|
|
14444
|
+
const jump = (direction, wrapGuard = false) => {
|
|
13893
14445
|
if (rowCount === 0) return;
|
|
14446
|
+
const count = model.blocks.length;
|
|
14447
|
+
if (count === 0) return;
|
|
14448
|
+
if (wrapGuard) {
|
|
14449
|
+
if (count === 1) onToast(t("panel.blockSingle"));
|
|
14450
|
+
else if (direction === 1 && focus === count - 1 || direction === -1 && focus === 0) {
|
|
14451
|
+
if (wrapArmedRef.current !== direction) {
|
|
14452
|
+
wrapArmedRef.current = direction;
|
|
14453
|
+
onToast(t(direction === 1 ? "panel.blockAtEnd" : "panel.blockAtStart"));
|
|
14454
|
+
bumpFlash(true);
|
|
14455
|
+
return;
|
|
14456
|
+
}
|
|
14457
|
+
wrapArmedRef.current = 0;
|
|
14458
|
+
} else wrapArmedRef.current = 0;
|
|
14459
|
+
}
|
|
13894
14460
|
setFocus((current) => {
|
|
13895
|
-
if (direction === -1) return (current - 1 +
|
|
14461
|
+
if (direction === -1) return (current - 1 + count) % count;
|
|
13896
14462
|
const top = bodyRef.current?.scrollTop ?? 0;
|
|
13897
|
-
for (let index = current + 1; index <
|
|
14463
|
+
for (let index = current + 1; index < count; index++) {
|
|
13898
14464
|
const block = model.blocks[index];
|
|
13899
14465
|
if (block === void 0) continue;
|
|
13900
14466
|
if (offsetOf(block.start) >= top) return index;
|
|
@@ -13902,14 +14468,14 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
13902
14468
|
return 0;
|
|
13903
14469
|
});
|
|
13904
14470
|
setScrollTick((tick) => tick + 1);
|
|
13905
|
-
|
|
14471
|
+
bumpFlash(false);
|
|
13906
14472
|
};
|
|
13907
|
-
const jumpBlock = (direction) => {
|
|
14473
|
+
const jumpBlock = (direction, wrapGuard = false) => {
|
|
13908
14474
|
if (splitView) {
|
|
13909
|
-
splitDiffRef.current?.jump(direction);
|
|
14475
|
+
splitDiffRef.current?.jump(direction, wrapGuard);
|
|
13910
14476
|
return;
|
|
13911
14477
|
}
|
|
13912
|
-
jump(direction);
|
|
14478
|
+
jump(direction, wrapGuard);
|
|
13913
14479
|
};
|
|
13914
14480
|
const jumpBlockRef = (0, react.useRef)(jumpBlock);
|
|
13915
14481
|
jumpBlockRef.current = jumpBlock;
|
|
@@ -13920,7 +14486,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
13920
14486
|
setHoveredBlock(target);
|
|
13921
14487
|
setFocus(target);
|
|
13922
14488
|
setScrollTick((tick) => tick + 1);
|
|
13923
|
-
|
|
14489
|
+
bumpFlash(false);
|
|
13924
14490
|
};
|
|
13925
14491
|
const runBlockAction = async (action, range, operated) => {
|
|
13926
14492
|
await (action === "keep" ? onBlockKeep(file.sessionId, file.id, range) : onBlockRevert(file.sessionId, file.id, range));
|
|
@@ -13930,7 +14496,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
13930
14496
|
setFocus(next);
|
|
13931
14497
|
setHoveredBlock(void 0);
|
|
13932
14498
|
setScrollTick((tick) => tick + 1);
|
|
13933
|
-
|
|
14499
|
+
bumpFlash(false);
|
|
13934
14500
|
};
|
|
13935
14501
|
const handleBlockAction = async (action) => {
|
|
13936
14502
|
if (busy || hoveredBlock === void 0) return;
|
|
@@ -13943,10 +14509,12 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
13943
14509
|
const firstCovered = coveredBlockIndices[0];
|
|
13944
14510
|
if (firstCovered === void 0) return;
|
|
13945
14511
|
await runBlockAction(action, selectionRange, firstCovered);
|
|
14512
|
+
setSelection(void 0);
|
|
14513
|
+
window.getSelection()?.removeAllRanges?.();
|
|
13946
14514
|
};
|
|
13947
14515
|
(0, react.useEffect)(() => {
|
|
13948
14516
|
if (jumpSignal === 0) return;
|
|
13949
|
-
jumpBlock(1);
|
|
14517
|
+
jumpBlock(1, true);
|
|
13950
14518
|
}, [jumpSignal]);
|
|
13951
14519
|
const onScroll = () => {
|
|
13952
14520
|
const body = bodyRef.current;
|
|
@@ -13955,16 +14523,39 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
13955
14523
|
setViewportHeight(body.clientHeight);
|
|
13956
14524
|
const count = model.blocks.length;
|
|
13957
14525
|
if (rowCount === 0 || count === 0) return;
|
|
13958
|
-
const anchor = body.scrollTop + leadRows * ROW_HEIGHT_PX;
|
|
13959
14526
|
let ref = -1;
|
|
13960
|
-
|
|
13961
|
-
const
|
|
13962
|
-
if (
|
|
14527
|
+
if (body.clientHeight > 0) {
|
|
14528
|
+
const viewportBottom = body.scrollTop + body.clientHeight;
|
|
14529
|
+
if (body.scrollTop <= NAV_ANCHOR_TOLERANCE_PX) ref = 0;
|
|
14530
|
+
else if (body.scrollHeight - viewportBottom <= NAV_ANCHOR_TOLERANCE_PX) ref = count - 1;
|
|
14531
|
+
}
|
|
14532
|
+
if (ref === -1) {
|
|
14533
|
+
const anchor = body.scrollTop + leadRows * ROW_HEIGHT_PX;
|
|
14534
|
+
for (let index = 0; index < count; index++) {
|
|
14535
|
+
const block = model.blocks[index];
|
|
14536
|
+
if (block !== void 0 && offsetOf(block.start) <= anchor + NAV_ANCHOR_TOLERANCE_PX) ref = index;
|
|
14537
|
+
}
|
|
13963
14538
|
}
|
|
13964
14539
|
setFocus(ref === -1 ? 0 : ref);
|
|
13965
14540
|
};
|
|
13966
14541
|
(0, react.useEffect)(() => {
|
|
13967
|
-
const update = () =>
|
|
14542
|
+
const update = () => {
|
|
14543
|
+
const live = window.getSelection();
|
|
14544
|
+
const range = splitView ? splitRowRangeOf(live) : rowRangeOf(live);
|
|
14545
|
+
let text = "";
|
|
14546
|
+
if (range !== void 0) {
|
|
14547
|
+
const raw = live?.toString() ?? "";
|
|
14548
|
+
text = raw !== "" && !raw.includes("\n") ? raw.trim() : "";
|
|
14549
|
+
}
|
|
14550
|
+
selectionTextRef.current = text;
|
|
14551
|
+
setSelection(range);
|
|
14552
|
+
const last = lastRecordedCursorRef.current;
|
|
14553
|
+
const sameRange = last !== void 0 && range !== void 0 && last.start === range.start && last.end === range.end;
|
|
14554
|
+
if (searchOpenRef.current && range !== void 0 && text !== "" && !sameRange) {
|
|
14555
|
+
lastRecordedCursorRef.current = range;
|
|
14556
|
+
cursorPosRef.current = range;
|
|
14557
|
+
}
|
|
14558
|
+
};
|
|
13968
14559
|
document.addEventListener("selectionchange", update);
|
|
13969
14560
|
update();
|
|
13970
14561
|
return () => {
|
|
@@ -13985,7 +14576,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
13985
14576
|
document.removeEventListener("copy", onCopy);
|
|
13986
14577
|
};
|
|
13987
14578
|
}, []);
|
|
13988
|
-
const
|
|
14579
|
+
const selectionReferenceLabel = (() => {
|
|
13989
14580
|
if (selection === void 0) return void 0;
|
|
13990
14581
|
if (splitView) {
|
|
13991
14582
|
if (selection.side === void 0 || splitPairs === null) return void 0;
|
|
@@ -13997,12 +14588,13 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
13997
14588
|
if (line !== void 0) lineNumbers.push(line);
|
|
13998
14589
|
}
|
|
13999
14590
|
if (lineNumbers.length === 0) return void 0;
|
|
14000
|
-
return
|
|
14591
|
+
return referenceLabelOf(file.path, workspacePath, Math.min(...lineNumbers), Math.max(...lineNumbers));
|
|
14001
14592
|
}
|
|
14002
14593
|
const lineNumbers = model.diff.rows.slice(selection.start, selection.end + 1).map((row) => row.newLine).filter((number) => number !== void 0);
|
|
14003
14594
|
if (lineNumbers.length === 0) return void 0;
|
|
14004
|
-
return
|
|
14595
|
+
return referenceLabelOf(file.path, workspacePath, Math.min(...lineNumbers), Math.max(...lineNumbers));
|
|
14005
14596
|
})();
|
|
14597
|
+
const selectionReference = selectionReferenceLabel === void 0 ? void 0 : `(${selectionReferenceLabel})`;
|
|
14006
14598
|
const copySelection = (0, react.useCallback)(async () => {
|
|
14007
14599
|
if (selectionReference === void 0) return;
|
|
14008
14600
|
if (pasteOnCopyEnabled()) {
|
|
@@ -14024,8 +14616,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
14024
14616
|
]);
|
|
14025
14617
|
(0, react.useEffect)(() => {
|
|
14026
14618
|
const onKeyDown = (event) => {
|
|
14027
|
-
if (!(event
|
|
14028
|
-
if (event.key.toLowerCase() !== "l") return;
|
|
14619
|
+
if (!matchesShortcut(event, keybindingOf("copyRef"))) return;
|
|
14029
14620
|
if (selectionReference === void 0) return;
|
|
14030
14621
|
event.preventDefault();
|
|
14031
14622
|
copySelection();
|
|
@@ -14037,33 +14628,55 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
14037
14628
|
}, [copySelection]);
|
|
14038
14629
|
(0, react.useEffect)(() => {
|
|
14039
14630
|
const onKeyDown = (event) => {
|
|
14040
|
-
if (!(event
|
|
14041
|
-
if (event.key.toLowerCase() !== "f") return;
|
|
14631
|
+
if (!matchesShortcut(event, keybindingOf("openSearch"))) return;
|
|
14042
14632
|
event.preventDefault();
|
|
14043
14633
|
if (splitView) {
|
|
14044
14634
|
splitDiffRef.current?.openSearch();
|
|
14045
14635
|
return;
|
|
14046
14636
|
}
|
|
14047
|
-
|
|
14048
|
-
searchInputRef.current?.focus();
|
|
14049
|
-
searchInputRef.current?.select();
|
|
14637
|
+
openSearchRef.current?.();
|
|
14050
14638
|
};
|
|
14051
14639
|
window.addEventListener("keydown", onKeyDown, true);
|
|
14052
14640
|
return () => {
|
|
14053
14641
|
window.removeEventListener("keydown", onKeyDown, true);
|
|
14054
14642
|
};
|
|
14055
|
-
}, []);
|
|
14643
|
+
}, [searchOpen, splitView]);
|
|
14644
|
+
(0, react.useEffect)(() => {
|
|
14645
|
+
const onKeyDown = (event) => {
|
|
14646
|
+
let direction = 0;
|
|
14647
|
+
if (matchesShortcut(event, keybindingOf("searchNext"))) direction = 1;
|
|
14648
|
+
else if (matchesShortcut(event, keybindingOf("searchPrev"))) direction = -1;
|
|
14649
|
+
if (direction === 0) return;
|
|
14650
|
+
if (splitView) {
|
|
14651
|
+
if (splitDiffRef.current?.searchNext(direction)) event.preventDefault();
|
|
14652
|
+
return;
|
|
14653
|
+
}
|
|
14654
|
+
if (searchOpen && searchMatches.length > 0) {
|
|
14655
|
+
event.preventDefault();
|
|
14656
|
+
goSearch(direction);
|
|
14657
|
+
}
|
|
14658
|
+
};
|
|
14659
|
+
window.addEventListener("keydown", onKeyDown, true);
|
|
14660
|
+
return () => {
|
|
14661
|
+
window.removeEventListener("keydown", onKeyDown, true);
|
|
14662
|
+
};
|
|
14663
|
+
}, [
|
|
14664
|
+
searchOpen,
|
|
14665
|
+
searchMatches,
|
|
14666
|
+
splitView
|
|
14667
|
+
]);
|
|
14056
14668
|
const jumpRef = (0, react.useRef)(jumpBlock);
|
|
14057
14669
|
jumpRef.current = jumpBlock;
|
|
14058
14670
|
(0, react.useEffect)(() => {
|
|
14059
14671
|
const onKeyDown = (event) => {
|
|
14060
|
-
|
|
14061
|
-
|
|
14062
|
-
if (
|
|
14672
|
+
let direction = 0;
|
|
14673
|
+
if (matchesShortcut(event, keybindingOf("jumpUp"))) direction = -1;
|
|
14674
|
+
else if (matchesShortcut(event, keybindingOf("jumpDown"))) direction = 1;
|
|
14675
|
+
if (direction === 0) return;
|
|
14063
14676
|
const target = event.target;
|
|
14064
14677
|
if (target instanceof Element && target.closest("input, textarea, [contenteditable=\"true\"]") !== null) return;
|
|
14065
14678
|
event.preventDefault();
|
|
14066
|
-
jumpRef.current(
|
|
14679
|
+
jumpRef.current(direction, true);
|
|
14067
14680
|
};
|
|
14068
14681
|
window.addEventListener("keydown", onKeyDown, true);
|
|
14069
14682
|
return () => {
|
|
@@ -14158,7 +14771,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
14158
14771
|
"aria-label": t("action.prevDiff"),
|
|
14159
14772
|
disabled: busy,
|
|
14160
14773
|
onClick: () => {
|
|
14161
|
-
jumpBlock(-1);
|
|
14774
|
+
jumpBlock(-1, true);
|
|
14162
14775
|
},
|
|
14163
14776
|
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronUpOutline14, { size: 14 })
|
|
14164
14777
|
})
|
|
@@ -14173,7 +14786,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
14173
14786
|
"aria-label": t("action.nextDiff"),
|
|
14174
14787
|
disabled: busy,
|
|
14175
14788
|
onClick: () => {
|
|
14176
|
-
jumpBlock(1);
|
|
14789
|
+
jumpBlock(1, true);
|
|
14177
14790
|
},
|
|
14178
14791
|
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, { size: 14 })
|
|
14179
14792
|
})
|
|
@@ -14249,7 +14862,8 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
14249
14862
|
selection,
|
|
14250
14863
|
leadRows,
|
|
14251
14864
|
onBlockKeep,
|
|
14252
|
-
onBlockRevert
|
|
14865
|
+
onBlockRevert,
|
|
14866
|
+
onWrapToast: (text) => onToast(text)
|
|
14253
14867
|
}) : (0, react_jsx_runtime.jsxs)("div", {
|
|
14254
14868
|
className: PendingPanel_module_css_default.diffBodyWrap,
|
|
14255
14869
|
onMouseLeave: () => {
|
|
@@ -14284,6 +14898,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
14284
14898
|
focused: inFocusedBlock(index),
|
|
14285
14899
|
searchHit: searchHitSet.has(index),
|
|
14286
14900
|
searchCurrent: index === currentSearchRow,
|
|
14901
|
+
searchQuery,
|
|
14287
14902
|
onRowHover,
|
|
14288
14903
|
wrappedLines: rowWrapped?.[index]
|
|
14289
14904
|
}, index);
|
|
@@ -14377,7 +14992,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
14377
14992
|
]
|
|
14378
14993
|
}) : null,
|
|
14379
14994
|
focusedBlock !== void 0 && flashKey > 0 && (0, react_jsx_runtime.jsx)("div", {
|
|
14380
|
-
className: PendingPanel_module_css_default.blockFlash,
|
|
14995
|
+
className: pinShakeRef.current ? `${PendingPanel_module_css_default.blockFlash} ${PendingPanel_module_css_default.blockFlashShake}` : PendingPanel_module_css_default.blockFlash,
|
|
14381
14996
|
"data-diff-block-flash": true,
|
|
14382
14997
|
style: {
|
|
14383
14998
|
top: flashTop,
|
|
@@ -14396,7 +15011,9 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
14396
15011
|
value: searchQuery,
|
|
14397
15012
|
placeholder: t("panel.searchPlaceholder"),
|
|
14398
15013
|
onChange: (event) => {
|
|
14399
|
-
|
|
15014
|
+
const value = event.target.value;
|
|
15015
|
+
setSearchQuery(value);
|
|
15016
|
+
setSearchIndex(startIndexFor(value));
|
|
14400
15017
|
},
|
|
14401
15018
|
onKeyDown: (event) => {
|
|
14402
15019
|
if (event.key === "Enter") {
|
|
@@ -14410,27 +15027,37 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
14410
15027
|
"data-diff-search-count": true,
|
|
14411
15028
|
children: searchMatches.length === 0 ? "0/0" : `${searchIndex % searchMatches.length + 1}/${searchMatches.length}`
|
|
14412
15029
|
}),
|
|
14413
|
-
(0, react_jsx_runtime.jsx)(
|
|
14414
|
-
|
|
14415
|
-
|
|
14416
|
-
|
|
14417
|
-
|
|
14418
|
-
|
|
14419
|
-
|
|
14420
|
-
|
|
14421
|
-
|
|
14422
|
-
|
|
15030
|
+
(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
15031
|
+
label: `${t("action.prevDiff")} (Shift+F3)`,
|
|
15032
|
+
side: "bottom",
|
|
15033
|
+
delayMs: 500,
|
|
15034
|
+
children: (0, react_jsx_runtime.jsx)("button", {
|
|
15035
|
+
type: "button",
|
|
15036
|
+
className: `${PendingPanel_module_css_default.action} ${PendingPanel_module_css_default.iconAction}`,
|
|
15037
|
+
"data-diff-search-prev": true,
|
|
15038
|
+
"aria-label": t("action.prevDiff"),
|
|
15039
|
+
disabled: searchMatches.length === 0,
|
|
15040
|
+
onClick: () => {
|
|
15041
|
+
goSearch(-1);
|
|
15042
|
+
},
|
|
15043
|
+
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronUpOutline14, { size: 14 })
|
|
15044
|
+
})
|
|
14423
15045
|
}),
|
|
14424
|
-
(0, react_jsx_runtime.jsx)(
|
|
14425
|
-
|
|
14426
|
-
|
|
14427
|
-
|
|
14428
|
-
|
|
14429
|
-
|
|
14430
|
-
|
|
14431
|
-
|
|
14432
|
-
|
|
14433
|
-
|
|
15046
|
+
(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
15047
|
+
label: `${t("action.nextDiff")} (F3)`,
|
|
15048
|
+
side: "bottom",
|
|
15049
|
+
delayMs: 500,
|
|
15050
|
+
children: (0, react_jsx_runtime.jsx)("button", {
|
|
15051
|
+
type: "button",
|
|
15052
|
+
className: `${PendingPanel_module_css_default.action} ${PendingPanel_module_css_default.iconAction}`,
|
|
15053
|
+
"data-diff-search-next": true,
|
|
15054
|
+
"aria-label": t("action.nextDiff"),
|
|
15055
|
+
disabled: searchMatches.length === 0,
|
|
15056
|
+
onClick: () => {
|
|
15057
|
+
goSearch(1);
|
|
15058
|
+
},
|
|
15059
|
+
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, { size: 14 })
|
|
15060
|
+
})
|
|
14434
15061
|
}),
|
|
14435
15062
|
(0, react_jsx_runtime.jsx)("button", {
|
|
14436
15063
|
type: "button",
|
|
@@ -14466,17 +15093,25 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
14466
15093
|
label: copied ? t("action.copied") : `${t("action.copyHint")} (Ctrl+L)`,
|
|
14467
15094
|
side: "top",
|
|
14468
15095
|
delayMs: 300,
|
|
14469
|
-
children: (0, react_jsx_runtime.jsx)("
|
|
14470
|
-
|
|
15096
|
+
children: (0, react_jsx_runtime.jsx)("span", {
|
|
15097
|
+
role: "button",
|
|
15098
|
+
tabIndex: 0,
|
|
14471
15099
|
className: PendingPanel_module_css_default.statusAction,
|
|
14472
15100
|
"data-diff-copy": true,
|
|
15101
|
+
"data-mobile-nav-copy": "1",
|
|
14473
15102
|
onMouseDown: (event) => {
|
|
14474
15103
|
event.preventDefault();
|
|
14475
15104
|
},
|
|
14476
15105
|
onClick: () => {
|
|
14477
15106
|
copySelection();
|
|
14478
15107
|
},
|
|
14479
|
-
|
|
15108
|
+
onKeyDown: (event) => {
|
|
15109
|
+
if (event.key === "Enter" || event.key === " ") {
|
|
15110
|
+
event.preventDefault();
|
|
15111
|
+
copySelection();
|
|
15112
|
+
}
|
|
15113
|
+
},
|
|
15114
|
+
children: copied ? t("action.copied") : selectionReferenceLabel
|
|
14480
15115
|
})
|
|
14481
15116
|
}),
|
|
14482
15117
|
(0, react_jsx_runtime.jsx)("span", { className: PendingPanel_module_css_default.flexSpacer }),
|
|
@@ -14566,6 +15201,33 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
14566
15201
|
const [actionToast, setActionToast] = (0, react.useState)(null);
|
|
14567
15202
|
/** A transient banner confirming a reference was copied to the clipboard. */
|
|
14568
15203
|
const [copyToast, setCopyToast] = (0, react.useState)(null);
|
|
15204
|
+
const showCopyToast = (text) => {
|
|
15205
|
+
setCopyToast((prev) => ({
|
|
15206
|
+
text,
|
|
15207
|
+
n: (prev?.n ?? 0) + 1
|
|
15208
|
+
}));
|
|
15209
|
+
};
|
|
15210
|
+
const handleOpenFileRef = (0, react.useRef)();
|
|
15211
|
+
handleOpenFileRef.current = (path) => {
|
|
15212
|
+
const entry = snapshot.files.find((file) => file.path === path);
|
|
15213
|
+
if (entry === void 0) {
|
|
15214
|
+
showCopyToast(t("panel.fileNotPending"));
|
|
15215
|
+
return;
|
|
15216
|
+
}
|
|
15217
|
+
setOpen(true);
|
|
15218
|
+
setSelected(entry.id);
|
|
15219
|
+
};
|
|
15220
|
+
(0, react.useEffect)(() => {
|
|
15221
|
+
const onOpenFile = (event) => {
|
|
15222
|
+
const path = event.detail?.path;
|
|
15223
|
+
if (typeof path !== "string") return;
|
|
15224
|
+
handleOpenFileRef.current?.(path);
|
|
15225
|
+
};
|
|
15226
|
+
window.addEventListener(OPEN_FILE_EVENT, onOpenFile);
|
|
15227
|
+
return () => {
|
|
15228
|
+
window.removeEventListener(OPEN_FILE_EVENT, onOpenFile);
|
|
15229
|
+
};
|
|
15230
|
+
}, []);
|
|
14569
15231
|
/** Whether the redo-cleared notice is showing (bottom-right, OK to dismiss). */
|
|
14570
15232
|
const [redoClearedNotice, setRedoClearedNotice] = (0, react.useState)(false);
|
|
14571
15233
|
/** A file whose last block just resolved, pending a remove-or-keep choice. */
|
|
@@ -14727,7 +15389,12 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
14727
15389
|
(0, react.useEffect)(() => {
|
|
14728
15390
|
if (snapshot.justResolved === void 0) return;
|
|
14729
15391
|
setConfirmDismiss(snapshot.justResolved);
|
|
14730
|
-
|
|
15392
|
+
const timer = window.setTimeout(() => {
|
|
15393
|
+
onAckJustResolved();
|
|
15394
|
+
}, 0);
|
|
15395
|
+
return () => {
|
|
15396
|
+
window.clearTimeout(timer);
|
|
15397
|
+
};
|
|
14731
15398
|
}, [snapshot.justResolved, onAckJustResolved]);
|
|
14732
15399
|
(0, react.useEffect)(() => {
|
|
14733
15400
|
if (!open) return;
|
|
@@ -14835,15 +15502,17 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
14835
15502
|
if (!open || current === void 0) return;
|
|
14836
15503
|
const onKeyDown = (event) => {
|
|
14837
15504
|
if (!(event.ctrlKey || event.metaKey) || event.altKey) return;
|
|
14838
|
-
const key = event.key.toLowerCase();
|
|
14839
|
-
if (key !== "z" && key !== "y") return;
|
|
14840
15505
|
const target = event.target;
|
|
14841
15506
|
if (target instanceof Element && target.closest("input, textarea, [contenteditable=\"true\"]") !== null) return;
|
|
14842
|
-
event
|
|
14843
|
-
|
|
14844
|
-
|
|
14845
|
-
|
|
14846
|
-
}
|
|
15507
|
+
if (matchesShortcut(event, keybindingOf("undo"))) {
|
|
15508
|
+
event.preventDefault();
|
|
15509
|
+
handleUndo(current);
|
|
15510
|
+
return;
|
|
15511
|
+
}
|
|
15512
|
+
if (matchesShortcut(event, keybindingOf("redo")) || matchesShortcut(event, "Ctrl+Y")) {
|
|
15513
|
+
event.preventDefault();
|
|
15514
|
+
handleRedo(current);
|
|
15515
|
+
}
|
|
14847
15516
|
};
|
|
14848
15517
|
window.addEventListener("keydown", onKeyDown, true);
|
|
14849
15518
|
return () => {
|
|
@@ -14858,14 +15527,15 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
14858
15527
|
(0, react.useEffect)(() => {
|
|
14859
15528
|
if (!open || current === void 0) return;
|
|
14860
15529
|
const onKeyDown = (event) => {
|
|
14861
|
-
|
|
14862
|
-
if (event
|
|
15530
|
+
let direction = 0;
|
|
15531
|
+
if (matchesShortcut(event, keybindingOf("cycleNext"))) direction = 1;
|
|
15532
|
+
else if (matchesShortcut(event, keybindingOf("cyclePrev"))) direction = -1;
|
|
15533
|
+
if (direction === 0) return;
|
|
14863
15534
|
const target = event.target;
|
|
14864
15535
|
if (target instanceof Element && target.closest("input, textarea, [contenteditable=\"true\"]") !== null) return;
|
|
14865
15536
|
if (files.length === 0) return;
|
|
14866
15537
|
event.preventDefault();
|
|
14867
15538
|
const index = files.findIndex((file) => file.id === selected);
|
|
14868
|
-
const direction = event.shiftKey ? -1 : 1;
|
|
14869
15539
|
const next = files[(index + direction + files.length) % files.length];
|
|
14870
15540
|
if (next !== void 0) setSelected(next.id);
|
|
14871
15541
|
};
|
|
@@ -14949,11 +15619,11 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
14949
15619
|
}
|
|
14950
15620
|
}),
|
|
14951
15621
|
copyToast !== null && (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Toast, {
|
|
14952
|
-
text: copyToast,
|
|
15622
|
+
text: copyToast.text,
|
|
14953
15623
|
onDone: () => {
|
|
14954
15624
|
setCopyToast(null);
|
|
14955
15625
|
}
|
|
14956
|
-
}),
|
|
15626
|
+
}, copyToast.n),
|
|
14957
15627
|
open && (0, react_dom.createPortal)((0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [expanded && (0, react_jsx_runtime.jsx)("div", {
|
|
14958
15628
|
className: PendingPanel_module_css_default.fullscreenBackdrop,
|
|
14959
15629
|
"data-diff-fullscreen-backdrop": true
|
|
@@ -15087,9 +15757,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
15087
15757
|
undoFlash,
|
|
15088
15758
|
failedMessage: failed.get(selectedFile.id),
|
|
15089
15759
|
onPasteReference,
|
|
15090
|
-
onToast:
|
|
15091
|
-
setCopyToast(text);
|
|
15092
|
-
},
|
|
15760
|
+
onToast: showCopyToast,
|
|
15093
15761
|
t,
|
|
15094
15762
|
onKeep,
|
|
15095
15763
|
onRevert,
|
|
@@ -15317,8 +15985,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
15317
15985
|
disabled: value <= min,
|
|
15318
15986
|
onClick: () => {
|
|
15319
15987
|
onChange(Math.max(min, value - 1));
|
|
15320
|
-
}
|
|
15321
|
-
children: "−"
|
|
15988
|
+
}
|
|
15322
15989
|
}),
|
|
15323
15990
|
(0, react_jsx_runtime.jsx)("span", {
|
|
15324
15991
|
className: PendingPanel_module_css_default.stepperValue,
|
|
@@ -15327,14 +15994,13 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
15327
15994
|
}),
|
|
15328
15995
|
(0, react_jsx_runtime.jsx)("button", {
|
|
15329
15996
|
type: "button",
|
|
15330
|
-
className: PendingPanel_module_css_default.stepperButton
|
|
15997
|
+
className: `${PendingPanel_module_css_default.stepperButton} ${PendingPanel_module_css_default.stepperButtonUp}`,
|
|
15331
15998
|
"data-diff-stepper-up": true,
|
|
15332
15999
|
"aria-label": t("action.increase"),
|
|
15333
16000
|
disabled: value >= max,
|
|
15334
16001
|
onClick: () => {
|
|
15335
16002
|
onChange(Math.min(max, value + 1));
|
|
15336
|
-
}
|
|
15337
|
-
children: "+"
|
|
16003
|
+
}
|
|
15338
16004
|
})
|
|
15339
16005
|
]
|
|
15340
16006
|
})]
|
|
@@ -15418,6 +16084,19 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
15418
16084
|
const [split, setSplitState] = (0, react.useState)(splitMode);
|
|
15419
16085
|
const [lead, setLeadState] = (0, react.useState)(navLeadRows);
|
|
15420
16086
|
const [summon, setSummonState] = (0, react.useState)(quickSummonKey);
|
|
16087
|
+
const [keysOpen, setKeysOpen] = (0, react.useState)(false);
|
|
16088
|
+
const [keybindings, setKeybindingsState] = (0, react.useState)(() => {
|
|
16089
|
+
const initial = {};
|
|
16090
|
+
for (const action of Object.keys(DEFAULT_KEYBINDINGS)) initial[action] = keybindingOf(action);
|
|
16091
|
+
return initial;
|
|
16092
|
+
});
|
|
16093
|
+
const setKB = (action, chord) => {
|
|
16094
|
+
setKeybindingsState((prev) => ({
|
|
16095
|
+
...prev,
|
|
16096
|
+
[action]: chord
|
|
16097
|
+
}));
|
|
16098
|
+
setKeybinding(action, chord);
|
|
16099
|
+
};
|
|
15421
16100
|
const setSummon = (value) => {
|
|
15422
16101
|
setSummonState(value);
|
|
15423
16102
|
setQuickSummonKey(value);
|
|
@@ -15446,6 +16125,41 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
15446
16125
|
className: PendingPanel_module_css_default.settingsPage,
|
|
15447
16126
|
"data-diff-settings": true,
|
|
15448
16127
|
children: [
|
|
16128
|
+
(0, react_jsx_runtime.jsxs)("div", {
|
|
16129
|
+
className: PendingPanel_module_css_default.settingsGroup,
|
|
16130
|
+
"data-open": keysOpen || void 0,
|
|
16131
|
+
children: [(0, react_jsx_runtime.jsxs)("button", {
|
|
16132
|
+
type: "button",
|
|
16133
|
+
className: PendingPanel_module_css_default.settingsGroupHeader,
|
|
16134
|
+
onClick: () => {
|
|
16135
|
+
setKeysOpen((open) => !open);
|
|
16136
|
+
},
|
|
16137
|
+
"data-diff-keybindings-toggle": true,
|
|
16138
|
+
children: [(0, react_jsx_runtime.jsx)("span", {
|
|
16139
|
+
className: PendingPanel_module_css_default.settingsGroupTitle,
|
|
16140
|
+
children: t("settings.keybindings")
|
|
16141
|
+
}), (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, { className: PendingPanel_module_css_default.settingsGroupChevron })]
|
|
16142
|
+
}), keysOpen && (0, react_jsx_runtime.jsxs)("div", {
|
|
16143
|
+
className: PendingPanel_module_css_default.settingsGroupBody,
|
|
16144
|
+
children: [(0, react_jsx_runtime.jsx)(ShortcutRow, {
|
|
16145
|
+
title: t("panel.quickSummon"),
|
|
16146
|
+
description: t("panel.quickSummonDesc"),
|
|
16147
|
+
value: summon,
|
|
16148
|
+
onChange: setSummon,
|
|
16149
|
+
dataAttribute: "data-diff-quick-summon-key",
|
|
16150
|
+
placeholder: t("panel.recordShortcut")
|
|
16151
|
+
}), Object.keys(DEFAULT_KEYBINDINGS).map((action) => (0, react_jsx_runtime.jsx)(ShortcutRow, {
|
|
16152
|
+
title: t(`panel.key.${action}`),
|
|
16153
|
+
description: t("panel.keyDesc"),
|
|
16154
|
+
value: keybindings[action] ?? DEFAULT_KEYBINDINGS[action] ?? "",
|
|
16155
|
+
onChange: (chord) => {
|
|
16156
|
+
setKB(action, chord);
|
|
16157
|
+
},
|
|
16158
|
+
dataAttribute: `data-diff-key-${action}`,
|
|
16159
|
+
placeholder: t("panel.recordShortcut")
|
|
16160
|
+
}, action))]
|
|
16161
|
+
})]
|
|
16162
|
+
}),
|
|
15449
16163
|
(0, react_jsx_runtime.jsx)(PreferenceRow, {
|
|
15450
16164
|
title: t("panel.pasteOnCopy"),
|
|
15451
16165
|
description: t("panel.pasteOnCopyDesc"),
|
|
@@ -15488,14 +16202,6 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
15488
16202
|
max: 10,
|
|
15489
16203
|
dataAttribute: "data-diff-nav-lead-rows",
|
|
15490
16204
|
t
|
|
15491
|
-
}),
|
|
15492
|
-
(0, react_jsx_runtime.jsx)(ShortcutRow, {
|
|
15493
|
-
title: t("panel.quickSummon"),
|
|
15494
|
-
description: t("panel.quickSummonDesc"),
|
|
15495
|
-
value: summon,
|
|
15496
|
-
onChange: setSummon,
|
|
15497
|
-
dataAttribute: "data-diff-quick-summon-key",
|
|
15498
|
-
placeholder: t("panel.recordShortcut")
|
|
15499
16205
|
})
|
|
15500
16206
|
]
|
|
15501
16207
|
});
|
|
@@ -16002,6 +16708,13 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
16002
16708
|
if (scoped === void 0) return;
|
|
16003
16709
|
scoped.conversation.input.for(scoped.actx).setDraft(text);
|
|
16004
16710
|
},
|
|
16711
|
+
appendDraft: (suffix) => {
|
|
16712
|
+
const scoped = resolveConversation(ctx, sessionId());
|
|
16713
|
+
if (scoped === void 0) return;
|
|
16714
|
+
const input = scoped.conversation.input.for(scoped.actx);
|
|
16715
|
+
const current = input.state.getSnapshot().draft ?? "";
|
|
16716
|
+
input.setDraft(current === "" ? suffix : `${current} ${suffix}`);
|
|
16717
|
+
},
|
|
16005
16718
|
readQueue: () => {
|
|
16006
16719
|
const scoped = resolveConversation(ctx, sessionId());
|
|
16007
16720
|
if (scoped === void 0) return [];
|
|
@@ -16032,6 +16745,11 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
16032
16745
|
"panel.aria": "待处理改动",
|
|
16033
16746
|
"panel.stats": "+{added} -{removed}",
|
|
16034
16747
|
"panel.blockPosition": "第 {current}/{total} 块",
|
|
16748
|
+
"panel.blockAtEnd": "已在最后一个差异块,再按一次即可跳到第一个",
|
|
16749
|
+
"panel.blockAtStart": "已在第一个差异块,再按一次即可跳到最后一个",
|
|
16750
|
+
"panel.blockSingle": "仅有一个差异块",
|
|
16751
|
+
"panel.viewDiff": "查看差异",
|
|
16752
|
+
"panel.fileNotPending": "该文件不在待处理差异列表中",
|
|
16035
16753
|
"panel.selectHint": "点击每项查看整个文件的差异;选中文本后,用底部状态栏或 Ctrl+L 复制引用",
|
|
16036
16754
|
"panel.searchPlaceholder": "搜索",
|
|
16037
16755
|
"panel.missing": "文件已不存在",
|
|
@@ -16051,6 +16769,18 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
16051
16769
|
"panel.quickSummon": "快速呼出",
|
|
16052
16770
|
"panel.quickSummonDesc": "用键盘快捷键打开/关闭差异面板。点击右侧按钮后按下新的按键组合即可修改。",
|
|
16053
16771
|
"panel.recordShortcut": "按下快捷键…",
|
|
16772
|
+
"settings.keybindings": "按键设置",
|
|
16773
|
+
"panel.keyDesc": "点击右侧按钮后按下新的按键组合即可修改。",
|
|
16774
|
+
"panel.key.jumpUp": "跳转到上一个差异块",
|
|
16775
|
+
"panel.key.jumpDown": "跳转到下一个差异块",
|
|
16776
|
+
"panel.key.copyRef": "复制引用",
|
|
16777
|
+
"panel.key.openSearch": "打开差异搜索",
|
|
16778
|
+
"panel.key.searchNext": "下一个搜索结果",
|
|
16779
|
+
"panel.key.searchPrev": "上一个搜索结果",
|
|
16780
|
+
"panel.key.undo": "撤销",
|
|
16781
|
+
"panel.key.redo": "重做",
|
|
16782
|
+
"panel.key.cycleNext": "下一个待处理文件",
|
|
16783
|
+
"panel.key.cyclePrev": "上一个待处理文件",
|
|
16054
16784
|
"settings.tabLabel": "改动审批",
|
|
16055
16785
|
"row.create": "新增文件",
|
|
16056
16786
|
"row.failed": "失败",
|
|
@@ -16109,6 +16839,11 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
16109
16839
|
"panel.aria": "Pending changes",
|
|
16110
16840
|
"panel.stats": "+{added} -{removed}",
|
|
16111
16841
|
"panel.blockPosition": "{current}/{total}",
|
|
16842
|
+
"panel.blockAtEnd": "At the last diff block, press again to jump to the first",
|
|
16843
|
+
"panel.blockAtStart": "At the first diff block, press again to jump to the last",
|
|
16844
|
+
"panel.blockSingle": "Only one diff block",
|
|
16845
|
+
"panel.viewDiff": "View diff",
|
|
16846
|
+
"panel.fileNotPending": "This file is not in the pending diff list",
|
|
16112
16847
|
"panel.selectHint": "Select an item to review its whole-file diff; select text and copy its reference from the status bar (Ctrl+L)",
|
|
16113
16848
|
"panel.searchPlaceholder": "Search",
|
|
16114
16849
|
"panel.missing": "File is gone",
|
|
@@ -16128,6 +16863,18 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
16128
16863
|
"panel.quickSummon": "Quick summon",
|
|
16129
16864
|
"panel.quickSummonDesc": "Open or close the diff panel with a keyboard shortcut. Click the button and press a new chord to change it.",
|
|
16130
16865
|
"panel.recordShortcut": "Press keys…",
|
|
16866
|
+
"settings.keybindings": "Keybindings",
|
|
16867
|
+
"panel.keyDesc": "Click the button, then press a new chord to rebind it.",
|
|
16868
|
+
"panel.key.jumpUp": "Jump to the previous diff block",
|
|
16869
|
+
"panel.key.jumpDown": "Jump to the next diff block",
|
|
16870
|
+
"panel.key.copyRef": "Copy reference",
|
|
16871
|
+
"panel.key.openSearch": "Open diff search",
|
|
16872
|
+
"panel.key.searchNext": "Next search result",
|
|
16873
|
+
"panel.key.searchPrev": "Previous search result",
|
|
16874
|
+
"panel.key.undo": "Undo",
|
|
16875
|
+
"panel.key.redo": "Redo",
|
|
16876
|
+
"panel.key.cycleNext": "Next pending file",
|
|
16877
|
+
"panel.key.cyclePrev": "Previous pending file",
|
|
16131
16878
|
"settings.tabLabel": "Diff Approval",
|
|
16132
16879
|
"row.create": "New file",
|
|
16133
16880
|
"row.failed": "Failed",
|
|
@@ -16307,18 +17054,13 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
16307
17054
|
onAckRedoCleared: () => store.clearRedoCleared(),
|
|
16308
17055
|
onAckJustResolved: () => store.clearJustResolved(),
|
|
16309
17056
|
onPasteReference: (sessionId, reference) => {
|
|
16310
|
-
|
|
16311
|
-
|
|
16312
|
-
const conversation = actx.get("conversation");
|
|
16313
|
-
if (conversation?.input === void 0) return;
|
|
16314
|
-
const textarea = document.querySelector("[data-composer-card] textarea");
|
|
16315
|
-
const base = textarea?.value ?? "";
|
|
16316
|
-
conversation.input.for(actx).setDraft(base === "" ? reference : `${base} ${reference}`);
|
|
16317
|
-
textarea?.focus();
|
|
17057
|
+
conversationAccess(ctx, () => sessionId).appendDraft(reference);
|
|
17058
|
+
document.querySelector("[data-composer-input]")?.focus();
|
|
16318
17059
|
},
|
|
16319
17060
|
collapseSidebar
|
|
16320
17061
|
})
|
|
16321
17062
|
}, PendingPanel));
|
|
17063
|
+
if (typeof window !== "undefined") ctx.effect(() => startProducedDiffInjection(t("panel.viewDiff"), (path) => window.dispatchEvent(new CustomEvent(OPEN_FILE_EVENT, { detail: { path } }))), "diff-approval: produced-files diff buttons");
|
|
16322
17064
|
ctx.slots.inject("settings.section", () => ctx.slots.register({
|
|
16323
17065
|
name: "settings.section",
|
|
16324
17066
|
id: "diff-approval",
|