dsh-diff-approval 0.15.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 +1131 -311
- package/lib/index.js +7 -19
- 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 +38 -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/lib/types/vcs.d.ts +2 -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_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,123 +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
|
-
"
|
|
12230
|
-
"badge": "F1KBNa_badge",
|
|
12231
|
-
"settingsRowTitle": "F1KBNa_settingsRowTitle",
|
|
12232
|
-
"code": "F1KBNa_code",
|
|
12233
|
-
"confirmCard": "F1KBNa_confirmCard",
|
|
12234
|
-
"title": "F1KBNa_title",
|
|
12235
|
-
"diffBody": "F1KBNa_diffBody",
|
|
12236
|
-
"diffFlash": "F1KBNa_diffFlash",
|
|
12237
|
-
"states": "F1KBNa_states",
|
|
12238
|
-
"actionQuietDisabled": "F1KBNa_actionQuietDisabled",
|
|
12239
|
-
"resizeHandle": "F1KBNa_resizeHandle",
|
|
12240
|
-
"addCount": "F1KBNa_addCount",
|
|
12241
|
-
"actionPrimary": "F1KBNa_actionPrimary",
|
|
12242
|
-
"splitHScrollFill": "F1KBNa_splitHScrollFill",
|
|
12243
|
-
"importButton": "F1KBNa_importButton",
|
|
12244
|
-
"splitDivider": "F1KBNa_splitDivider",
|
|
12563
|
+
"stepperButton": "F1KBNa_stepperButton",
|
|
12245
12564
|
"divergedHint": "F1KBNa_divergedHint",
|
|
12246
|
-
"
|
|
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",
|
|
12572
|
+
"rail": "F1KBNa_rail",
|
|
12573
|
+
"title": "F1KBNa_title",
|
|
12247
12574
|
"importNote": "F1KBNa_importNote",
|
|
12248
|
-
"
|
|
12249
|
-
"
|
|
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",
|
|
12582
|
+
"noteCentered": "F1KBNa_noteCentered",
|
|
12583
|
+
"overviewRuler": "F1KBNa_overviewRuler",
|
|
12584
|
+
"actionQuietDisabled": "F1KBNa_actionQuietDisabled",
|
|
12585
|
+
"states": "F1KBNa_states",
|
|
12250
12586
|
"vSpacer": "F1KBNa_vSpacer",
|
|
12251
|
-
"
|
|
12252
|
-
"
|
|
12587
|
+
"fullscreenBackdrop": "F1KBNa_fullscreenBackdrop",
|
|
12588
|
+
"resizeHandle": "F1KBNa_resizeHandle",
|
|
12589
|
+
"diffBodySplit": "F1KBNa_diffBodySplit",
|
|
12253
12590
|
"splitCol": "F1KBNa_splitCol",
|
|
12254
|
-
"
|
|
12255
|
-
"
|
|
12256
|
-
"
|
|
12257
|
-
"
|
|
12258
|
-
"
|
|
12259
|
-
"bulkActions": "F1KBNa_bulkActions",
|
|
12260
|
-
"searchInput": "F1KBNa_searchInput",
|
|
12261
|
-
"splitLdel": "F1KBNa_splitLdel",
|
|
12591
|
+
"badge": "F1KBNa_badge",
|
|
12592
|
+
"settingsGroupHeader": "F1KBNa_settingsGroupHeader",
|
|
12593
|
+
"code": "F1KBNa_code",
|
|
12594
|
+
"settingsGroupTitle": "F1KBNa_settingsGroupTitle",
|
|
12595
|
+
"toggleOn": "F1KBNa_toggleOn",
|
|
12262
12596
|
"diffPath": "F1KBNa_diffPath",
|
|
12263
|
-
"
|
|
12264
|
-
"
|
|
12265
|
-
"
|
|
12266
|
-
"
|
|
12267
|
-
"rows": "F1KBNa_rows",
|
|
12268
|
-
"wrapActive": "F1KBNa_wrapActive",
|
|
12597
|
+
"confirmText": "F1KBNa_confirmText",
|
|
12598
|
+
"searchCount": "F1KBNa_searchCount",
|
|
12599
|
+
"blockFlashShake": "F1KBNa_blockFlashShake",
|
|
12600
|
+
"settingsSelectorChevron": "F1KBNa_settingsSelectorChevron",
|
|
12269
12601
|
"hint": "F1KBNa_hint",
|
|
12270
|
-
"
|
|
12602
|
+
"settingsGroupBody": "F1KBNa_settingsGroupBody",
|
|
12603
|
+
"group": "F1KBNa_group",
|
|
12271
12604
|
"wrap": "F1KBNa_wrap",
|
|
12272
|
-
"fullscreenBackdrop": "F1KBNa_fullscreenBackdrop",
|
|
12273
|
-
"readError": "F1KBNa_readError",
|
|
12274
|
-
"add": "F1KBNa_add",
|
|
12275
|
-
"overviewRuler": "F1KBNa_overviewRuler",
|
|
12276
12605
|
"splitHScrollRow": "F1KBNa_splitHScrollRow",
|
|
12277
|
-
"
|
|
12278
|
-
"
|
|
12279
|
-
"
|
|
12280
|
-
"
|
|
12281
|
-
"toggleOn": "F1KBNa_toggleOn",
|
|
12282
|
-
"badgeCount": "F1KBNa_badgeCount",
|
|
12606
|
+
"header": "F1KBNa_header",
|
|
12607
|
+
"iconAction": "F1KBNa_iconAction",
|
|
12608
|
+
"gutter": "F1KBNa_gutter",
|
|
12609
|
+
"diffActions": "F1KBNa_diffActions",
|
|
12283
12610
|
"settingsPage": "F1KBNa_settingsPage",
|
|
12284
|
-
"
|
|
12611
|
+
"badgeCount": "F1KBNa_badgeCount",
|
|
12285
12612
|
"footerButtons": "F1KBNa_footerButtons",
|
|
12286
|
-
"
|
|
12287
|
-
"
|
|
12288
|
-
"
|
|
12289
|
-
"
|
|
12290
|
-
"
|
|
12291
|
-
"
|
|
12292
|
-
"lines": "F1KBNa_lines",
|
|
12293
|
-
"stepperValue": "F1KBNa_stepperValue",
|
|
12294
|
-
"splitRoot": "F1KBNa_splitRoot",
|
|
12295
|
-
"rowHead": "F1KBNa_rowHead",
|
|
12296
|
-
"stepperButton": "F1KBNa_stepperButton",
|
|
12297
|
-
"rowMeta": "F1KBNa_rowMeta",
|
|
12613
|
+
"noticeText": "F1KBNa_noticeText",
|
|
12614
|
+
"detailEmpty": "F1KBNa_detailEmpty",
|
|
12615
|
+
"divider": "F1KBNa_divider",
|
|
12616
|
+
"searchMatchCurrent": "F1KBNa_searchMatchCurrent",
|
|
12617
|
+
"splitLadd": "F1KBNa_splitLadd",
|
|
12618
|
+
"actionError": "F1KBNa_actionError",
|
|
12298
12619
|
"expand": "F1KBNa_expand",
|
|
12299
|
-
"settingsRow": "F1KBNa_settingsRow",
|
|
12300
|
-
"diff": "F1KBNa_diff",
|
|
12301
|
-
"panel": "F1KBNa_panel",
|
|
12302
|
-
"diffBodySplit": "F1KBNa_diffBodySplit",
|
|
12303
|
-
"note": "F1KBNa_note",
|
|
12304
|
-
"rowPath": "F1KBNa_rowPath",
|
|
12305
|
-
"detail": "F1KBNa_detail",
|
|
12306
|
-
"confirmActions": "F1KBNa_confirmActions",
|
|
12307
|
-
"badgeLabel": "F1KBNa_badgeLabel",
|
|
12308
|
-
"blockActions": "F1KBNa_blockActions",
|
|
12309
12620
|
"blockPosition": "F1KBNa_blockPosition",
|
|
12310
|
-
"
|
|
12311
|
-
"
|
|
12312
|
-
"subline": "F1KBNa_subline",
|
|
12313
|
-
"settingsSelector": "F1KBNa_settingsSelector",
|
|
12314
|
-
"kindHint": "F1KBNa_kindHint",
|
|
12315
|
-
"notice": "F1KBNa_notice",
|
|
12316
|
-
"noticeText": "F1KBNa_noticeText",
|
|
12317
|
-
"markerAdd": "F1KBNa_markerAdd",
|
|
12318
|
-
"flexSpacer": "F1KBNa_flexSpacer",
|
|
12621
|
+
"splitHScrollFill": "F1KBNa_splitHScrollFill",
|
|
12622
|
+
"badgeLabel": "F1KBNa_badgeLabel",
|
|
12319
12623
|
"stepper": "F1KBNa_stepper",
|
|
12624
|
+
"statusBar": "F1KBNa_statusBar",
|
|
12625
|
+
"markerDel": "F1KBNa_markerDel",
|
|
12626
|
+
"confirmCard": "F1KBNa_confirmCard",
|
|
12627
|
+
"blockActions": "F1KBNa_blockActions",
|
|
12320
12628
|
"toggle": "F1KBNa_toggle",
|
|
12321
|
-
"
|
|
12322
|
-
"
|
|
12323
|
-
"
|
|
12324
|
-
"
|
|
12629
|
+
"missingHint": "F1KBNa_missingHint",
|
|
12630
|
+
"diffHeader": "F1KBNa_diffHeader",
|
|
12631
|
+
"splitRdel": "F1KBNa_splitRdel",
|
|
12632
|
+
"headerActions": "F1KBNa_headerActions",
|
|
12633
|
+
"actionPrimary": "F1KBNa_actionPrimary",
|
|
12634
|
+
"readError": "F1KBNa_readError",
|
|
12635
|
+
"splitCols": "F1KBNa_splitCols",
|
|
12636
|
+
"kindHint": "F1KBNa_kindHint",
|
|
12637
|
+
"settingsRowText": "F1KBNa_settingsRowText",
|
|
12638
|
+
"blockFlash": "F1KBNa_blockFlash",
|
|
12639
|
+
"langSelect": "F1KBNa_langSelect",
|
|
12640
|
+
"settingsGroup": "F1KBNa_settingsGroup",
|
|
12641
|
+
"wrapActive": "F1KBNa_wrapActive",
|
|
12325
12642
|
"rowFailed": "F1KBNa_rowFailed",
|
|
12326
12643
|
"del": "F1KBNa_del",
|
|
12327
|
-
"
|
|
12328
|
-
"
|
|
12329
|
-
"
|
|
12330
|
-
"
|
|
12331
|
-
"
|
|
12332
|
-
"
|
|
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",
|
|
12653
|
+
"rowHead": "F1KBNa_rowHead",
|
|
12654
|
+
"settingsSelector": "F1KBNa_settingsSelector",
|
|
12655
|
+
"note": "F1KBNa_note",
|
|
12656
|
+
"row": "F1KBNa_row",
|
|
12657
|
+
"markerAdd": "F1KBNa_markerAdd",
|
|
12658
|
+
"intraAdd": "F1KBNa_intraAdd",
|
|
12659
|
+
"importButton": "F1KBNa_importButton",
|
|
12333
12660
|
"overviewMarker": "F1KBNa_overviewMarker",
|
|
12334
|
-
"
|
|
12335
|
-
"
|
|
12336
|
-
"splitRdel": "F1KBNa_splitRdel",
|
|
12337
|
-
"missing": "F1KBNa_missing",
|
|
12338
|
-
"statusBar": "F1KBNa_statusBar",
|
|
12661
|
+
"bulkActions": "F1KBNa_bulkActions",
|
|
12662
|
+
"rowPath": "F1KBNa_rowPath",
|
|
12339
12663
|
"diffStats": "F1KBNa_diffStats",
|
|
12340
|
-
"
|
|
12341
|
-
"delCount": "F1KBNa_delCount",
|
|
12664
|
+
"emptyState": "F1KBNa_emptyState",
|
|
12342
12665
|
"toggleThumb": "F1KBNa_toggleThumb",
|
|
12343
|
-
"
|
|
12344
|
-
"
|
|
12345
|
-
"
|
|
12666
|
+
"stepperValue": "F1KBNa_stepperValue",
|
|
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",
|
|
12679
|
+
"splitDivider": "F1KBNa_splitDivider",
|
|
12680
|
+
"confirmBackdrop": "F1KBNa_confirmBackdrop",
|
|
12681
|
+
"diffBodyWrap": "F1KBNa_diffBodyWrap",
|
|
12682
|
+
"splitLdel": "F1KBNa_splitLdel",
|
|
12683
|
+
"add": "F1KBNa_add",
|
|
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"
|
|
12346
12691
|
};
|
|
12347
12692
|
//#endregion
|
|
12348
12693
|
//#region lib/types/client/PendingPanel.js
|
|
@@ -12382,6 +12727,47 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
12382
12727
|
* so a sub-pixel float error (wrapped row heights, fractional scrollTop) never
|
|
12383
12728
|
* mis-classifies the block the view is sitting on. Half a row height. */
|
|
12384
12729
|
const NAV_ANCHOR_TOLERANCE_PX = ROW_HEIGHT_PX / 4;
|
|
12730
|
+
/** The diff view-mode toggle glyph: the whole file as one column of text lines
|
|
12731
|
+
* (unified) or two side-by-side columns of text lines (split). Hand-drawn
|
|
12732
|
+
* because the icon library has no single/double-column glyph. Rendered 1:1
|
|
12733
|
+
* (viewBox matches the size) with integer bar geometry, so every thin line
|
|
12734
|
+
* lands on whole pixels and stays crisp on any display scale. */
|
|
12735
|
+
function ViewModeIcon({ split, size = 14 }) {
|
|
12736
|
+
const lineY = [
|
|
12737
|
+
1,
|
|
12738
|
+
4,
|
|
12739
|
+
7,
|
|
12740
|
+
10,
|
|
12741
|
+
13
|
|
12742
|
+
];
|
|
12743
|
+
const lineH = 1;
|
|
12744
|
+
return (0, react_jsx_runtime.jsx)("svg", {
|
|
12745
|
+
width: size,
|
|
12746
|
+
height: size,
|
|
12747
|
+
viewBox: "0 0 14 14",
|
|
12748
|
+
fill: "none",
|
|
12749
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
12750
|
+
children: split ? (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [lineY.map((y) => (0, react_jsx_runtime.jsx)("rect", {
|
|
12751
|
+
x: "1",
|
|
12752
|
+
y,
|
|
12753
|
+
width: "5",
|
|
12754
|
+
height: lineH,
|
|
12755
|
+
fill: "currentColor"
|
|
12756
|
+
}, `l${y}`)), lineY.map((y) => (0, react_jsx_runtime.jsx)("rect", {
|
|
12757
|
+
x: "8",
|
|
12758
|
+
y,
|
|
12759
|
+
width: "5",
|
|
12760
|
+
height: lineH,
|
|
12761
|
+
fill: "currentColor"
|
|
12762
|
+
}, `r${y}`))] }) : lineY.map((y) => (0, react_jsx_runtime.jsx)("rect", {
|
|
12763
|
+
x: "1",
|
|
12764
|
+
y,
|
|
12765
|
+
width: "12",
|
|
12766
|
+
height: lineH,
|
|
12767
|
+
fill: "currentColor"
|
|
12768
|
+
}, `u${y}`))
|
|
12769
|
+
});
|
|
12770
|
+
}
|
|
12385
12771
|
/** Total width of the two line-number gutters, subtracted from the code width
|
|
12386
12772
|
* when measuring wrapped line heights. */
|
|
12387
12773
|
const WRAP_GUTTERS_PX = 88;
|
|
@@ -12645,6 +13031,53 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
12645
13031
|
}, i);
|
|
12646
13032
|
});
|
|
12647
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
|
+
}
|
|
12648
13081
|
/**
|
|
12649
13082
|
* One rendered diff row, memoized so a poll or an unrelated state change
|
|
12650
13083
|
* does not re-render rows whose content, highlight, and focus are unchanged.
|
|
@@ -12653,22 +13086,18 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
12653
13086
|
* is `wrappedLines.length * 22` by construction.
|
|
12654
13087
|
*/
|
|
12655
13088
|
const DiffRow = (0, react.memo)(function DiffRow(props) {
|
|
12656
|
-
const { index, row, runs, focused, searchHit, searchCurrent, onRowHover, wrappedLines } = props;
|
|
13089
|
+
const { index, row, runs, focused, searchHit, searchCurrent, searchQuery, onRowHover, wrappedLines } = props;
|
|
12657
13090
|
const lineNumber = row.kind === "del" ? row.oldLine : row.newLine;
|
|
12658
13091
|
const sideRuns = row.kind === "del" ? runs?.oldRuns : runs?.newRuns;
|
|
12659
13092
|
const lineRuns = lineNumber === void 0 ? void 0 : sideRuns?.[lineNumber - 1];
|
|
12660
13093
|
let code;
|
|
12661
|
-
if (wrappedLines === void 0) code = lineRuns
|
|
12662
|
-
style: span.style,
|
|
12663
|
-
children: span.text
|
|
12664
|
-
}, spanIndex)) : row.text === "" ? "\xA0" : row.text;
|
|
13094
|
+
if (wrappedLines === void 0) code = textWithSearch(row.text, lineRuns, void 0, searchQuery, 0, row.text.length, searchCurrent);
|
|
12665
13095
|
else {
|
|
12666
|
-
const highlighted = lineRuns !== void 0 && lineRuns.length > 0;
|
|
12667
13096
|
let offset = 0;
|
|
12668
13097
|
code = wrappedLines.map((line, lineIndex) => {
|
|
12669
13098
|
const start = offset;
|
|
12670
13099
|
offset += line.length;
|
|
12671
|
-
const content =
|
|
13100
|
+
const content = textWithSearch(row.text, lineRuns, void 0, searchQuery, start, offset, searchCurrent);
|
|
12672
13101
|
return (0, react_jsx_runtime.jsx)("div", {
|
|
12673
13102
|
className: PendingPanel_module_css_default.subline,
|
|
12674
13103
|
children: content
|
|
@@ -12687,10 +13116,12 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
12687
13116
|
children: [
|
|
12688
13117
|
(0, react_jsx_runtime.jsx)("span", {
|
|
12689
13118
|
className: PendingPanel_module_css_default.gutter,
|
|
13119
|
+
"data-diff-gutter": true,
|
|
12690
13120
|
children: row.oldLine ?? ""
|
|
12691
13121
|
}),
|
|
12692
13122
|
(0, react_jsx_runtime.jsx)("span", {
|
|
12693
13123
|
className: PendingPanel_module_css_default.gutter,
|
|
13124
|
+
"data-diff-gutter": true,
|
|
12694
13125
|
children: row.newLine ?? ""
|
|
12695
13126
|
}),
|
|
12696
13127
|
(0, react_jsx_runtime.jsx)("span", {
|
|
@@ -12764,20 +13195,23 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
12764
13195
|
});
|
|
12765
13196
|
return blocks;
|
|
12766
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
|
+
}
|
|
12767
13206
|
/** One side's line-content for the split view: the highlighted runs or plain text. */
|
|
12768
|
-
function splitSideContent(side, wrapped, runs, intra) {
|
|
13207
|
+
function splitSideContent(side, wrapped, runs, intra, query, current) {
|
|
12769
13208
|
if (side === void 0) return "";
|
|
12770
|
-
|
|
12771
|
-
const hasIntra = intra !== void 0 && intra.length > 0;
|
|
12772
|
-
if (wrapped === void 0) return hasIntra ? renderIntra(runs, intra, 0, side.text.length) : highlighted ? runs.map((span, i) => (0, react_jsx_runtime.jsx)("span", {
|
|
12773
|
-
style: span.style,
|
|
12774
|
-
children: span.text
|
|
12775
|
-
}, i)) : side.text === "" ? "\xA0" : side.text;
|
|
13209
|
+
if (wrapped === void 0) return textWithSearch(side.text, runs, intra, query, 0, side.text.length, current);
|
|
12776
13210
|
let offset = 0;
|
|
12777
13211
|
return wrapped.map((line, i) => {
|
|
12778
13212
|
const start = offset;
|
|
12779
13213
|
offset += line.length;
|
|
12780
|
-
const content =
|
|
13214
|
+
const content = textWithSearch(side.text, runs, intra, query, start, offset, current);
|
|
12781
13215
|
return (0, react_jsx_runtime.jsx)("div", {
|
|
12782
13216
|
className: PendingPanel_module_css_default.subline,
|
|
12783
13217
|
children: content
|
|
@@ -12793,7 +13227,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
12793
13227
|
* one side is longer. The gutter and code are top-aligned so sub-lines line up
|
|
12794
13228
|
* across the divider.
|
|
12795
13229
|
*/
|
|
12796
|
-
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 }) {
|
|
12797
13231
|
const tint = isLeft ? kind === "del" || kind === "replace" ? PendingPanel_module_css_default.splitLdel : "" : kind === "add" || kind === "replace" ? PendingPanel_module_css_default.splitRadd : "";
|
|
12798
13232
|
return (0, react_jsx_runtime.jsxs)("div", {
|
|
12799
13233
|
className: PendingPanel_module_css_default.line,
|
|
@@ -12806,16 +13240,17 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
12806
13240
|
onMouseEnter: onHover,
|
|
12807
13241
|
children: [(0, react_jsx_runtime.jsx)("span", {
|
|
12808
13242
|
className: PendingPanel_module_css_default.gutter,
|
|
13243
|
+
"data-diff-gutter": true,
|
|
12809
13244
|
children: side?.line ?? ""
|
|
12810
13245
|
}), (0, react_jsx_runtime.jsx)("span", {
|
|
12811
13246
|
className: `${PendingPanel_module_css_default.code} ${tint}`,
|
|
12812
13247
|
"data-diff-code": true,
|
|
12813
|
-
children: splitSideContent(side, wrapped, runs, intra)
|
|
13248
|
+
children: splitSideContent(side, wrapped, runs, intra, searchQuery, searchCurrent)
|
|
12814
13249
|
})]
|
|
12815
13250
|
});
|
|
12816
13251
|
}
|
|
12817
13252
|
/** The two-column (side-by-side) whole-file diff view. */
|
|
12818
|
-
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) {
|
|
12819
13254
|
const { pairs, pairOfRow } = (0, react.useMemo)(() => computeSideBySideDiff(model.diff.rows, true), [model]);
|
|
12820
13255
|
const pairCount = pairs.length;
|
|
12821
13256
|
const pairRowIndices = (0, react.useMemo)(() => {
|
|
@@ -12837,6 +13272,11 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
12837
13272
|
const [hoveredBlock, setHoveredBlock] = (0, react.useState)(void 0);
|
|
12838
13273
|
const [focus, setFocus] = (0, react.useState)(0);
|
|
12839
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
|
+
};
|
|
12840
13280
|
const hoveredBlockRef = (0, react.useRef)(void 0);
|
|
12841
13281
|
const leftColRef = (0, react.useRef)(null);
|
|
12842
13282
|
const rightColRef = (0, react.useRef)(null);
|
|
@@ -12853,7 +13293,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
12853
13293
|
(0, react.useEffect)(() => {
|
|
12854
13294
|
setFocus(0);
|
|
12855
13295
|
bodyRef.current?.focus();
|
|
12856
|
-
|
|
13296
|
+
bumpFlash(false);
|
|
12857
13297
|
setHoveredBlock(void 0);
|
|
12858
13298
|
setSearchOpen(false);
|
|
12859
13299
|
setSearchQuery("");
|
|
@@ -12873,17 +13313,30 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
12873
13313
|
observer?.disconnect();
|
|
12874
13314
|
};
|
|
12875
13315
|
}, [file.id]);
|
|
12876
|
-
const blockOfPair = (0, react.useMemo)(() => model.blocks.map((block) =>
|
|
12877
|
-
|
|
12878
|
-
|
|
12879
|
-
|
|
13316
|
+
const blockOfPair = (0, react.useMemo)(() => model.blocks.map((block) => {
|
|
13317
|
+
let start = Number.POSITIVE_INFINITY;
|
|
13318
|
+
let end = Number.NEGATIVE_INFINITY;
|
|
13319
|
+
for (let row = block.start; row <= block.end; row++) {
|
|
13320
|
+
const pair = pairOfRow.get(row);
|
|
13321
|
+
if (pair === void 0) continue;
|
|
13322
|
+
if (pair < start) start = pair;
|
|
13323
|
+
if (pair > end) end = pair;
|
|
13324
|
+
}
|
|
13325
|
+
return {
|
|
13326
|
+
start: Number.isFinite(start) ? start : 0,
|
|
13327
|
+
end: Number.isFinite(end) ? end : 0
|
|
13328
|
+
};
|
|
13329
|
+
}), [model, pairOfRow]);
|
|
12880
13330
|
const blockIndexByPair = (0, react.useMemo)(() => {
|
|
12881
13331
|
const map = /* @__PURE__ */ new Map();
|
|
12882
|
-
|
|
12883
|
-
for (let
|
|
13332
|
+
model.blocks.forEach((block, bi) => {
|
|
13333
|
+
for (let row = block.start; row <= block.end; row++) {
|
|
13334
|
+
const pair = pairOfRow.get(row);
|
|
13335
|
+
if (pair !== void 0) map.set(pair, bi);
|
|
13336
|
+
}
|
|
12884
13337
|
});
|
|
12885
13338
|
return map;
|
|
12886
|
-
}, [
|
|
13339
|
+
}, [model, pairOfRow]);
|
|
12887
13340
|
const onPairHover = (0, react.useCallback)((k) => {
|
|
12888
13341
|
const bi = blockIndexByPair.get(k);
|
|
12889
13342
|
setHoveredBlock(bi);
|
|
@@ -12970,6 +13423,30 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
12970
13423
|
bodyRef.current?.focus();
|
|
12971
13424
|
};
|
|
12972
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);
|
|
12973
13450
|
setSearchOpen(true);
|
|
12974
13451
|
requestAnimationFrame(() => {
|
|
12975
13452
|
searchInputRef.current?.focus();
|
|
@@ -12985,7 +13462,15 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
12985
13462
|
if (pairIndex === void 0) return;
|
|
12986
13463
|
const body = bodyRef.current;
|
|
12987
13464
|
if (body === null) return;
|
|
12988
|
-
|
|
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;
|
|
12989
13474
|
const clamped = Math.max(0, Math.min(target, body.scrollHeight - body.clientHeight));
|
|
12990
13475
|
if (body.scrollTop !== clamped) body.scrollTop = clamped;
|
|
12991
13476
|
setScrollTop(clamped);
|
|
@@ -13001,7 +13486,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
13001
13486
|
const next = Math.max(0, Math.min(operated, count - 1));
|
|
13002
13487
|
setFocus(next);
|
|
13003
13488
|
setHoveredBlock(void 0);
|
|
13004
|
-
|
|
13489
|
+
bumpFlash(false);
|
|
13005
13490
|
};
|
|
13006
13491
|
const pairAtY = (y) => {
|
|
13007
13492
|
if (pairOffsets === null) return Math.floor(y / ROW_HEIGHT_PX);
|
|
@@ -13022,20 +13507,52 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
13022
13507
|
end = Math.max(end, selection.end + 1);
|
|
13023
13508
|
}
|
|
13024
13509
|
const visiblePairs = pairs.slice(start, end);
|
|
13025
|
-
const
|
|
13026
|
-
|
|
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
|
+
}
|
|
13027
13526
|
setFocus((current) => {
|
|
13028
|
-
if (direction === -1) return (current - 1 +
|
|
13527
|
+
if (direction === -1) return (current - 1 + count) % count;
|
|
13029
13528
|
const top = bodyRef.current?.scrollTop ?? 0;
|
|
13030
|
-
for (let index = current + 1; index <
|
|
13529
|
+
for (let index = current + 1; index < count; index++) if (off(blockOfPair[index].start) >= top) return index;
|
|
13031
13530
|
return 0;
|
|
13032
13531
|
});
|
|
13033
|
-
|
|
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;
|
|
13034
13546
|
};
|
|
13035
13547
|
(0, react.useImperativeHandle)(ref, () => ({
|
|
13036
13548
|
jump,
|
|
13037
|
-
openSearch
|
|
13038
|
-
|
|
13549
|
+
openSearch,
|
|
13550
|
+
searchNext
|
|
13551
|
+
}), [
|
|
13552
|
+
jump,
|
|
13553
|
+
openSearch,
|
|
13554
|
+
searchNext
|
|
13555
|
+
]);
|
|
13039
13556
|
(0, react.useLayoutEffect)(() => {
|
|
13040
13557
|
if (pairCount === 0) return;
|
|
13041
13558
|
const block = blockOfPair[focus];
|
|
@@ -13053,9 +13570,16 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
13053
13570
|
setScrollTop(body.scrollTop);
|
|
13054
13571
|
const count = blockOfPair.length;
|
|
13055
13572
|
if (count === 0) return;
|
|
13056
|
-
const anchor = body.scrollTop + leadRows * ROW_HEIGHT_PX;
|
|
13057
13573
|
let ref = -1;
|
|
13058
|
-
|
|
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
|
+
}
|
|
13059
13583
|
setFocus(ref === -1 ? 0 : ref);
|
|
13060
13584
|
};
|
|
13061
13585
|
const inFocused = (k) => {
|
|
@@ -13110,6 +13634,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
13110
13634
|
focused: inFocused(index),
|
|
13111
13635
|
searchHit: searchHitSet.has(index),
|
|
13112
13636
|
searchCurrent: index === currentSearchPair,
|
|
13637
|
+
searchQuery,
|
|
13113
13638
|
onHover: () => onPairHover(index),
|
|
13114
13639
|
intra: sideIndex?.left === void 0 ? void 0 : model.intra.get(sideIndex.left)
|
|
13115
13640
|
}, index);
|
|
@@ -13151,6 +13676,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
13151
13676
|
focused: inFocused(index),
|
|
13152
13677
|
searchHit: searchHitSet.has(index),
|
|
13153
13678
|
searchCurrent: index === currentSearchPair,
|
|
13679
|
+
searchQuery,
|
|
13154
13680
|
onHover: () => onPairHover(index),
|
|
13155
13681
|
intra: sideIndex?.right === void 0 ? void 0 : model.intra.get(sideIndex.right)
|
|
13156
13682
|
}, index);
|
|
@@ -13212,8 +13738,15 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
13212
13738
|
value: searchQuery,
|
|
13213
13739
|
placeholder: t("panel.searchPlaceholder"),
|
|
13214
13740
|
onChange: (event) => {
|
|
13215
|
-
|
|
13216
|
-
|
|
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);
|
|
13217
13750
|
},
|
|
13218
13751
|
onKeyDown: (event) => {
|
|
13219
13752
|
if (event.key === "Enter") {
|
|
@@ -13227,27 +13760,37 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
13227
13760
|
"data-diff-search-count": true,
|
|
13228
13761
|
children: searchMatches.length === 0 ? "0/0" : `${searchIndex % searchMatches.length + 1}/${searchMatches.length}`
|
|
13229
13762
|
}),
|
|
13230
|
-
(0, react_jsx_runtime.jsx)(
|
|
13231
|
-
|
|
13232
|
-
|
|
13233
|
-
|
|
13234
|
-
|
|
13235
|
-
|
|
13236
|
-
|
|
13237
|
-
|
|
13238
|
-
|
|
13239
|
-
|
|
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
|
+
})
|
|
13240
13778
|
}),
|
|
13241
|
-
(0, react_jsx_runtime.jsx)(
|
|
13242
|
-
|
|
13243
|
-
|
|
13244
|
-
|
|
13245
|
-
|
|
13246
|
-
|
|
13247
|
-
|
|
13248
|
-
|
|
13249
|
-
|
|
13250
|
-
|
|
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
|
+
})
|
|
13251
13794
|
}),
|
|
13252
13795
|
(0, react_jsx_runtime.jsx)("button", {
|
|
13253
13796
|
type: "button",
|
|
@@ -13260,7 +13803,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
13260
13803
|
]
|
|
13261
13804
|
}),
|
|
13262
13805
|
focusedBlock !== void 0 && flashKey > 0 && (0, react_jsx_runtime.jsx)("div", {
|
|
13263
|
-
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,
|
|
13264
13807
|
"data-diff-block-flash": true,
|
|
13265
13808
|
style: {
|
|
13266
13809
|
top: flashTop,
|
|
@@ -13286,7 +13829,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
13286
13829
|
"data-diff-block-prev": true,
|
|
13287
13830
|
"aria-label": t("action.prevDiff"),
|
|
13288
13831
|
disabled: busy,
|
|
13289
|
-
onClick: () =>
|
|
13832
|
+
onClick: () => stepBlock(-1),
|
|
13290
13833
|
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronUpOutline14, { size: 14 })
|
|
13291
13834
|
}),
|
|
13292
13835
|
(0, react_jsx_runtime.jsx)("button", {
|
|
@@ -13295,7 +13838,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
13295
13838
|
"data-diff-block-next": true,
|
|
13296
13839
|
"aria-label": t("action.nextDiff"),
|
|
13297
13840
|
disabled: busy,
|
|
13298
|
-
onClick: () =>
|
|
13841
|
+
onClick: () => stepBlock(1),
|
|
13299
13842
|
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, { size: 14 })
|
|
13300
13843
|
}),
|
|
13301
13844
|
(0, react_jsx_runtime.jsx)("button", {
|
|
@@ -13435,6 +13978,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
13435
13978
|
}
|
|
13436
13979
|
if (!(node instanceof Element)) return;
|
|
13437
13980
|
const el = node;
|
|
13981
|
+
if (el.dataset.diffGutter !== void 0) return;
|
|
13438
13982
|
if ((el.dataset.diffRow !== void 0 || el.dataset.diffSplitRow !== void 0 || el.dataset.diffSplitIndex !== void 0) && !atLineStart) push("\n");
|
|
13439
13983
|
for (const child of node.childNodes) walk(child);
|
|
13440
13984
|
};
|
|
@@ -13536,8 +14080,17 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
13536
14080
|
setWrapEnabled(wrapKey, next);
|
|
13537
14081
|
};
|
|
13538
14082
|
const [tabWidthSpaces] = (0, react.useState)(() => tabWidth());
|
|
13539
|
-
const splitView = splitMode();
|
|
14083
|
+
const [splitView, setSplitView] = (0, react.useState)(() => splitMode());
|
|
13540
14084
|
const leadRows = navLeadRows();
|
|
14085
|
+
const toggleSplitView = () => {
|
|
14086
|
+
const next = !splitView;
|
|
14087
|
+
setSplitView(next);
|
|
14088
|
+
setSplitMode(next);
|
|
14089
|
+
if (!next) {
|
|
14090
|
+
setScrollTop(0);
|
|
14091
|
+
setScrollTick((tick) => tick + 1);
|
|
14092
|
+
}
|
|
14093
|
+
};
|
|
13541
14094
|
const splitDiffRef = (0, react.useRef)(null);
|
|
13542
14095
|
const model = (0, react.useMemo)(() => {
|
|
13543
14096
|
const diff = computeWholeFileDiff(file.oldText, file.newText);
|
|
@@ -13615,17 +14168,23 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
13615
14168
|
const [hScrollbarPx, setHScrollbarPx] = (0, react.useState)(0);
|
|
13616
14169
|
const [hoveredBlock, setHoveredBlock] = (0, react.useState)(void 0);
|
|
13617
14170
|
const [selection, setSelection] = (0, react.useState)(void 0);
|
|
14171
|
+
const selectionTextRef = (0, react.useRef)("");
|
|
13618
14172
|
const [copied, setCopied] = (0, react.useState)(false);
|
|
13619
14173
|
const [searchOpen, setSearchOpen] = (0, react.useState)(false);
|
|
13620
14174
|
const [searchQuery, setSearchQuery] = (0, react.useState)("");
|
|
13621
14175
|
const [searchIndex, setSearchIndex] = (0, react.useState)(0);
|
|
13622
14176
|
const searchInputRef = (0, react.useRef)(null);
|
|
13623
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
|
+
};
|
|
13624
14183
|
(0, react.useEffect)(() => {
|
|
13625
14184
|
setFocus(0);
|
|
13626
14185
|
setScrollTick((tick) => tick + 1);
|
|
13627
14186
|
bodyRef.current?.focus();
|
|
13628
|
-
|
|
14187
|
+
bumpFlash(false);
|
|
13629
14188
|
setHoveredBlock(void 0);
|
|
13630
14189
|
setSelection(void 0);
|
|
13631
14190
|
setLangOverride(void 0);
|
|
@@ -13639,7 +14198,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
13639
14198
|
if (undoFlash === 0) return;
|
|
13640
14199
|
setFocus(0);
|
|
13641
14200
|
setScrollTick((tick) => tick + 1);
|
|
13642
|
-
|
|
14201
|
+
bumpFlash(false);
|
|
13643
14202
|
}, [undoFlash]);
|
|
13644
14203
|
const blockRanges = (0, react.useMemo)(() => {
|
|
13645
14204
|
return model.blocks.map((block) => blockRangesOf(model.diff.rows, block));
|
|
@@ -13670,27 +14229,74 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
13670
14229
|
end: last.end
|
|
13671
14230
|
});
|
|
13672
14231
|
}, [coveredBlockIndices, model]);
|
|
13673
|
-
const searchMatches = (0, react.useMemo)(() =>
|
|
13674
|
-
if (searchQuery === "") return [];
|
|
13675
|
-
const lower = searchQuery.toLowerCase();
|
|
13676
|
-
const matches = [];
|
|
13677
|
-
for (let index = 0; index < model.diff.rows.length; index++) if (model.diff.rows[index].text.toLowerCase().includes(lower)) matches.push(index);
|
|
13678
|
-
return matches;
|
|
13679
|
-
}, [model, searchQuery]);
|
|
14232
|
+
const searchMatches = (0, react.useMemo)(() => matchingRows(model.diff.rows, searchQuery), [model, searchQuery]);
|
|
13680
14233
|
const searchHitSet = (0, react.useMemo)(() => new Set(searchMatches), [searchMatches]);
|
|
13681
14234
|
const currentSearchRow = searchMatches.length === 0 ? void 0 : searchMatches[searchIndex % searchMatches.length];
|
|
13682
14235
|
const goSearch = (direction) => {
|
|
13683
14236
|
if (searchMatches.length === 0) return;
|
|
14237
|
+
if (cursorPosRef.current !== void 0) {
|
|
14238
|
+
setSearchIndex(startIndexFor(searchQuery));
|
|
14239
|
+
return;
|
|
14240
|
+
}
|
|
13684
14241
|
setSearchIndex((current) => (current + direction + searchMatches.length) % searchMatches.length);
|
|
13685
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;
|
|
13686
14288
|
const toggleSearch = () => {
|
|
13687
14289
|
if (searchOpen) {
|
|
14290
|
+
cursorPosRef.current = void 0;
|
|
14291
|
+
lastRecordedCursorRef.current = void 0;
|
|
13688
14292
|
setSearchOpen(false);
|
|
13689
14293
|
setSearchQuery("");
|
|
13690
14294
|
setSearchIndex(0);
|
|
13691
|
-
} else
|
|
14295
|
+
} else openSearchWithSelection();
|
|
13692
14296
|
};
|
|
13693
14297
|
const closeSearch = () => {
|
|
14298
|
+
cursorPosRef.current = void 0;
|
|
14299
|
+
lastRecordedCursorRef.current = void 0;
|
|
13694
14300
|
setSearchOpen(false);
|
|
13695
14301
|
setSearchQuery("");
|
|
13696
14302
|
setSearchIndex(0);
|
|
@@ -13698,15 +14304,20 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
13698
14304
|
(0, react.useEffect)(() => {
|
|
13699
14305
|
if (searchOpen) searchInputRef.current?.focus();
|
|
13700
14306
|
}, [searchOpen]);
|
|
13701
|
-
(0, react.useEffect)(() => {
|
|
13702
|
-
setSearchIndex(0);
|
|
13703
|
-
}, [searchQuery, file.id]);
|
|
13704
14307
|
(0, react.useLayoutEffect)(() => {
|
|
13705
14308
|
const row = currentSearchRow;
|
|
13706
14309
|
if (row === void 0) return;
|
|
13707
14310
|
const body = bodyRef.current;
|
|
13708
14311
|
if (body === null) return;
|
|
13709
|
-
|
|
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;
|
|
13710
14321
|
const clamped = Math.max(0, Math.min(target, body.scrollHeight - body.clientHeight));
|
|
13711
14322
|
if (body.scrollTop !== clamped) body.scrollTop = clamped;
|
|
13712
14323
|
setScrollTop(clamped);
|
|
@@ -13778,14 +14389,14 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
13778
14389
|
}
|
|
13779
14390
|
const visibleRows = rows.slice(start, end);
|
|
13780
14391
|
const blockEnd = hoveredBlock === void 0 ? void 0 : model.blocks[hoveredBlock]?.end;
|
|
13781
|
-
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)));
|
|
13782
14393
|
const selectionBlockEnd = (() => {
|
|
13783
14394
|
if (coveredBlockIndices.length === 0) return void 0;
|
|
13784
14395
|
const lastIndex = coveredBlockIndices[coveredBlockIndices.length - 1];
|
|
13785
14396
|
if (lastIndex === void 0) return void 0;
|
|
13786
14397
|
return model.blocks[lastIndex]?.end;
|
|
13787
14398
|
})();
|
|
13788
|
-
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)));
|
|
13789
14400
|
const widestLine = (0, react.useMemo)(() => {
|
|
13790
14401
|
let widest = 0;
|
|
13791
14402
|
for (const row of model.diff.rows) if (row.text.length > widest) widest = row.text.length;
|
|
@@ -13803,7 +14414,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
13803
14414
|
return () => {
|
|
13804
14415
|
observer?.disconnect();
|
|
13805
14416
|
};
|
|
13806
|
-
}, [file.id]);
|
|
14417
|
+
}, [file.id, splitView]);
|
|
13807
14418
|
(0, react.useEffect)(() => {
|
|
13808
14419
|
const body = bodyRef.current;
|
|
13809
14420
|
if (body === null) return;
|
|
@@ -13829,12 +14440,27 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
13829
14440
|
if (body.scrollTop !== clamped) body.scrollTop = clamped;
|
|
13830
14441
|
setScrollTop(clamped);
|
|
13831
14442
|
}, [scrollTick, rowOffsets === null]);
|
|
13832
|
-
const
|
|
14443
|
+
const wrapArmedRef = (0, react.useRef)(0);
|
|
14444
|
+
const jump = (direction, wrapGuard = false) => {
|
|
13833
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
|
+
}
|
|
13834
14460
|
setFocus((current) => {
|
|
13835
|
-
if (direction === -1) return (current - 1 +
|
|
14461
|
+
if (direction === -1) return (current - 1 + count) % count;
|
|
13836
14462
|
const top = bodyRef.current?.scrollTop ?? 0;
|
|
13837
|
-
for (let index = current + 1; index <
|
|
14463
|
+
for (let index = current + 1; index < count; index++) {
|
|
13838
14464
|
const block = model.blocks[index];
|
|
13839
14465
|
if (block === void 0) continue;
|
|
13840
14466
|
if (offsetOf(block.start) >= top) return index;
|
|
@@ -13842,14 +14468,14 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
13842
14468
|
return 0;
|
|
13843
14469
|
});
|
|
13844
14470
|
setScrollTick((tick) => tick + 1);
|
|
13845
|
-
|
|
14471
|
+
bumpFlash(false);
|
|
13846
14472
|
};
|
|
13847
|
-
const jumpBlock = (direction) => {
|
|
14473
|
+
const jumpBlock = (direction, wrapGuard = false) => {
|
|
13848
14474
|
if (splitView) {
|
|
13849
|
-
splitDiffRef.current?.jump(direction);
|
|
14475
|
+
splitDiffRef.current?.jump(direction, wrapGuard);
|
|
13850
14476
|
return;
|
|
13851
14477
|
}
|
|
13852
|
-
jump(direction);
|
|
14478
|
+
jump(direction, wrapGuard);
|
|
13853
14479
|
};
|
|
13854
14480
|
const jumpBlockRef = (0, react.useRef)(jumpBlock);
|
|
13855
14481
|
jumpBlockRef.current = jumpBlock;
|
|
@@ -13860,7 +14486,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
13860
14486
|
setHoveredBlock(target);
|
|
13861
14487
|
setFocus(target);
|
|
13862
14488
|
setScrollTick((tick) => tick + 1);
|
|
13863
|
-
|
|
14489
|
+
bumpFlash(false);
|
|
13864
14490
|
};
|
|
13865
14491
|
const runBlockAction = async (action, range, operated) => {
|
|
13866
14492
|
await (action === "keep" ? onBlockKeep(file.sessionId, file.id, range) : onBlockRevert(file.sessionId, file.id, range));
|
|
@@ -13870,7 +14496,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
13870
14496
|
setFocus(next);
|
|
13871
14497
|
setHoveredBlock(void 0);
|
|
13872
14498
|
setScrollTick((tick) => tick + 1);
|
|
13873
|
-
|
|
14499
|
+
bumpFlash(false);
|
|
13874
14500
|
};
|
|
13875
14501
|
const handleBlockAction = async (action) => {
|
|
13876
14502
|
if (busy || hoveredBlock === void 0) return;
|
|
@@ -13883,10 +14509,12 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
13883
14509
|
const firstCovered = coveredBlockIndices[0];
|
|
13884
14510
|
if (firstCovered === void 0) return;
|
|
13885
14511
|
await runBlockAction(action, selectionRange, firstCovered);
|
|
14512
|
+
setSelection(void 0);
|
|
14513
|
+
window.getSelection()?.removeAllRanges?.();
|
|
13886
14514
|
};
|
|
13887
14515
|
(0, react.useEffect)(() => {
|
|
13888
14516
|
if (jumpSignal === 0) return;
|
|
13889
|
-
jumpBlock(1);
|
|
14517
|
+
jumpBlock(1, true);
|
|
13890
14518
|
}, [jumpSignal]);
|
|
13891
14519
|
const onScroll = () => {
|
|
13892
14520
|
const body = bodyRef.current;
|
|
@@ -13895,16 +14523,39 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
13895
14523
|
setViewportHeight(body.clientHeight);
|
|
13896
14524
|
const count = model.blocks.length;
|
|
13897
14525
|
if (rowCount === 0 || count === 0) return;
|
|
13898
|
-
const anchor = body.scrollTop + leadRows * ROW_HEIGHT_PX;
|
|
13899
14526
|
let ref = -1;
|
|
13900
|
-
|
|
13901
|
-
const
|
|
13902
|
-
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
|
+
}
|
|
13903
14538
|
}
|
|
13904
14539
|
setFocus(ref === -1 ? 0 : ref);
|
|
13905
14540
|
};
|
|
13906
14541
|
(0, react.useEffect)(() => {
|
|
13907
|
-
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
|
+
};
|
|
13908
14559
|
document.addEventListener("selectionchange", update);
|
|
13909
14560
|
update();
|
|
13910
14561
|
return () => {
|
|
@@ -13925,7 +14576,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
13925
14576
|
document.removeEventListener("copy", onCopy);
|
|
13926
14577
|
};
|
|
13927
14578
|
}, []);
|
|
13928
|
-
const
|
|
14579
|
+
const selectionReferenceLabel = (() => {
|
|
13929
14580
|
if (selection === void 0) return void 0;
|
|
13930
14581
|
if (splitView) {
|
|
13931
14582
|
if (selection.side === void 0 || splitPairs === null) return void 0;
|
|
@@ -13937,12 +14588,13 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
13937
14588
|
if (line !== void 0) lineNumbers.push(line);
|
|
13938
14589
|
}
|
|
13939
14590
|
if (lineNumbers.length === 0) return void 0;
|
|
13940
|
-
return
|
|
14591
|
+
return referenceLabelOf(file.path, workspacePath, Math.min(...lineNumbers), Math.max(...lineNumbers));
|
|
13941
14592
|
}
|
|
13942
14593
|
const lineNumbers = model.diff.rows.slice(selection.start, selection.end + 1).map((row) => row.newLine).filter((number) => number !== void 0);
|
|
13943
14594
|
if (lineNumbers.length === 0) return void 0;
|
|
13944
|
-
return
|
|
14595
|
+
return referenceLabelOf(file.path, workspacePath, Math.min(...lineNumbers), Math.max(...lineNumbers));
|
|
13945
14596
|
})();
|
|
14597
|
+
const selectionReference = selectionReferenceLabel === void 0 ? void 0 : `(${selectionReferenceLabel})`;
|
|
13946
14598
|
const copySelection = (0, react.useCallback)(async () => {
|
|
13947
14599
|
if (selectionReference === void 0) return;
|
|
13948
14600
|
if (pasteOnCopyEnabled()) {
|
|
@@ -13964,8 +14616,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
13964
14616
|
]);
|
|
13965
14617
|
(0, react.useEffect)(() => {
|
|
13966
14618
|
const onKeyDown = (event) => {
|
|
13967
|
-
if (!(event
|
|
13968
|
-
if (event.key.toLowerCase() !== "l") return;
|
|
14619
|
+
if (!matchesShortcut(event, keybindingOf("copyRef"))) return;
|
|
13969
14620
|
if (selectionReference === void 0) return;
|
|
13970
14621
|
event.preventDefault();
|
|
13971
14622
|
copySelection();
|
|
@@ -13977,33 +14628,55 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
13977
14628
|
}, [copySelection]);
|
|
13978
14629
|
(0, react.useEffect)(() => {
|
|
13979
14630
|
const onKeyDown = (event) => {
|
|
13980
|
-
if (!(event
|
|
13981
|
-
if (event.key.toLowerCase() !== "f") return;
|
|
14631
|
+
if (!matchesShortcut(event, keybindingOf("openSearch"))) return;
|
|
13982
14632
|
event.preventDefault();
|
|
13983
14633
|
if (splitView) {
|
|
13984
14634
|
splitDiffRef.current?.openSearch();
|
|
13985
14635
|
return;
|
|
13986
14636
|
}
|
|
13987
|
-
|
|
13988
|
-
searchInputRef.current?.focus();
|
|
13989
|
-
searchInputRef.current?.select();
|
|
14637
|
+
openSearchRef.current?.();
|
|
13990
14638
|
};
|
|
13991
14639
|
window.addEventListener("keydown", onKeyDown, true);
|
|
13992
14640
|
return () => {
|
|
13993
14641
|
window.removeEventListener("keydown", onKeyDown, true);
|
|
13994
14642
|
};
|
|
13995
|
-
}, []);
|
|
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
|
+
]);
|
|
13996
14668
|
const jumpRef = (0, react.useRef)(jumpBlock);
|
|
13997
14669
|
jumpRef.current = jumpBlock;
|
|
13998
14670
|
(0, react.useEffect)(() => {
|
|
13999
14671
|
const onKeyDown = (event) => {
|
|
14000
|
-
|
|
14001
|
-
|
|
14002
|
-
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;
|
|
14003
14676
|
const target = event.target;
|
|
14004
14677
|
if (target instanceof Element && target.closest("input, textarea, [contenteditable=\"true\"]") !== null) return;
|
|
14005
14678
|
event.preventDefault();
|
|
14006
|
-
jumpRef.current(
|
|
14679
|
+
jumpRef.current(direction, true);
|
|
14007
14680
|
};
|
|
14008
14681
|
window.addEventListener("keydown", onKeyDown, true);
|
|
14009
14682
|
return () => {
|
|
@@ -14098,7 +14771,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
14098
14771
|
"aria-label": t("action.prevDiff"),
|
|
14099
14772
|
disabled: busy,
|
|
14100
14773
|
onClick: () => {
|
|
14101
|
-
jumpBlock(-1);
|
|
14774
|
+
jumpBlock(-1, true);
|
|
14102
14775
|
},
|
|
14103
14776
|
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronUpOutline14, { size: 14 })
|
|
14104
14777
|
})
|
|
@@ -14113,7 +14786,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
14113
14786
|
"aria-label": t("action.nextDiff"),
|
|
14114
14787
|
disabled: busy,
|
|
14115
14788
|
onClick: () => {
|
|
14116
|
-
jumpBlock(1);
|
|
14789
|
+
jumpBlock(1, true);
|
|
14117
14790
|
},
|
|
14118
14791
|
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, { size: 14 })
|
|
14119
14792
|
})
|
|
@@ -14131,6 +14804,20 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
14131
14804
|
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconSearchOutline16, { size: 14 })
|
|
14132
14805
|
})
|
|
14133
14806
|
}),
|
|
14807
|
+
(0, react_jsx_runtime.jsx)("span", { className: PendingPanel_module_css_default.divider }),
|
|
14808
|
+
(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
14809
|
+
label: t(splitView ? "action.viewUnified" : "action.viewSplit"),
|
|
14810
|
+
side: "bottom",
|
|
14811
|
+
delayMs: 500,
|
|
14812
|
+
children: (0, react_jsx_runtime.jsx)("button", {
|
|
14813
|
+
type: "button",
|
|
14814
|
+
className: `${PendingPanel_module_css_default.action} ${PendingPanel_module_css_default.iconAction}`,
|
|
14815
|
+
"data-diff-toggle-view": true,
|
|
14816
|
+
"aria-label": t(splitView ? "action.viewUnified" : "action.viewSplit"),
|
|
14817
|
+
onClick: toggleSplitView,
|
|
14818
|
+
children: (0, react_jsx_runtime.jsx)(ViewModeIcon, { split: splitView })
|
|
14819
|
+
})
|
|
14820
|
+
}),
|
|
14134
14821
|
(0, react_jsx_runtime.jsx)("span", { className: PendingPanel_module_css_default.flexSpacer }),
|
|
14135
14822
|
(0, react_jsx_runtime.jsx)("button", {
|
|
14136
14823
|
type: "button",
|
|
@@ -14175,7 +14862,8 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
14175
14862
|
selection,
|
|
14176
14863
|
leadRows,
|
|
14177
14864
|
onBlockKeep,
|
|
14178
|
-
onBlockRevert
|
|
14865
|
+
onBlockRevert,
|
|
14866
|
+
onWrapToast: (text) => onToast(text)
|
|
14179
14867
|
}) : (0, react_jsx_runtime.jsxs)("div", {
|
|
14180
14868
|
className: PendingPanel_module_css_default.diffBodyWrap,
|
|
14181
14869
|
onMouseLeave: () => {
|
|
@@ -14210,6 +14898,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
14210
14898
|
focused: inFocusedBlock(index),
|
|
14211
14899
|
searchHit: searchHitSet.has(index),
|
|
14212
14900
|
searchCurrent: index === currentSearchRow,
|
|
14901
|
+
searchQuery,
|
|
14213
14902
|
onRowHover,
|
|
14214
14903
|
wrappedLines: rowWrapped?.[index]
|
|
14215
14904
|
}, index);
|
|
@@ -14303,7 +14992,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
14303
14992
|
]
|
|
14304
14993
|
}) : null,
|
|
14305
14994
|
focusedBlock !== void 0 && flashKey > 0 && (0, react_jsx_runtime.jsx)("div", {
|
|
14306
|
-
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,
|
|
14307
14996
|
"data-diff-block-flash": true,
|
|
14308
14997
|
style: {
|
|
14309
14998
|
top: flashTop,
|
|
@@ -14322,7 +15011,9 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
14322
15011
|
value: searchQuery,
|
|
14323
15012
|
placeholder: t("panel.searchPlaceholder"),
|
|
14324
15013
|
onChange: (event) => {
|
|
14325
|
-
|
|
15014
|
+
const value = event.target.value;
|
|
15015
|
+
setSearchQuery(value);
|
|
15016
|
+
setSearchIndex(startIndexFor(value));
|
|
14326
15017
|
},
|
|
14327
15018
|
onKeyDown: (event) => {
|
|
14328
15019
|
if (event.key === "Enter") {
|
|
@@ -14336,27 +15027,37 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
14336
15027
|
"data-diff-search-count": true,
|
|
14337
15028
|
children: searchMatches.length === 0 ? "0/0" : `${searchIndex % searchMatches.length + 1}/${searchMatches.length}`
|
|
14338
15029
|
}),
|
|
14339
|
-
(0, react_jsx_runtime.jsx)(
|
|
14340
|
-
|
|
14341
|
-
|
|
14342
|
-
|
|
14343
|
-
|
|
14344
|
-
|
|
14345
|
-
|
|
14346
|
-
|
|
14347
|
-
|
|
14348
|
-
|
|
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
|
+
})
|
|
14349
15045
|
}),
|
|
14350
|
-
(0, react_jsx_runtime.jsx)(
|
|
14351
|
-
|
|
14352
|
-
|
|
14353
|
-
|
|
14354
|
-
|
|
14355
|
-
|
|
14356
|
-
|
|
14357
|
-
|
|
14358
|
-
|
|
14359
|
-
|
|
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
|
+
})
|
|
14360
15061
|
}),
|
|
14361
15062
|
(0, react_jsx_runtime.jsx)("button", {
|
|
14362
15063
|
type: "button",
|
|
@@ -14392,17 +15093,25 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
14392
15093
|
label: copied ? t("action.copied") : `${t("action.copyHint")} (Ctrl+L)`,
|
|
14393
15094
|
side: "top",
|
|
14394
15095
|
delayMs: 300,
|
|
14395
|
-
children: (0, react_jsx_runtime.jsx)("
|
|
14396
|
-
|
|
15096
|
+
children: (0, react_jsx_runtime.jsx)("span", {
|
|
15097
|
+
role: "button",
|
|
15098
|
+
tabIndex: 0,
|
|
14397
15099
|
className: PendingPanel_module_css_default.statusAction,
|
|
14398
15100
|
"data-diff-copy": true,
|
|
15101
|
+
"data-mobile-nav-copy": "1",
|
|
14399
15102
|
onMouseDown: (event) => {
|
|
14400
15103
|
event.preventDefault();
|
|
14401
15104
|
},
|
|
14402
15105
|
onClick: () => {
|
|
14403
15106
|
copySelection();
|
|
14404
15107
|
},
|
|
14405
|
-
|
|
15108
|
+
onKeyDown: (event) => {
|
|
15109
|
+
if (event.key === "Enter" || event.key === " ") {
|
|
15110
|
+
event.preventDefault();
|
|
15111
|
+
copySelection();
|
|
15112
|
+
}
|
|
15113
|
+
},
|
|
15114
|
+
children: copied ? t("action.copied") : selectionReferenceLabel
|
|
14406
15115
|
})
|
|
14407
15116
|
}),
|
|
14408
15117
|
(0, react_jsx_runtime.jsx)("span", { className: PendingPanel_module_css_default.flexSpacer }),
|
|
@@ -14492,6 +15201,33 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
14492
15201
|
const [actionToast, setActionToast] = (0, react.useState)(null);
|
|
14493
15202
|
/** A transient banner confirming a reference was copied to the clipboard. */
|
|
14494
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
|
+
}, []);
|
|
14495
15231
|
/** Whether the redo-cleared notice is showing (bottom-right, OK to dismiss). */
|
|
14496
15232
|
const [redoClearedNotice, setRedoClearedNotice] = (0, react.useState)(false);
|
|
14497
15233
|
/** A file whose last block just resolved, pending a remove-or-keep choice. */
|
|
@@ -14653,7 +15389,12 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
14653
15389
|
(0, react.useEffect)(() => {
|
|
14654
15390
|
if (snapshot.justResolved === void 0) return;
|
|
14655
15391
|
setConfirmDismiss(snapshot.justResolved);
|
|
14656
|
-
|
|
15392
|
+
const timer = window.setTimeout(() => {
|
|
15393
|
+
onAckJustResolved();
|
|
15394
|
+
}, 0);
|
|
15395
|
+
return () => {
|
|
15396
|
+
window.clearTimeout(timer);
|
|
15397
|
+
};
|
|
14657
15398
|
}, [snapshot.justResolved, onAckJustResolved]);
|
|
14658
15399
|
(0, react.useEffect)(() => {
|
|
14659
15400
|
if (!open) return;
|
|
@@ -14761,15 +15502,17 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
14761
15502
|
if (!open || current === void 0) return;
|
|
14762
15503
|
const onKeyDown = (event) => {
|
|
14763
15504
|
if (!(event.ctrlKey || event.metaKey) || event.altKey) return;
|
|
14764
|
-
const key = event.key.toLowerCase();
|
|
14765
|
-
if (key !== "z" && key !== "y") return;
|
|
14766
15505
|
const target = event.target;
|
|
14767
15506
|
if (target instanceof Element && target.closest("input, textarea, [contenteditable=\"true\"]") !== null) return;
|
|
14768
|
-
event
|
|
14769
|
-
|
|
14770
|
-
|
|
14771
|
-
|
|
14772
|
-
}
|
|
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
|
+
}
|
|
14773
15516
|
};
|
|
14774
15517
|
window.addEventListener("keydown", onKeyDown, true);
|
|
14775
15518
|
return () => {
|
|
@@ -14784,14 +15527,15 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
14784
15527
|
(0, react.useEffect)(() => {
|
|
14785
15528
|
if (!open || current === void 0) return;
|
|
14786
15529
|
const onKeyDown = (event) => {
|
|
14787
|
-
|
|
14788
|
-
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;
|
|
14789
15534
|
const target = event.target;
|
|
14790
15535
|
if (target instanceof Element && target.closest("input, textarea, [contenteditable=\"true\"]") !== null) return;
|
|
14791
15536
|
if (files.length === 0) return;
|
|
14792
15537
|
event.preventDefault();
|
|
14793
15538
|
const index = files.findIndex((file) => file.id === selected);
|
|
14794
|
-
const direction = event.shiftKey ? -1 : 1;
|
|
14795
15539
|
const next = files[(index + direction + files.length) % files.length];
|
|
14796
15540
|
if (next !== void 0) setSelected(next.id);
|
|
14797
15541
|
};
|
|
@@ -14875,11 +15619,11 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
14875
15619
|
}
|
|
14876
15620
|
}),
|
|
14877
15621
|
copyToast !== null && (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Toast, {
|
|
14878
|
-
text: copyToast,
|
|
15622
|
+
text: copyToast.text,
|
|
14879
15623
|
onDone: () => {
|
|
14880
15624
|
setCopyToast(null);
|
|
14881
15625
|
}
|
|
14882
|
-
}),
|
|
15626
|
+
}, copyToast.n),
|
|
14883
15627
|
open && (0, react_dom.createPortal)((0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [expanded && (0, react_jsx_runtime.jsx)("div", {
|
|
14884
15628
|
className: PendingPanel_module_css_default.fullscreenBackdrop,
|
|
14885
15629
|
"data-diff-fullscreen-backdrop": true
|
|
@@ -15013,9 +15757,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
15013
15757
|
undoFlash,
|
|
15014
15758
|
failedMessage: failed.get(selectedFile.id),
|
|
15015
15759
|
onPasteReference,
|
|
15016
|
-
onToast:
|
|
15017
|
-
setCopyToast(text);
|
|
15018
|
-
},
|
|
15760
|
+
onToast: showCopyToast,
|
|
15019
15761
|
t,
|
|
15020
15762
|
onKeep,
|
|
15021
15763
|
onRevert,
|
|
@@ -15243,8 +15985,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
15243
15985
|
disabled: value <= min,
|
|
15244
15986
|
onClick: () => {
|
|
15245
15987
|
onChange(Math.max(min, value - 1));
|
|
15246
|
-
}
|
|
15247
|
-
children: "−"
|
|
15988
|
+
}
|
|
15248
15989
|
}),
|
|
15249
15990
|
(0, react_jsx_runtime.jsx)("span", {
|
|
15250
15991
|
className: PendingPanel_module_css_default.stepperValue,
|
|
@@ -15253,14 +15994,13 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
15253
15994
|
}),
|
|
15254
15995
|
(0, react_jsx_runtime.jsx)("button", {
|
|
15255
15996
|
type: "button",
|
|
15256
|
-
className: PendingPanel_module_css_default.stepperButton
|
|
15997
|
+
className: `${PendingPanel_module_css_default.stepperButton} ${PendingPanel_module_css_default.stepperButtonUp}`,
|
|
15257
15998
|
"data-diff-stepper-up": true,
|
|
15258
15999
|
"aria-label": t("action.increase"),
|
|
15259
16000
|
disabled: value >= max,
|
|
15260
16001
|
onClick: () => {
|
|
15261
16002
|
onChange(Math.min(max, value + 1));
|
|
15262
|
-
}
|
|
15263
|
-
children: "+"
|
|
16003
|
+
}
|
|
15264
16004
|
})
|
|
15265
16005
|
]
|
|
15266
16006
|
})]
|
|
@@ -15344,6 +16084,19 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
15344
16084
|
const [split, setSplitState] = (0, react.useState)(splitMode);
|
|
15345
16085
|
const [lead, setLeadState] = (0, react.useState)(navLeadRows);
|
|
15346
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
|
+
};
|
|
15347
16100
|
const setSummon = (value) => {
|
|
15348
16101
|
setSummonState(value);
|
|
15349
16102
|
setQuickSummonKey(value);
|
|
@@ -15372,6 +16125,41 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
15372
16125
|
className: PendingPanel_module_css_default.settingsPage,
|
|
15373
16126
|
"data-diff-settings": true,
|
|
15374
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
|
+
}),
|
|
15375
16163
|
(0, react_jsx_runtime.jsx)(PreferenceRow, {
|
|
15376
16164
|
title: t("panel.pasteOnCopy"),
|
|
15377
16165
|
description: t("panel.pasteOnCopyDesc"),
|
|
@@ -15414,14 +16202,6 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
15414
16202
|
max: 10,
|
|
15415
16203
|
dataAttribute: "data-diff-nav-lead-rows",
|
|
15416
16204
|
t
|
|
15417
|
-
}),
|
|
15418
|
-
(0, react_jsx_runtime.jsx)(ShortcutRow, {
|
|
15419
|
-
title: t("panel.quickSummon"),
|
|
15420
|
-
description: t("panel.quickSummonDesc"),
|
|
15421
|
-
value: summon,
|
|
15422
|
-
onChange: setSummon,
|
|
15423
|
-
dataAttribute: "data-diff-quick-summon-key",
|
|
15424
|
-
placeholder: t("panel.recordShortcut")
|
|
15425
16205
|
})
|
|
15426
16206
|
]
|
|
15427
16207
|
});
|
|
@@ -15928,6 +16708,13 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
15928
16708
|
if (scoped === void 0) return;
|
|
15929
16709
|
scoped.conversation.input.for(scoped.actx).setDraft(text);
|
|
15930
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
|
+
},
|
|
15931
16718
|
readQueue: () => {
|
|
15932
16719
|
const scoped = resolveConversation(ctx, sessionId());
|
|
15933
16720
|
if (scoped === void 0) return [];
|
|
@@ -15958,6 +16745,11 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
15958
16745
|
"panel.aria": "待处理改动",
|
|
15959
16746
|
"panel.stats": "+{added} -{removed}",
|
|
15960
16747
|
"panel.blockPosition": "第 {current}/{total} 块",
|
|
16748
|
+
"panel.blockAtEnd": "已在最后一个差异块,再按一次即可跳到第一个",
|
|
16749
|
+
"panel.blockAtStart": "已在第一个差异块,再按一次即可跳到最后一个",
|
|
16750
|
+
"panel.blockSingle": "仅有一个差异块",
|
|
16751
|
+
"panel.viewDiff": "查看差异",
|
|
16752
|
+
"panel.fileNotPending": "该文件不在待处理差异列表中",
|
|
15961
16753
|
"panel.selectHint": "点击每项查看整个文件的差异;选中文本后,用底部状态栏或 Ctrl+L 复制引用",
|
|
15962
16754
|
"panel.searchPlaceholder": "搜索",
|
|
15963
16755
|
"panel.missing": "文件已不存在",
|
|
@@ -15977,6 +16769,18 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
15977
16769
|
"panel.quickSummon": "快速呼出",
|
|
15978
16770
|
"panel.quickSummonDesc": "用键盘快捷键打开/关闭差异面板。点击右侧按钮后按下新的按键组合即可修改。",
|
|
15979
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": "上一个待处理文件",
|
|
15980
16784
|
"settings.tabLabel": "改动审批",
|
|
15981
16785
|
"row.create": "新增文件",
|
|
15982
16786
|
"row.failed": "失败",
|
|
@@ -15995,6 +16799,8 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
15995
16799
|
"action.nextDiff": "下一处差异",
|
|
15996
16800
|
"action.showFileList": "展开文件列表",
|
|
15997
16801
|
"action.hideFileList": "收起文件列表",
|
|
16802
|
+
"action.viewSplit": "切换到双栏",
|
|
16803
|
+
"action.viewUnified": "切换到单栏",
|
|
15998
16804
|
"action.copyHint": "复制引用",
|
|
15999
16805
|
"action.copied": "已复制",
|
|
16000
16806
|
"action.langAuto": "自动",
|
|
@@ -16033,6 +16839,11 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
16033
16839
|
"panel.aria": "Pending changes",
|
|
16034
16840
|
"panel.stats": "+{added} -{removed}",
|
|
16035
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",
|
|
16036
16847
|
"panel.selectHint": "Select an item to review its whole-file diff; select text and copy its reference from the status bar (Ctrl+L)",
|
|
16037
16848
|
"panel.searchPlaceholder": "Search",
|
|
16038
16849
|
"panel.missing": "File is gone",
|
|
@@ -16052,6 +16863,18 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
16052
16863
|
"panel.quickSummon": "Quick summon",
|
|
16053
16864
|
"panel.quickSummonDesc": "Open or close the diff panel with a keyboard shortcut. Click the button and press a new chord to change it.",
|
|
16054
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",
|
|
16055
16878
|
"settings.tabLabel": "Diff Approval",
|
|
16056
16879
|
"row.create": "New file",
|
|
16057
16880
|
"row.failed": "Failed",
|
|
@@ -16070,6 +16893,8 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
16070
16893
|
"action.nextDiff": "Next diff",
|
|
16071
16894
|
"action.showFileList": "Show file list",
|
|
16072
16895
|
"action.hideFileList": "Hide file list",
|
|
16896
|
+
"action.viewSplit": "Switch to side-by-side",
|
|
16897
|
+
"action.viewUnified": "Switch to unified",
|
|
16073
16898
|
"action.copyHint": "Copy reference",
|
|
16074
16899
|
"action.copied": "Copied",
|
|
16075
16900
|
"action.langAuto": "Auto",
|
|
@@ -16229,18 +17054,13 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
16229
17054
|
onAckRedoCleared: () => store.clearRedoCleared(),
|
|
16230
17055
|
onAckJustResolved: () => store.clearJustResolved(),
|
|
16231
17056
|
onPasteReference: (sessionId, reference) => {
|
|
16232
|
-
|
|
16233
|
-
|
|
16234
|
-
const conversation = actx.get("conversation");
|
|
16235
|
-
if (conversation?.input === void 0) return;
|
|
16236
|
-
const textarea = document.querySelector("[data-composer-card] textarea");
|
|
16237
|
-
const base = textarea?.value ?? "";
|
|
16238
|
-
conversation.input.for(actx).setDraft(base === "" ? reference : `${base} ${reference}`);
|
|
16239
|
-
textarea?.focus();
|
|
17057
|
+
conversationAccess(ctx, () => sessionId).appendDraft(reference);
|
|
17058
|
+
document.querySelector("[data-composer-input]")?.focus();
|
|
16240
17059
|
},
|
|
16241
17060
|
collapseSidebar
|
|
16242
17061
|
})
|
|
16243
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");
|
|
16244
17064
|
ctx.slots.inject("settings.section", () => ctx.slots.register({
|
|
16245
17065
|
name: "settings.section",
|
|
16246
17066
|
id: "diff-approval",
|