dsh-diff-approval 0.5.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/client.js CHANGED
@@ -11582,30 +11582,21 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
11582
11582
  //#endregion
11583
11583
  //#region lib/types/client/reference.js
11584
11584
  /**
11585
- * Reference labels for the selection toolbar: a file name plus a 1-based line
11586
- * range, using the short name when unambiguous and the full path otherwise.
11587
- * Pure derivation so the display rule is unit-testable without the panel.
11588
- * @module dsh-diff-approval/client/reference
11589
- */
11590
- /**
11591
- * Last path segment of a file path, any separator style.
11592
- * @param path - the path to shorten.
11593
- * @returns the segment after the final separator.
11594
- */
11595
- function basenameOf$1(path) {
11596
- const normalized = path.replaceAll("\\", "/");
11597
- return normalized.slice(normalized.lastIndexOf("/") + 1);
11598
- }
11599
- /**
11600
- * The path shown in a copied reference: the short name when no other listed
11601
- * file shares it, the full path otherwise.
11585
+ * The path embedded in a copied reference: workspace-relative (forward
11586
+ * slashes) when the file lives inside the current workspace, the absolute
11587
+ * path otherwise. A bare file name is never enough a reference must resolve
11588
+ * to exactly one file.
11602
11589
  * @param path - the selected file's path.
11603
- * @param files - every currently listed pending file.
11604
- * @returns the display path for a copied reference.
11590
+ * @param workspacePath - the current workspace root, or `undefined`.
11591
+ * @returns the reference path.
11605
11592
  */
11606
- function copyDisplayPath(path, files) {
11607
- const base = basenameOf$1(path);
11608
- return files.some((file) => file.path !== path && basenameOf$1(file.path) === base) ? path : base;
11593
+ function referencePathOf(path, workspacePath) {
11594
+ if (workspacePath === void 0 || workspacePath.length === 0) return path;
11595
+ const normalized = path.replaceAll("\\", "/");
11596
+ const root = workspacePath.replaceAll("\\", "/");
11597
+ const prefix = root.endsWith("/") ? root : `${root}/`;
11598
+ if (normalized.length > prefix.length && normalized.slice(0, prefix.length).toLowerCase() === prefix.toLowerCase()) return normalized.slice(prefix.length);
11599
+ return path;
11609
11600
  }
11610
11601
  /**
11611
11602
  * Format one line range as a reference suffix: a single line number, or the
@@ -11620,17 +11611,17 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
11620
11611
  /**
11621
11612
  * Build the clipboard text for a selected line range.
11622
11613
  * @param path - the selected file's path.
11623
- * @param files - every currently listed pending file.
11614
+ * @param workspacePath - the current workspace root, or `undefined`.
11624
11615
  * @param start - first selected line number.
11625
11616
  * @param end - last selected line number.
11626
11617
  * @returns the `path:range` reference text.
11627
11618
  */
11628
- function referenceOf(path, files, start, end) {
11629
- return `${copyDisplayPath(path, files)}:${lineRangeLabel(start, end)}`;
11619
+ function referenceOf(path, workspacePath, start, end) {
11620
+ return `${referencePathOf(path, workspacePath)}:${lineRangeLabel(start, end)}`;
11630
11621
  }
11631
11622
  //#endregion
11632
11623
  //#region \0dsh-css:/home/runner/work/dsh-diff-approval/dsh-diff-approval/src/client/PendingPanel.module.css.mjs
11633
- const css = ".F1KBNa_layer{box-sizing:border-box;flex:none;align-items:center;width:calc(100% + 8px);height:42px;margin:4px -4px;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_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_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_states{flex:1;min-height:0;padding:4px 12px 12px;overflow-y:auto}.F1KBNa_split{flex:1;align-items:stretch;min-height:0;display:flex}.F1KBNa_fileList{box-sizing:border-box;border-right:1px solid var(--dsw-alias-border-l2);flex:none;width:240px;min-height:0;padding:4px 8px 12px;overflow-y:auto}.F1KBNa_resizeHandle{cursor:col-resize;background:0 0;flex:none;width:5px;margin:0 -2px}.F1KBNa_resizeHandle:hover{background:var(--dsw-alias-border-l2)}.F1KBNa_detail{flex-direction:column;flex:1;min-width:0;min-height:0;display:flex}.F1KBNa_detailEmpty{color:var(--dsw-alias-label-tertiary);text-align:center;flex:1;justify-content:center;align-items:center;margin:0;padding:24px;font-size:12px;line-height:18px;display:flex}.F1KBNa_title{color:var(--dsw-alias-label-primary);font-size:13px;font-weight:500;line-height:20px}.F1KBNa_note,.F1KBNa_readError,.F1KBNa_hint{color:var(--dsw-alias-label-tertiary);margin:4px 0;font-size:12px;line-height:18px}.F1KBNa_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_missingHint{color:var(--dsw-alias-state-warn-label,var(--dsw-alias-state-warn-primary))}.F1KBNa_rowMeta{color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;flex:none;gap:6px;margin-left:auto;font-size:12px;line-height:16px;display:inline-flex}.F1KBNa_addCount{color:color-mix(in srgb, var(--dsw-alias-state-success-primary) 60%, var(--dsw-alias-label-primary))}.F1KBNa_delCount{color:color-mix(in srgb, var(--dsw-alias-state-error-primary) 60%, var(--dsw-alias-label-primary))}.F1KBNa_diff{background:var(--dsw-alias-markdown-code-block);flex-direction:column;flex:1;min-height:0;display:flex;position:relative;overflow:hidden}.F1KBNa_diffHeader{border-bottom:1px solid var(--dsw-alias-border-l2);align-items:center;gap:8px;padding:6px 8px;display:flex}.F1KBNa_diffPath{text-overflow:ellipsis;white-space:nowrap;min-width:0;color:var(--dsw-alias-label-primary);font:var(--dsw-font-markdown-code-block);flex:1;font-size:12px;line-height:18px;overflow:hidden}.F1KBNa_diffActions{border-bottom:1px solid var(--dsw-alias-border-l2);align-items:center;gap:8px;padding:6px 8px;display:flex}.F1KBNa_diffStats{color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;font-size:12px;line-height:16px}.F1KBNa_flexSpacer{flex:1}.F1KBNa_action{border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-primary);cursor:pointer;background:0 0;border-radius:8px;padding:3px 12px;font-family:inherit;font-size:12px;line-height:18px}.F1KBNa_action:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.F1KBNa_action:disabled{color:var(--dsw-alias-label-tertiary);cursor:default}.F1KBNa_iconAction{justify-content:center;align-items:center;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);flex:1;padding:8px 10px;overflow:auto}.F1KBNa_overviewRuler{pointer-events:none;opacity:.5;width:8px;position:absolute;top:0;bottom:0;right:0}.F1KBNa_overviewMarker{border-radius:2px;width:100%;min-height:2px;position:absolute;right:0}.F1KBNa_markerDel{background-color:var(--dsw-alias-state-error-primary)}.F1KBNa_markerAdd{background-color:var(--dsw-alias-state-success-primary)}.F1KBNa_lines{border-spacing:0;width:max-content;min-width:100%;display:table}.F1KBNa_line{height:22px;line-height:22px;display:table-row}.F1KBNa_vSpacer{display:table-row}.F1KBNa_gutter{box-sizing:border-box;text-align:right;width:44px;color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;user-select:none;padding-right:10px;display:table-cell}.F1KBNa_code{white-space:pre;display:table-cell}.F1KBNa_context{color:var(--dsw-alias-label-primary)}.F1KBNa_del{background-color:color-mix(in srgb, var(--dsw-alias-state-error-primary) 12%, transparent)}.F1KBNa_add{background-color:color-mix(in srgb, var(--dsw-alias-state-success-primary) 10%, transparent)}.F1KBNa_statusBar{box-sizing:border-box;border-top:1px solid var(--dsw-alias-border-l2);height:34px;color:var(--dsw-alias-label-tertiary);font-family:var(--dsw-font-markdown-code-block);font-variant-numeric:tabular-nums;flex:none;align-items:center;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}";
11624
+ const css = ".F1KBNa_layer{box-sizing:border-box;flex:none;align-items:center;width:calc(100% + 8px);height:42px;margin:4px -4px;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_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_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_states{flex:1;min-height:0;padding:4px 12px 12px;overflow-y:auto}.F1KBNa_split{flex:1;align-items:stretch;min-height:0;display:flex}.F1KBNa_fileList{box-sizing:border-box;border-right:1px solid var(--dsw-alias-border-l2);flex:none;width:240px;min-height:0;padding:4px 8px 12px;overflow-y:auto}.F1KBNa_resizeHandle{cursor:col-resize;background:0 0;flex:none;width:5px;margin:0 -2px}.F1KBNa_resizeHandle:hover{background:var(--dsw-alias-border-l2)}.F1KBNa_detail{flex-direction:column;flex:1;min-width:0;min-height:0;display:flex}.F1KBNa_detailEmpty{color:var(--dsw-alias-label-tertiary);text-align:center;flex:1;justify-content:center;align-items:center;margin:0;padding:24px;font-size:12px;line-height:18px;display:flex}.F1KBNa_title{color:var(--dsw-alias-label-primary);font-size:13px;font-weight:500;line-height:20px}.F1KBNa_note,.F1KBNa_readError,.F1KBNa_hint{color:var(--dsw-alias-label-tertiary);margin:4px 0;font-size:12px;line-height:18px}.F1KBNa_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_missingHint{color:var(--dsw-alias-state-warn-label,var(--dsw-alias-state-warn-primary))}.F1KBNa_rowMeta{color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;flex:none;gap:6px;margin-left:auto;font-size:12px;line-height:16px;display:inline-flex}.F1KBNa_addCount{color:color-mix(in srgb, var(--dsw-alias-state-success-primary) 60%, var(--dsw-alias-label-primary))}.F1KBNa_delCount{color:color-mix(in srgb, var(--dsw-alias-state-error-primary) 60%, var(--dsw-alias-label-primary))}.F1KBNa_diff{background:var(--dsw-alias-markdown-code-block);flex-direction:column;flex:1;min-height:0;display:flex;position:relative;overflow:hidden}.F1KBNa_diffHeader{border-bottom:1px solid var(--dsw-alias-border-l2);align-items:center;gap:8px;padding:6px 8px;display:flex}.F1KBNa_diffPath{text-overflow:ellipsis;white-space:nowrap;min-width:0;color:var(--dsw-alias-label-primary);font:var(--dsw-font-markdown-code-block);flex:1;font-size:12px;line-height:18px;overflow:hidden}.F1KBNa_diffActions{border-bottom:1px solid var(--dsw-alias-border-l2);align-items:center;gap:8px;padding:6px 8px;display:flex}.F1KBNa_diffStats{color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;font-size:12px;line-height:16px}.F1KBNa_flexSpacer{flex:1}.F1KBNa_action{border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-primary);cursor:pointer;background:0 0;border-radius:8px;padding:3px 12px;font-family:inherit;font-size:12px;line-height:18px}.F1KBNa_action:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.F1KBNa_action:disabled{color:var(--dsw-alias-label-tertiary);cursor:default}.F1KBNa_actionPrimary{background:var(--dsw-alias-state-business-primary);border-color:var(--dsw-alias-state-business-primary);color:var(--dsw-static-neutral-bluish-50)}.F1KBNa_actionPrimary:hover:not(:disabled){background:color-mix(in srgb, var(--dsw-alias-state-business-primary) 85%, var(--dsw-static-neutral-bluish-1000))}.F1KBNa_actionPrimary:disabled{background:var(--dsw-alias-state-business-primary);color:var(--dsw-static-neutral-bluish-50);opacity:.55}.F1KBNa_iconAction{justify-content:center;align-items:center;min-height:25px;padding:4px 6px;display:inline-flex}.F1KBNa_close{cursor:pointer;width:28px;height:28px;color:var(--dsw-alias-label-tertiary);background:0 0;border:none;border-radius:28px;justify-content:center;align-items:center;padding:0;display:inline-flex}.F1KBNa_close:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}.F1KBNa_expand{cursor:pointer;width:28px;height:28px;color:var(--dsw-alias-label-tertiary);background:0 0;border:none;border-radius:28px;justify-content:center;align-items:center;padding:0;display:inline-flex}.F1KBNa_expand:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}.F1KBNa_expandExpanded svg{transform:rotate(180deg)}.F1KBNa_diffBodyWrap{flex:1;min-height:0;display:flex;position:relative}.F1KBNa_diffBody{min-width:0;font:var(--dsw-font-markdown-code-block);cursor:text;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_overviewRuler{pointer-events:none;opacity:.5;width:4px;position:absolute;top:0;bottom:0;right:0}.F1KBNa_overviewMarker{border-radius:2px;width:100%;min-height:2px;position:absolute;right:0}.F1KBNa_markerDel{background-color:var(--dsw-alias-state-error-primary)}.F1KBNa_markerAdd{background-color:var(--dsw-alias-state-success-primary)}.F1KBNa_lines{border-spacing:0;width:max-content;min-width:100%;display:table}.F1KBNa_line{height:22px;line-height:22px;display:table-row}.F1KBNa_vSpacer{display:table-row}.F1KBNa_gutter{box-sizing:border-box;text-align:right;width:44px;color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;user-select:none;cursor:default;padding-right:10px;display:table-cell}.F1KBNa_code{white-space:pre;display:table-cell}.F1KBNa_context{color:var(--dsw-alias-label-primary)}.F1KBNa_del{background-color:color-mix(in srgb, var(--dsw-alias-state-error-primary) 12%, transparent)}.F1KBNa_add{background-color:color-mix(in srgb, var(--dsw-alias-state-success-primary) 10%, transparent)}.F1KBNa_statusBar{box-sizing:border-box;border-top:1px solid var(--dsw-alias-border-l2);height:34px;color:var(--dsw-alias-label-tertiary);font-family:var(--dsw-font-markdown-code-block);font-variant-numeric:tabular-nums;flex:none;align-items:center;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}";
11634
11625
  const tagId = "dsh-diff-approval/PendingPanel.module.css";
11635
11626
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
11636
11627
  const tag = document.createElement("style");
@@ -11640,67 +11631,73 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
11640
11631
  document.head.appendChild(tag);
11641
11632
  }
11642
11633
  var PendingPanel_module_css_default = {
11643
- "code": "F1KBNa_code",
11644
- "diffBodyWrap": "F1KBNa_diffBodyWrap",
11645
- "badge": "F1KBNa_badge",
11646
- "readError": "F1KBNa_readError",
11647
- "diff": "F1KBNa_diff",
11634
+ "gutter": "F1KBNa_gutter",
11635
+ "missingHint": "F1KBNa_missingHint",
11636
+ "searchCount": "F1KBNa_searchCount",
11637
+ "hint": "F1KBNa_hint",
11638
+ "layer": "F1KBNa_layer",
11639
+ "footerButtons": "F1KBNa_footerButtons",
11640
+ "langSelect": "F1KBNa_langSelect",
11641
+ "addCount": "F1KBNa_addCount",
11642
+ "rowPath": "F1KBNa_rowPath",
11643
+ "context": "F1KBNa_context",
11644
+ "kindTag": "F1KBNa_kindTag",
11645
+ "states": "F1KBNa_states",
11646
+ "resizeHandle": "F1KBNa_resizeHandle",
11647
+ "diffStats": "F1KBNa_diffStats",
11648
+ "del": "F1KBNa_del",
11649
+ "searchInput": "F1KBNa_searchInput",
11650
+ "expand": "F1KBNa_expand",
11651
+ "searchBar": "F1KBNa_searchBar",
11648
11652
  "markerDel": "F1KBNa_markerDel",
11649
- "group": "F1KBNa_group",
11653
+ "title": "F1KBNa_title",
11654
+ "vSpacer": "F1KBNa_vSpacer",
11655
+ "badgeCount": "F1KBNa_badgeCount",
11656
+ "kindHint": "F1KBNa_kindHint",
11657
+ "rail": "F1KBNa_rail",
11658
+ "expandExpanded": "F1KBNa_expandExpanded",
11659
+ "action": "F1KBNa_action",
11660
+ "diffHeader": "F1KBNa_diffHeader",
11661
+ "fileList": "F1KBNa_fileList",
11662
+ "diffActions": "F1KBNa_diffActions",
11663
+ "readError": "F1KBNa_readError",
11664
+ "note": "F1KBNa_note",
11665
+ "line": "F1KBNa_line",
11650
11666
  "divergedHint": "F1KBNa_divergedHint",
11651
- "detailEmpty": "F1KBNa_detailEmpty",
11667
+ "group": "F1KBNa_group",
11668
+ "diff": "F1KBNa_diff",
11669
+ "code": "F1KBNa_code",
11670
+ "headerActions": "F1KBNa_headerActions",
11671
+ "detail": "F1KBNa_detail",
11672
+ "missing": "F1KBNa_missing",
11673
+ "add": "F1KBNa_add",
11674
+ "badgeLabel": "F1KBNa_badgeLabel",
11652
11675
  "iconAction": "F1KBNa_iconAction",
11676
+ "markerAdd": "F1KBNa_markerAdd",
11677
+ "row": "F1KBNa_row",
11653
11678
  "diffBody": "F1KBNa_diffBody",
11654
- "rowPath": "F1KBNa_rowPath",
11655
- "addCount": "F1KBNa_addCount",
11656
- "vSpacer": "F1KBNa_vSpacer",
11657
- "add": "F1KBNa_add",
11658
- "overviewMarker": "F1KBNa_overviewMarker",
11679
+ "langLabel": "F1KBNa_langLabel",
11680
+ "lines": "F1KBNa_lines",
11659
11681
  "panel": "F1KBNa_panel",
11660
- "del": "F1KBNa_del",
11661
- "row": "F1KBNa_row",
11662
- "kindHint": "F1KBNa_kindHint",
11663
- "layer": "F1KBNa_layer",
11682
+ "split": "F1KBNa_split",
11683
+ "badge": "F1KBNa_badge",
11684
+ "delCount": "F1KBNa_delCount",
11685
+ "overviewRuler": "F1KBNa_overviewRuler",
11664
11686
  "rowMeta": "F1KBNa_rowMeta",
11665
- "resizeHandle": "F1KBNa_resizeHandle",
11666
- "footerButtons": "F1KBNa_footerButtons",
11667
- "rowHead": "F1KBNa_rowHead",
11687
+ "blockActions": "F1KBNa_blockActions",
11688
+ "rows": "F1KBNa_rows",
11689
+ "diffBodyWrap": "F1KBNa_diffBodyWrap",
11690
+ "detailEmpty": "F1KBNa_detailEmpty",
11668
11691
  "close": "F1KBNa_close",
11669
- "title": "F1KBNa_title",
11670
- "overviewRuler": "F1KBNa_overviewRuler",
11671
- "lines": "F1KBNa_lines",
11672
- "missingHint": "F1KBNa_missingHint",
11673
- "diffPath": "F1KBNa_diffPath",
11674
- "detail": "F1KBNa_detail",
11675
- "states": "F1KBNa_states",
11676
- "headerActions": "F1KBNa_headerActions",
11677
- "fileList": "F1KBNa_fileList",
11678
- "langSelect": "F1KBNa_langSelect",
11679
- "expand": "F1KBNa_expand",
11680
- "langLabel": "F1KBNa_langLabel",
11692
+ "statusBar": "F1KBNa_statusBar",
11693
+ "actionPrimary": "F1KBNa_actionPrimary",
11694
+ "rowHead": "F1KBNa_rowHead",
11681
11695
  "statusAction": "F1KBNa_statusAction",
11682
- "badgeLabel": "F1KBNa_badgeLabel",
11683
- "diffActions": "F1KBNa_diffActions",
11696
+ "diffPath": "F1KBNa_diffPath",
11684
11697
  "flexSpacer": "F1KBNa_flexSpacer",
11685
- "kindTag": "F1KBNa_kindTag",
11686
- "gutter": "F1KBNa_gutter",
11687
- "markerAdd": "F1KBNa_markerAdd",
11688
- "rows": "F1KBNa_rows",
11689
- "delCount": "F1KBNa_delCount",
11690
- "context": "F1KBNa_context",
11691
- "action": "F1KBNa_action",
11692
- "statusBar": "F1KBNa_statusBar",
11693
- "badgeCount": "F1KBNa_badgeCount",
11694
- "diffHeader": "F1KBNa_diffHeader",
11695
- "diffStats": "F1KBNa_diffStats",
11696
- "split": "F1KBNa_split",
11697
- "line": "F1KBNa_line",
11698
- "expandExpanded": "F1KBNa_expandExpanded",
11699
- "rail": "F1KBNa_rail",
11700
- "note": "F1KBNa_note",
11701
- "missing": "F1KBNa_missing",
11698
+ "blockPosition": "F1KBNa_blockPosition",
11702
11699
  "header": "F1KBNa_header",
11703
- "hint": "F1KBNa_hint"
11700
+ "overviewMarker": "F1KBNa_overviewMarker"
11704
11701
  };
11705
11702
  //#endregion
11706
11703
  //#region lib/types/client/PendingPanel.js
@@ -11736,6 +11733,10 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
11736
11733
  const ROW_HEIGHT_PX = 22;
11737
11734
  /** Rows rendered beyond the visible window in each direction. */
11738
11735
  const OVERSCAN_ROWS = 8;
11736
+ /** Height of the floating per-block Keep/Revert frame in px: 26px actions +
11737
+ * 5px frame padding on each side + 1px border on each side, plus a little
11738
+ * breathing room so the bottom padding never sits flush against it. */
11739
+ const BLOCK_ACTIONS_FRAME_PX = 40;
11739
11740
  /** The trailing file-name segment of a path, used for row display. */
11740
11741
  function basenameOf(path) {
11741
11742
  const index = Math.max(path.lastIndexOf("/"), path.lastIndexOf("\\"));
@@ -11752,7 +11753,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
11752
11753
  * does not re-render rows whose content, highlight, and focus are unchanged.
11753
11754
  */
11754
11755
  const DiffRow = (0, react.memo)(function DiffRow(props) {
11755
- const { index, row, runs, focused } = props;
11756
+ const { index, row, runs, focused, searchHit, searchCurrent, onRowHover } = props;
11756
11757
  const lineNumber = row.kind === "del" ? row.oldLine : row.newLine;
11757
11758
  const sideRuns = row.kind === "del" ? runs?.oldRuns : runs?.newRuns;
11758
11759
  const lineRuns = lineNumber === void 0 ? void 0 : sideRuns?.[lineNumber - 1];
@@ -11765,6 +11766,10 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
11765
11766
  "data-diff-line": row.kind,
11766
11767
  "data-diff-row": index,
11767
11768
  "data-diff-focused": focused ? "" : void 0,
11769
+ "data-diff-search": searchHit ? searchCurrent ? "current" : "hit" : void 0,
11770
+ onMouseEnter: () => {
11771
+ onRowHover(index);
11772
+ },
11768
11773
  children: [
11769
11774
  (0, react_jsx_runtime.jsx)("span", {
11770
11775
  className: PendingPanel_module_css_default.gutter,
@@ -11782,6 +11787,48 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
11782
11787
  ]
11783
11788
  });
11784
11789
  });
11790
+ /**
11791
+ * One diff block's old/new line ranges, 1-based inclusive, for block-level
11792
+ * keep/revert. A side with no lines (a pure addition or deletion) is empty;
11793
+ * its start is that side's insertion point — the line after the surrounding
11794
+ * context — so the host can insert there.
11795
+ * @param rows - the whole-file diff rows.
11796
+ * @param block - the block's row range.
11797
+ * @returns the old and new line ranges.
11798
+ */
11799
+ function blockRangesOf(rows, block) {
11800
+ let oldStart = Infinity;
11801
+ let oldEnd = -Infinity;
11802
+ let newStart = Infinity;
11803
+ let newEnd = -Infinity;
11804
+ for (let index = block.start; index <= block.end; index++) {
11805
+ const row = rows[index];
11806
+ if (row === void 0) continue;
11807
+ if (row.oldLine !== void 0) {
11808
+ oldStart = Math.min(oldStart, row.oldLine);
11809
+ oldEnd = Math.max(oldEnd, row.oldLine);
11810
+ }
11811
+ if (row.newLine !== void 0) {
11812
+ newStart = Math.min(newStart, row.newLine);
11813
+ newEnd = Math.max(newEnd, row.newLine);
11814
+ }
11815
+ }
11816
+ const before = rows[block.start - 1];
11817
+ if (oldStart === Infinity) {
11818
+ oldStart = (before?.oldLine ?? 0) + 1;
11819
+ oldEnd = oldStart - 1;
11820
+ }
11821
+ if (newStart === Infinity) {
11822
+ newStart = (before?.newLine ?? 0) + 1;
11823
+ newEnd = newStart - 1;
11824
+ }
11825
+ return {
11826
+ oldStart,
11827
+ oldEnd,
11828
+ newStart,
11829
+ newEnd
11830
+ };
11831
+ }
11785
11832
  /** Split a row list into maximal runs of non-context rows. */
11786
11833
  function changeBlocksOf(diff) {
11787
11834
  const blocks = [];
@@ -11866,48 +11913,48 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
11866
11913
  const stats = (0, react.useMemo)(() => computeWholeFileDiff(file.oldText, file.newText), [file.oldText, file.newText]);
11867
11914
  return (0, react_jsx_runtime.jsx)("li", {
11868
11915
  className: PendingPanel_module_css_default.row,
11869
- children: (0, react_jsx_runtime.jsxs)("button", {
11870
- type: "button",
11871
- className: PendingPanel_module_css_default.rowHead,
11872
- "data-selected": selected || void 0,
11873
- onClick: () => {
11874
- onSelect(file.id);
11875
- },
11876
- children: [
11877
- (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
11878
- label: file.path,
11879
- delayMs: 500,
11880
- maxWidth: 560,
11881
- children: (0, react_jsx_runtime.jsx)("span", {
11916
+ children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
11917
+ label: file.path,
11918
+ delayMs: 500,
11919
+ maxWidth: 560,
11920
+ children: (0, react_jsx_runtime.jsxs)("button", {
11921
+ type: "button",
11922
+ className: PendingPanel_module_css_default.rowHead,
11923
+ "data-selected": selected || void 0,
11924
+ onClick: () => {
11925
+ onSelect(file.id);
11926
+ },
11927
+ children: [
11928
+ (0, react_jsx_runtime.jsx)("span", {
11882
11929
  className: PendingPanel_module_css_default.rowPath,
11883
11930
  children: basenameOf(file.path)
11931
+ }),
11932
+ file.kind === "create" && (0, react_jsx_runtime.jsx)("span", {
11933
+ className: PendingPanel_module_css_default.kindTag,
11934
+ children: t("row.create")
11935
+ }),
11936
+ file.missing && (0, react_jsx_runtime.jsx)("span", {
11937
+ className: PendingPanel_module_css_default.missing,
11938
+ title: t("panel.missingHint"),
11939
+ children: t("panel.missing")
11940
+ }),
11941
+ (0, react_jsx_runtime.jsxs)("span", {
11942
+ className: PendingPanel_module_css_default.rowMeta,
11943
+ children: [(0, react_jsx_runtime.jsx)("span", {
11944
+ className: PendingPanel_module_css_default.addCount,
11945
+ children: t("row.added", { added: stats.added })
11946
+ }), (0, react_jsx_runtime.jsx)("span", {
11947
+ className: PendingPanel_module_css_default.delCount,
11948
+ children: t("row.removed", { removed: stats.removed })
11949
+ })]
11884
11950
  })
11885
- }),
11886
- file.kind === "create" && (0, react_jsx_runtime.jsx)("span", {
11887
- className: PendingPanel_module_css_default.kindTag,
11888
- children: t("row.create")
11889
- }),
11890
- file.missing && (0, react_jsx_runtime.jsx)("span", {
11891
- className: PendingPanel_module_css_default.missing,
11892
- title: t("panel.missingHint"),
11893
- children: t("panel.missing")
11894
- }),
11895
- (0, react_jsx_runtime.jsxs)("span", {
11896
- className: PendingPanel_module_css_default.rowMeta,
11897
- children: [(0, react_jsx_runtime.jsx)("span", {
11898
- className: PendingPanel_module_css_default.addCount,
11899
- children: t("row.added", { added: stats.added })
11900
- }), (0, react_jsx_runtime.jsx)("span", {
11901
- className: PendingPanel_module_css_default.delCount,
11902
- children: t("row.removed", { removed: stats.removed })
11903
- })]
11904
- })
11905
- ]
11951
+ ]
11952
+ })
11906
11953
  })
11907
11954
  });
11908
11955
  }
11909
11956
  /** The selected file's diff, actions, jump controls, and copy toolbar. */
11910
- function PendingDiff({ file, files, busy, t, onKeep, onRevert, onOpen }) {
11957
+ function PendingDiff({ file, busy, workspacePath, jumpSignal, t, onKeep, onRevert, onBlockKeep, onBlockRevert, onOpen }) {
11911
11958
  const [langOverride, setLangOverride] = (0, react.useState)(void 0);
11912
11959
  const [langMenuOpen, setLangMenuOpen] = (0, react.useState)(false);
11913
11960
  const langMenuItems = (0, react.useMemo)(() => [{
@@ -11985,23 +12032,88 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
11985
12032
  const [scrollTick, setScrollTick] = (0, react.useState)(0);
11986
12033
  const [scrollTop, setScrollTop] = (0, react.useState)(0);
11987
12034
  const [viewportHeight, setViewportHeight] = (0, react.useState)(0);
12035
+ const [hoveredBlock, setHoveredBlock] = (0, react.useState)(void 0);
11988
12036
  const [selection, setSelection] = (0, react.useState)(void 0);
11989
12037
  const [copied, setCopied] = (0, react.useState)(false);
12038
+ const [searchOpen, setSearchOpen] = (0, react.useState)(false);
12039
+ const [searchQuery, setSearchQuery] = (0, react.useState)("");
12040
+ const [searchIndex, setSearchIndex] = (0, react.useState)(0);
12041
+ const searchInputRef = (0, react.useRef)(null);
11990
12042
  (0, react.useEffect)(() => {
11991
12043
  setFocus(0);
11992
12044
  setScrollTop(0);
11993
12045
  if (bodyRef.current !== null) bodyRef.current.scrollTop = 0;
12046
+ setHoveredBlock(void 0);
11994
12047
  setSelection(void 0);
11995
12048
  setLangOverride(void 0);
11996
12049
  setLangMenuOpen(false);
11997
12050
  setCopied(false);
12051
+ setSearchOpen(false);
12052
+ setSearchQuery("");
12053
+ setSearchIndex(0);
11998
12054
  }, [file.id]);
12055
+ const blockRanges = (0, react.useMemo)(() => {
12056
+ return model.blocks.map((block) => blockRangesOf(model.diff.rows, block));
12057
+ }, [model]);
12058
+ const searchMatches = (0, react.useMemo)(() => {
12059
+ if (searchQuery === "") return [];
12060
+ const lower = searchQuery.toLowerCase();
12061
+ const matches = [];
12062
+ for (let index = 0; index < model.diff.rows.length; index++) if (model.diff.rows[index].text.toLowerCase().includes(lower)) matches.push(index);
12063
+ return matches;
12064
+ }, [model, searchQuery]);
12065
+ const searchHitSet = (0, react.useMemo)(() => new Set(searchMatches), [searchMatches]);
12066
+ const currentSearchRow = searchMatches.length === 0 ? void 0 : searchMatches[searchIndex % searchMatches.length];
12067
+ const goSearch = (direction) => {
12068
+ if (searchMatches.length === 0) return;
12069
+ setSearchIndex((current) => (current + direction + searchMatches.length) % searchMatches.length);
12070
+ };
12071
+ const toggleSearch = () => {
12072
+ if (searchOpen) {
12073
+ setSearchOpen(false);
12074
+ setSearchQuery("");
12075
+ setSearchIndex(0);
12076
+ } else setSearchOpen(true);
12077
+ };
12078
+ const closeSearch = () => {
12079
+ setSearchOpen(false);
12080
+ setSearchQuery("");
12081
+ setSearchIndex(0);
12082
+ };
12083
+ (0, react.useEffect)(() => {
12084
+ if (searchOpen) searchInputRef.current?.focus();
12085
+ }, [searchOpen]);
12086
+ (0, react.useEffect)(() => {
12087
+ setSearchIndex(0);
12088
+ }, [searchQuery, file.id]);
12089
+ (0, react.useEffect)(() => {
12090
+ const row = currentSearchRow;
12091
+ if (row === void 0) return;
12092
+ const body = bodyRef.current;
12093
+ if (body === null) return;
12094
+ const target = row * ROW_HEIGHT_PX + ROW_HEIGHT_PX / 2 - body.clientHeight / 2;
12095
+ const clamped = Math.max(0, Math.min(target, body.scrollHeight - body.clientHeight));
12096
+ if (body.scrollTop !== clamped) body.scrollTop = clamped;
12097
+ setScrollTop(clamped);
12098
+ }, [currentSearchRow, searchMatches]);
12099
+ const blockIndexByRow = (0, react.useMemo)(() => {
12100
+ const map = /* @__PURE__ */ new Map();
12101
+ model.blocks.forEach((block, blockIndex) => {
12102
+ for (let index = block.start; index <= block.end; index++) map.set(index, blockIndex);
12103
+ });
12104
+ return map;
12105
+ }, [model]);
12106
+ const onRowHover = (0, react.useCallback)((index) => {
12107
+ setHoveredBlock(blockIndexByRow.get(index));
12108
+ }, [blockIndexByRow]);
11999
12109
  const rows = model.diff.rows;
12000
12110
  const rowCount = rows.length;
12001
12111
  const viewport = viewportHeight > 0 ? viewportHeight : rowCount * ROW_HEIGHT_PX;
12002
12112
  const start = Math.max(0, Math.floor(scrollTop / ROW_HEIGHT_PX) - OVERSCAN_ROWS);
12003
12113
  const end = Math.min(rowCount, Math.ceil((scrollTop + viewport) / ROW_HEIGHT_PX) + OVERSCAN_ROWS);
12004
12114
  const visibleRows = rows.slice(start, end);
12115
+ const hoveredBlockEnd = hoveredBlock !== void 0 ? model.blocks[hoveredBlock]?.end : void 0;
12116
+ const blockActionsPadPx = hoveredBlockEnd === void 0 ? 0 : Math.max(0, (hoveredBlockEnd + 1) * ROW_HEIGHT_PX + BLOCK_ACTIONS_FRAME_PX - rowCount * ROW_HEIGHT_PX);
12005
12117
  const widestLine = (0, react.useMemo)(() => {
12006
12118
  let widest = 0;
12007
12119
  for (const row of model.diff.rows) if (row.text.length > widest) widest = row.text.length;
@@ -12050,6 +12162,10 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
12050
12162
  });
12051
12163
  setScrollTick((tick) => tick + 1);
12052
12164
  };
12165
+ (0, react.useEffect)(() => {
12166
+ if (jumpSignal === 0) return;
12167
+ jump(1);
12168
+ }, [jumpSignal]);
12053
12169
  const onScroll = () => {
12054
12170
  const body = bodyRef.current;
12055
12171
  if (body === null) return;
@@ -12066,9 +12182,9 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
12066
12182
  }, [file.id]);
12067
12183
  const selectionReference = (() => {
12068
12184
  if (selection === void 0) return void 0;
12069
- const lineNumbers = model.diff.rows.slice(selection.start, selection.end + 1).map((row) => row.newLine ?? row.oldLine).filter((number) => number !== void 0);
12185
+ const lineNumbers = model.diff.rows.slice(selection.start, selection.end + 1).map((row) => row.newLine).filter((number) => number !== void 0);
12070
12186
  if (lineNumbers.length === 0) return void 0;
12071
- return referenceOf(file.path, files, Math.min(...lineNumbers), Math.max(...lineNumbers));
12187
+ return referenceOf(file.path, workspacePath, Math.min(...lineNumbers), Math.max(...lineNumbers));
12072
12188
  })();
12073
12189
  const copySelection = (0, react.useCallback)(async () => {
12074
12190
  if (selectionReference === void 0) return;
@@ -12091,6 +12207,20 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
12091
12207
  document.removeEventListener("keydown", onKeyDown);
12092
12208
  };
12093
12209
  }, [copySelection]);
12210
+ (0, react.useEffect)(() => {
12211
+ const onKeyDown = (event) => {
12212
+ if (!(event.ctrlKey || event.metaKey) || event.altKey || event.shiftKey) return;
12213
+ if (event.key.toLowerCase() !== "f") return;
12214
+ event.preventDefault();
12215
+ setSearchOpen(true);
12216
+ searchInputRef.current?.focus();
12217
+ searchInputRef.current?.select();
12218
+ };
12219
+ window.addEventListener("keydown", onKeyDown, true);
12220
+ return () => {
12221
+ window.removeEventListener("keydown", onKeyDown, true);
12222
+ };
12223
+ }, []);
12094
12224
  const focusedBlock = model.blocks.length > 0 ? model.blocks[focus] : void 0;
12095
12225
  const inFocusedBlock = (index) => focusedBlock !== void 0 && index >= focusedBlock.start && index <= focusedBlock.end;
12096
12226
  return (0, react_jsx_runtime.jsxs)("div", {
@@ -12104,27 +12234,35 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
12104
12234
  className: PendingPanel_module_css_default.diffPath,
12105
12235
  children: file.path
12106
12236
  }),
12107
- (0, react_jsx_runtime.jsx)("button", {
12108
- type: "button",
12109
- className: `${PendingPanel_module_css_default.action} ${PendingPanel_module_css_default.iconAction}`,
12110
- "data-diff-open": true,
12111
- "aria-label": t("action.openFile"),
12112
- title: t("action.openFile"),
12113
- onClick: () => {
12114
- onOpen(file.sessionId, file.id, "open");
12115
- },
12116
- children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconBrowseOutline16, { size: 14 })
12237
+ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
12238
+ label: t("action.openFile"),
12239
+ side: "bottom",
12240
+ delayMs: 500,
12241
+ children: (0, react_jsx_runtime.jsx)("button", {
12242
+ type: "button",
12243
+ className: `${PendingPanel_module_css_default.action} ${PendingPanel_module_css_default.iconAction}`,
12244
+ "data-diff-open": true,
12245
+ "aria-label": t("action.openFile"),
12246
+ onClick: () => {
12247
+ onOpen(file.sessionId, file.id, "open");
12248
+ },
12249
+ children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconBrowseOutline16, { size: 14 })
12250
+ })
12117
12251
  }),
12118
- (0, react_jsx_runtime.jsx)("button", {
12119
- type: "button",
12120
- className: `${PendingPanel_module_css_default.action} ${PendingPanel_module_css_default.iconAction}`,
12121
- "data-diff-reveal": true,
12122
- "aria-label": t("action.revealFile"),
12123
- title: t("action.revealFile"),
12124
- onClick: () => {
12125
- onOpen(file.sessionId, file.id, "reveal");
12126
- },
12127
- children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconFolderOpenOutline16, { size: 14 })
12252
+ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
12253
+ label: t("action.revealFile"),
12254
+ side: "bottom",
12255
+ delayMs: 500,
12256
+ children: (0, react_jsx_runtime.jsx)("button", {
12257
+ type: "button",
12258
+ className: `${PendingPanel_module_css_default.action} ${PendingPanel_module_css_default.iconAction}`,
12259
+ "data-diff-reveal": true,
12260
+ "aria-label": t("action.revealFile"),
12261
+ onClick: () => {
12262
+ onOpen(file.sessionId, file.id, "reveal");
12263
+ },
12264
+ children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconFolderOpenOutline16, { size: 14 })
12265
+ })
12128
12266
  })
12129
12267
  ]
12130
12268
  }),
@@ -12142,33 +12280,54 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
12142
12280
  className: PendingPanel_module_css_default.kindHint,
12143
12281
  children: t("panel.createHint")
12144
12282
  }),
12145
- model.blocks.length > 0 && (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)("button", {
12146
- type: "button",
12147
- className: `${PendingPanel_module_css_default.action} ${PendingPanel_module_css_default.iconAction}`,
12148
- "data-diff-prev": true,
12149
- "aria-label": t("action.prevDiff"),
12150
- title: t("action.prevDiff"),
12151
- disabled: busy,
12152
- onClick: () => {
12153
- jump(-1);
12154
- },
12155
- children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronUpOutline14, { size: 14 })
12156
- }), (0, react_jsx_runtime.jsx)("button", {
12157
- type: "button",
12158
- className: `${PendingPanel_module_css_default.action} ${PendingPanel_module_css_default.iconAction}`,
12159
- "data-diff-next": true,
12160
- "aria-label": t("action.nextDiff"),
12161
- title: t("action.nextDiff"),
12162
- disabled: busy,
12163
- onClick: () => {
12164
- jump(1);
12165
- },
12166
- children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, { size: 14 })
12283
+ model.blocks.length > 0 && (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
12284
+ label: t("action.prevDiff"),
12285
+ side: "bottom",
12286
+ delayMs: 500,
12287
+ children: (0, react_jsx_runtime.jsx)("button", {
12288
+ type: "button",
12289
+ className: `${PendingPanel_module_css_default.action} ${PendingPanel_module_css_default.iconAction}`,
12290
+ "data-diff-prev": true,
12291
+ "aria-label": t("action.prevDiff"),
12292
+ disabled: busy,
12293
+ onClick: () => {
12294
+ jump(-1);
12295
+ },
12296
+ children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronUpOutline14, { size: 14 })
12297
+ })
12298
+ }), (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
12299
+ label: t("action.nextDiff"),
12300
+ side: "bottom",
12301
+ delayMs: 500,
12302
+ children: (0, react_jsx_runtime.jsx)("button", {
12303
+ type: "button",
12304
+ className: `${PendingPanel_module_css_default.action} ${PendingPanel_module_css_default.iconAction}`,
12305
+ "data-diff-next": true,
12306
+ "aria-label": t("action.nextDiff"),
12307
+ disabled: busy,
12308
+ onClick: () => {
12309
+ jump(1);
12310
+ },
12311
+ children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, { size: 14 })
12312
+ })
12167
12313
  })] }),
12314
+ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
12315
+ label: t("action.search"),
12316
+ side: "bottom",
12317
+ delayMs: 500,
12318
+ children: (0, react_jsx_runtime.jsx)("button", {
12319
+ type: "button",
12320
+ className: `${PendingPanel_module_css_default.action} ${PendingPanel_module_css_default.iconAction}`,
12321
+ "data-diff-search-toggle": true,
12322
+ "aria-label": t("action.search"),
12323
+ onClick: toggleSearch,
12324
+ children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconSearchOutline16, { size: 14 })
12325
+ })
12326
+ }),
12168
12327
  (0, react_jsx_runtime.jsx)("span", { className: PendingPanel_module_css_default.flexSpacer }),
12169
12328
  (0, react_jsx_runtime.jsx)("button", {
12170
12329
  type: "button",
12171
- className: PendingPanel_module_css_default.action,
12330
+ className: `${PendingPanel_module_css_default.action} ${PendingPanel_module_css_default.actionPrimary}`,
12172
12331
  "data-diff-keep": true,
12173
12332
  disabled: busy,
12174
12333
  onClick: () => {
@@ -12194,49 +12353,154 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
12194
12353
  }),
12195
12354
  (0, react_jsx_runtime.jsxs)("div", {
12196
12355
  className: PendingPanel_module_css_default.diffBodyWrap,
12197
- children: [(0, react_jsx_runtime.jsx)("div", {
12198
- className: PendingPanel_module_css_default.diffBody,
12199
- ref: bodyRef,
12200
- onScroll,
12201
- "data-diff-body": true,
12202
- children: (0, react_jsx_runtime.jsxs)("div", {
12203
- className: PendingPanel_module_css_default.lines,
12204
- style: { minWidth: `max(100%, ${widestLine}ch)` },
12356
+ children: [
12357
+ (0, react_jsx_runtime.jsxs)("div", {
12358
+ className: PendingPanel_module_css_default.diffBody,
12359
+ ref: bodyRef,
12360
+ onScroll,
12361
+ onMouseLeave: () => {
12362
+ setHoveredBlock(void 0);
12363
+ },
12364
+ "data-diff-body": true,
12365
+ children: [(0, react_jsx_runtime.jsxs)("div", {
12366
+ className: PendingPanel_module_css_default.lines,
12367
+ style: { minWidth: `max(100%, ${widestLine}ch)` },
12368
+ children: [
12369
+ start > 0 && (0, react_jsx_runtime.jsx)("div", {
12370
+ className: PendingPanel_module_css_default.vSpacer,
12371
+ style: { height: start * ROW_HEIGHT_PX },
12372
+ "aria-hidden": "true"
12373
+ }),
12374
+ visibleRows.map((row, offset) => {
12375
+ const index = start + offset;
12376
+ return (0, react_jsx_runtime.jsx)(DiffRow, {
12377
+ index,
12378
+ row,
12379
+ runs,
12380
+ focused: inFocusedBlock(index),
12381
+ searchHit: searchHitSet.has(index),
12382
+ searchCurrent: index === currentSearchRow,
12383
+ onRowHover
12384
+ }, index);
12385
+ }),
12386
+ end < rowCount && (0, react_jsx_runtime.jsx)("div", {
12387
+ className: PendingPanel_module_css_default.vSpacer,
12388
+ style: { height: (rowCount - end) * ROW_HEIGHT_PX },
12389
+ "aria-hidden": "true"
12390
+ }),
12391
+ blockActionsPadPx > 0 && (0, react_jsx_runtime.jsx)("div", {
12392
+ className: PendingPanel_module_css_default.vSpacer,
12393
+ style: { height: blockActionsPadPx },
12394
+ "aria-hidden": "true"
12395
+ })
12396
+ ]
12397
+ }), hoveredBlock !== void 0 && model.blocks[hoveredBlock] !== void 0 && (0, react_jsx_runtime.jsxs)("div", {
12398
+ className: PendingPanel_module_css_default.blockActions,
12399
+ "data-diff-block-actions": true,
12400
+ style: { top: (model.blocks[hoveredBlock].end + 1) * ROW_HEIGHT_PX },
12401
+ children: [
12402
+ (0, react_jsx_runtime.jsx)("span", {
12403
+ className: PendingPanel_module_css_default.blockPosition,
12404
+ "data-diff-block-position": true,
12405
+ children: t("panel.blockPosition", {
12406
+ current: hoveredBlock + 1,
12407
+ total: model.blocks.length
12408
+ })
12409
+ }),
12410
+ (0, react_jsx_runtime.jsx)("button", {
12411
+ type: "button",
12412
+ className: `${PendingPanel_module_css_default.action} ${PendingPanel_module_css_default.actionPrimary}`,
12413
+ "data-diff-block-keep": true,
12414
+ disabled: busy,
12415
+ onClick: () => {
12416
+ onBlockKeep(file.sessionId, file.id, blockRanges[hoveredBlock]);
12417
+ },
12418
+ children: t("action.keep")
12419
+ }),
12420
+ (0, react_jsx_runtime.jsx)("button", {
12421
+ type: "button",
12422
+ className: PendingPanel_module_css_default.action,
12423
+ "data-diff-block-revert": true,
12424
+ disabled: busy,
12425
+ onClick: () => {
12426
+ onBlockRevert(file.sessionId, file.id, blockRanges[hoveredBlock]);
12427
+ },
12428
+ children: t("action.revert")
12429
+ })
12430
+ ]
12431
+ })]
12432
+ }),
12433
+ searchOpen && (0, react_jsx_runtime.jsxs)("div", {
12434
+ className: PendingPanel_module_css_default.searchBar,
12435
+ "data-diff-searchbar": true,
12205
12436
  children: [
12206
- start > 0 && (0, react_jsx_runtime.jsx)("div", {
12207
- className: PendingPanel_module_css_default.vSpacer,
12208
- style: { height: start * ROW_HEIGHT_PX },
12209
- "aria-hidden": "true"
12437
+ (0, react_jsx_runtime.jsx)("input", {
12438
+ ref: searchInputRef,
12439
+ className: PendingPanel_module_css_default.searchInput,
12440
+ "data-diff-search-input": true,
12441
+ value: searchQuery,
12442
+ placeholder: t("panel.searchPlaceholder"),
12443
+ onChange: (event) => {
12444
+ setSearchQuery(event.target.value);
12445
+ },
12446
+ onKeyDown: (event) => {
12447
+ if (event.key === "Enter") {
12448
+ event.preventDefault();
12449
+ goSearch(event.shiftKey ? -1 : 1);
12450
+ } else if (event.key === "Escape") closeSearch();
12451
+ }
12210
12452
  }),
12211
- visibleRows.map((row, offset) => {
12212
- const index = start + offset;
12213
- return (0, react_jsx_runtime.jsx)(DiffRow, {
12214
- index,
12215
- row,
12216
- runs,
12217
- focused: inFocusedBlock(index)
12218
- }, index);
12453
+ (0, react_jsx_runtime.jsx)("span", {
12454
+ className: PendingPanel_module_css_default.searchCount,
12455
+ "data-diff-search-count": true,
12456
+ children: searchMatches.length === 0 ? "0/0" : `${searchIndex % searchMatches.length + 1}/${searchMatches.length}`
12219
12457
  }),
12220
- end < rowCount && (0, react_jsx_runtime.jsx)("div", {
12221
- className: PendingPanel_module_css_default.vSpacer,
12222
- style: { height: (rowCount - end) * ROW_HEIGHT_PX },
12223
- "aria-hidden": "true"
12458
+ (0, react_jsx_runtime.jsx)("button", {
12459
+ type: "button",
12460
+ className: `${PendingPanel_module_css_default.action} ${PendingPanel_module_css_default.iconAction}`,
12461
+ "data-diff-search-prev": true,
12462
+ "aria-label": t("action.prevDiff"),
12463
+ disabled: searchMatches.length === 0,
12464
+ onClick: () => {
12465
+ goSearch(-1);
12466
+ },
12467
+ children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronUpOutline14, { size: 14 })
12468
+ }),
12469
+ (0, react_jsx_runtime.jsx)("button", {
12470
+ type: "button",
12471
+ className: `${PendingPanel_module_css_default.action} ${PendingPanel_module_css_default.iconAction}`,
12472
+ "data-diff-search-next": true,
12473
+ "aria-label": t("action.nextDiff"),
12474
+ disabled: searchMatches.length === 0,
12475
+ onClick: () => {
12476
+ goSearch(1);
12477
+ },
12478
+ children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, { size: 14 })
12479
+ }),
12480
+ (0, react_jsx_runtime.jsx)("button", {
12481
+ type: "button",
12482
+ className: `${PendingPanel_module_css_default.action} ${PendingPanel_module_css_default.iconAction}`,
12483
+ "data-diff-search-close": true,
12484
+ "aria-label": t("action.close"),
12485
+ onClick: closeSearch,
12486
+ children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCloseOutline16, { size: 14 })
12224
12487
  })
12225
12488
  ]
12489
+ }),
12490
+ rulerMarkers.length > 0 && (0, react_jsx_runtime.jsx)("div", {
12491
+ className: PendingPanel_module_css_default.overviewRuler,
12492
+ "data-diff-approval-ruler": true,
12493
+ "aria-hidden": "true",
12494
+ children: rulerMarkers.map((marker, index) => (0, react_jsx_runtime.jsx)("div", {
12495
+ className: `${PendingPanel_module_css_default.overviewMarker} ${marker.kind === "del" ? PendingPanel_module_css_default.markerDel : PendingPanel_module_css_default.markerAdd}`,
12496
+ "data-diff-ruler-marker": marker.kind,
12497
+ style: {
12498
+ top: `${marker.top}%`,
12499
+ height: `${marker.height}%`
12500
+ }
12501
+ }, index))
12226
12502
  })
12227
- }), rulerMarkers.length > 0 && (0, react_jsx_runtime.jsx)("div", {
12228
- className: PendingPanel_module_css_default.overviewRuler,
12229
- "data-diff-approval-ruler": true,
12230
- "aria-hidden": "true",
12231
- children: rulerMarkers.map((marker, index) => (0, react_jsx_runtime.jsx)("div", {
12232
- className: `${PendingPanel_module_css_default.overviewMarker} ${marker.kind === "del" ? PendingPanel_module_css_default.markerDel : PendingPanel_module_css_default.markerAdd}`,
12233
- "data-diff-ruler-marker": marker.kind,
12234
- style: {
12235
- top: `${marker.top}%`,
12236
- height: `${marker.height}%`
12237
- }
12238
- }, index))
12239
- })]
12503
+ ]
12240
12504
  }),
12241
12505
  (0, react_jsx_runtime.jsxs)("div", {
12242
12506
  className: PendingPanel_module_css_default.statusBar,
@@ -12274,19 +12538,23 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
12274
12538
  onClose: () => {
12275
12539
  setLangMenuOpen(false);
12276
12540
  },
12277
- anchor: (0, react_jsx_runtime.jsxs)("button", {
12278
- type: "button",
12279
- className: PendingPanel_module_css_default.langSelect,
12280
- "data-diff-lang": true,
12281
- "aria-label": t("action.langSelect"),
12282
- title: t("action.langSelect"),
12283
- onClick: () => {
12284
- setLangMenuOpen((value) => !value);
12285
- },
12286
- children: [(0, react_jsx_runtime.jsx)("span", {
12287
- className: PendingPanel_module_css_default.langLabel,
12288
- children: langLabel
12289
- }), (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, { size: 12 })]
12541
+ anchor: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
12542
+ label: t("action.langSelect"),
12543
+ side: "top",
12544
+ delayMs: 500,
12545
+ children: (0, react_jsx_runtime.jsxs)("button", {
12546
+ type: "button",
12547
+ className: PendingPanel_module_css_default.langSelect,
12548
+ "data-diff-lang": true,
12549
+ "aria-label": t("action.langSelect"),
12550
+ onClick: () => {
12551
+ setLangMenuOpen((value) => !value);
12552
+ },
12553
+ children: [(0, react_jsx_runtime.jsx)("span", {
12554
+ className: PendingPanel_module_css_default.langLabel,
12555
+ children: langLabel
12556
+ }), (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, { size: 12 })]
12557
+ })
12290
12558
  })
12291
12559
  })
12292
12560
  ]
@@ -12295,11 +12563,14 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
12295
12563
  });
12296
12564
  }
12297
12565
  /** Render the pending-edit review panel and its unified footer action. */
12298
- function PendingPanel({ wide, useSessions, usePending, onRefresh, onKeep, onRevert, onOpen, t }) {
12566
+ function PendingPanel({ wide, useSessions, usePending, onRefresh, onKeep, onRevert, onBlockKeep, onBlockRevert, onOpen, t }) {
12299
12567
  const current = useSessions((state) => state.current);
12300
12568
  const snapshot = usePending((snapshot) => snapshot);
12301
12569
  const [open, setOpen] = (0, react.useState)(false);
12302
12570
  const [selected, setSelected] = (0, react.useState)("");
12571
+ /** Bumped when the already-open file is clicked again, to jump to the next
12572
+ * diff block in the open file's detail pane. */
12573
+ const [jumpSignal, setJumpSignal] = (0, react.useState)(0);
12303
12574
  /** Pending count captured at open time; auto-close needs a list that emptied. */
12304
12575
  const [openedCount, setOpenedCount] = (0, react.useState)(0);
12305
12576
  /** Bottom offset tracking the chat composer's top edge so the input stays visible. */
@@ -12361,13 +12632,11 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
12361
12632
  document.removeEventListener("pointerdown", onPointerDown, true);
12362
12633
  };
12363
12634
  }, [open]);
12364
- const files = snapshot.files;
12365
- const mine = files.filter((file) => file.sessionId === current);
12366
- const theirs = files.filter((file) => file.sessionId !== current);
12635
+ const files = snapshot.files.filter((file) => file.sessionId === current);
12367
12636
  (0, react.useEffect)(() => {
12368
12637
  if (!open) return;
12369
12638
  if (selected !== "" && files.some((file) => file.id === selected)) return;
12370
- const next = [...mine, ...theirs][0];
12639
+ const next = files[0];
12371
12640
  if (next !== void 0 && next.id !== selected) setSelected(next.id);
12372
12641
  }, [
12373
12642
  open,
@@ -12395,7 +12664,10 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
12395
12664
  file: entry,
12396
12665
  selected: selected === entry.id,
12397
12666
  t,
12398
- onSelect: setSelected
12667
+ onSelect: (id) => {
12668
+ if (id === selected) setJumpSignal((signal) => signal + 1);
12669
+ else setSelected(id);
12670
+ }
12399
12671
  }, entry.id);
12400
12672
  const selectedFile = files.find((file) => file.id === selected);
12401
12673
  /** Drag the list/detail divider; width follows the pointer within its bounds. */
@@ -12438,26 +12710,34 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
12438
12710
  children: t("panel.title")
12439
12711
  }), (0, react_jsx_runtime.jsxs)("div", {
12440
12712
  className: PendingPanel_module_css_default.headerActions,
12441
- children: [(0, react_jsx_runtime.jsx)("button", {
12442
- type: "button",
12443
- className: expanded ? `${PendingPanel_module_css_default.expand} ${PendingPanel_module_css_default.expandExpanded}` : PendingPanel_module_css_default.expand,
12444
- "data-diff-approval-expand": true,
12445
- "aria-label": t(expanded ? "action.exitFullscreen" : "action.expand"),
12446
- title: t(expanded ? "action.exitFullscreen" : "action.expand"),
12447
- onClick: () => {
12448
- setExpanded((value) => !value);
12449
- },
12450
- children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconFullscreenOutline16, { size: 14 })
12451
- }), (0, react_jsx_runtime.jsx)("button", {
12452
- type: "button",
12453
- className: PendingPanel_module_css_default.close,
12454
- "data-diff-approval-close": true,
12455
- "aria-label": t("action.close"),
12456
- title: t("action.close"),
12457
- onClick: () => {
12458
- setOpen(false);
12459
- },
12460
- children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCloseOutline16, { size: 14 })
12713
+ children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
12714
+ label: t(expanded ? "action.exitFullscreen" : "action.expand"),
12715
+ side: "bottom",
12716
+ delayMs: 500,
12717
+ children: (0, react_jsx_runtime.jsx)("button", {
12718
+ type: "button",
12719
+ className: expanded ? `${PendingPanel_module_css_default.expand} ${PendingPanel_module_css_default.expandExpanded}` : PendingPanel_module_css_default.expand,
12720
+ "data-diff-approval-expand": true,
12721
+ "aria-label": t(expanded ? "action.exitFullscreen" : "action.expand"),
12722
+ onClick: () => {
12723
+ setExpanded((value) => !value);
12724
+ },
12725
+ children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconFullscreenOutline16, { size: 14 })
12726
+ })
12727
+ }), (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
12728
+ label: t("action.close"),
12729
+ side: "bottom",
12730
+ delayMs: 500,
12731
+ children: (0, react_jsx_runtime.jsx)("button", {
12732
+ type: "button",
12733
+ className: PendingPanel_module_css_default.close,
12734
+ "data-diff-approval-close": true,
12735
+ "aria-label": t("action.close"),
12736
+ onClick: () => {
12737
+ setOpen(false);
12738
+ },
12739
+ children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCloseOutline16, { size: 14 })
12740
+ })
12461
12741
  })]
12462
12742
  })]
12463
12743
  }), snapshot.error !== void 0 || !snapshot.read || files.length === 0 ? (0, react_jsx_runtime.jsxs)("div", {
@@ -12480,23 +12760,17 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
12480
12760
  }) : (0, react_jsx_runtime.jsxs)("div", {
12481
12761
  className: PendingPanel_module_css_default.split,
12482
12762
  children: [
12483
- (0, react_jsx_runtime.jsxs)("nav", {
12763
+ (0, react_jsx_runtime.jsx)("nav", {
12484
12764
  className: PendingPanel_module_css_default.fileList,
12485
12765
  style: { width: listWidth },
12486
12766
  "data-diff-approval-file-list": true,
12487
- children: [mine.length > 0 && (0, react_jsx_runtime.jsxs)("section", { children: [(0, react_jsx_runtime.jsx)("h3", {
12767
+ children: files.length > 0 && (0, react_jsx_runtime.jsxs)("section", { children: [(0, react_jsx_runtime.jsx)("h3", {
12488
12768
  className: PendingPanel_module_css_default.group,
12489
12769
  children: t("panel.group.current")
12490
12770
  }), (0, react_jsx_runtime.jsx)("ul", {
12491
12771
  className: PendingPanel_module_css_default.rows,
12492
- children: mine.map(renderEntry)
12493
- })] }), theirs.length > 0 && (0, react_jsx_runtime.jsxs)("section", { children: [(0, react_jsx_runtime.jsx)("h3", {
12494
- className: PendingPanel_module_css_default.group,
12495
- children: t("panel.group.others")
12496
- }), (0, react_jsx_runtime.jsx)("ul", {
12497
- className: PendingPanel_module_css_default.rows,
12498
- children: theirs.map(renderEntry)
12499
- })] })]
12772
+ children: files.map(renderEntry)
12773
+ })] })
12500
12774
  }),
12501
12775
  (0, react_jsx_runtime.jsx)("div", {
12502
12776
  className: PendingPanel_module_css_default.resizeHandle,
@@ -12510,11 +12784,14 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
12510
12784
  children: t("panel.selectHint")
12511
12785
  }) : (0, react_jsx_runtime.jsx)(PendingDiff, {
12512
12786
  file: selectedFile,
12513
- files,
12514
12787
  busy: snapshot.busy.has(selectedFile.id),
12788
+ workspacePath: snapshot.workspacePath,
12789
+ jumpSignal,
12515
12790
  t,
12516
12791
  onKeep,
12517
12792
  onRevert,
12793
+ onBlockKeep,
12794
+ onBlockRevert,
12518
12795
  onOpen
12519
12796
  })
12520
12797
  })
@@ -12562,7 +12839,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
12562
12839
  function createDiffApprovalPort(rpc) {
12563
12840
  return {
12564
12841
  async list(sessionId) {
12565
- return filesOf(await rpc.call(DIFF_APPROVAL_CHANNEL, "list", { sessionId }));
12842
+ return listValueOf(await rpc.call(DIFF_APPROVAL_CHANNEL, "list", { sessionId }));
12566
12843
  },
12567
12844
  async keep(sessionId, id) {
12568
12845
  return actionOf(await rpc.call(DIFF_APPROVAL_CHANNEL, "keep", {
@@ -12576,6 +12853,20 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
12576
12853
  id
12577
12854
  }));
12578
12855
  },
12856
+ async blockKeep(sessionId, id, block) {
12857
+ return actionOf(await rpc.call(DIFF_APPROVAL_CHANNEL, "block-keep", {
12858
+ sessionId,
12859
+ id,
12860
+ block
12861
+ }));
12862
+ },
12863
+ async blockRevert(sessionId, id, block) {
12864
+ return actionOf(await rpc.call(DIFF_APPROVAL_CHANNEL, "block-revert", {
12865
+ sessionId,
12866
+ id,
12867
+ block
12868
+ }));
12869
+ },
12579
12870
  async open(sessionId, id, action) {
12580
12871
  return openOf(await rpc.call(DIFF_APPROVAL_CHANNEL, "open", {
12581
12872
  sessionId,
@@ -12608,18 +12899,23 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
12608
12899
  };
12609
12900
  }
12610
12901
  /** Narrow the list endpoint's value; a malformed wire value is a read failure. */
12611
- function filesOf(result) {
12902
+ function listValueOf(result) {
12612
12903
  if (!result.ok) throw new Error(`${result.error.code}: ${result.error.message}`);
12613
12904
  const value = result.value;
12614
12905
  if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error("list returned a malformed value");
12615
12906
  const rows = value.files;
12616
12907
  if (!Array.isArray(rows)) throw new Error("list returned a malformed value");
12908
+ const workspace = value.workspacePath;
12909
+ const workspacePath = typeof workspace === "string" && workspace.length > 0 ? workspace : void 0;
12617
12910
  const files = [];
12618
12911
  for (const row of rows) {
12619
12912
  const file = pendingFileOf(row);
12620
12913
  if (file !== void 0) files.push(file);
12621
12914
  }
12622
- return files;
12915
+ return {
12916
+ files,
12917
+ workspacePath
12918
+ };
12623
12919
  }
12624
12920
  /** Narrow one action endpoint's value; a malformed wire value is an action failure. */
12625
12921
  function actionOf(result) {
@@ -12706,16 +13002,18 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
12706
13002
  return;
12707
13003
  }
12708
13004
  try {
12709
- const files = await port.list(sessionId);
13005
+ const { files, workspacePath } = await port.list(sessionId);
12710
13006
  publish({
12711
13007
  read: true,
12712
13008
  files,
13009
+ workspacePath,
12713
13010
  busy: EMPTY_BUSY
12714
13011
  });
12715
13012
  } catch (error) {
12716
13013
  publish({
12717
13014
  read: true,
12718
13015
  files: snapshot.files,
13016
+ workspacePath: snapshot.workspacePath,
12719
13017
  error: error instanceof Error ? error.message : String(error),
12720
13018
  busy: snapshot.busy
12721
13019
  });
@@ -12731,6 +13029,42 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
12731
13029
  await port.revert(sessionId, id);
12732
13030
  });
12733
13031
  },
13032
+ async blockKeep(sessionId, id, block) {
13033
+ const { error: _cleared, ...base } = snapshot;
13034
+ publish({
13035
+ ...base,
13036
+ busy: /* @__PURE__ */ new Set([...snapshot.busy, id])
13037
+ });
13038
+ try {
13039
+ await port.blockKeep(sessionId, id, block);
13040
+ } catch (error) {
13041
+ publish({
13042
+ ...snapshot,
13043
+ error: error instanceof Error ? error.message : String(error),
13044
+ busy: new Set([...snapshot.busy].filter((busy) => busy !== id))
13045
+ });
13046
+ return;
13047
+ }
13048
+ await this.refresh(sessionId);
13049
+ },
13050
+ async blockRevert(sessionId, id, block) {
13051
+ const { error: _cleared, ...base } = snapshot;
13052
+ publish({
13053
+ ...base,
13054
+ busy: /* @__PURE__ */ new Set([...snapshot.busy, id])
13055
+ });
13056
+ try {
13057
+ await port.blockRevert(sessionId, id, block);
13058
+ } catch (error) {
13059
+ publish({
13060
+ ...snapshot,
13061
+ error: error instanceof Error ? error.message : String(error),
13062
+ busy: new Set([...snapshot.busy].filter((busy) => busy !== id))
13063
+ });
13064
+ return;
13065
+ }
13066
+ await this.refresh(sessionId);
13067
+ },
12734
13068
  async open(sessionId, id, action) {
12735
13069
  try {
12736
13070
  await port.open(sessionId, id, action);
@@ -12764,7 +13098,9 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
12764
13098
  "panel.group.others": "其他会话",
12765
13099
  "panel.aria": "待处理改动",
12766
13100
  "panel.stats": "+{added} -{removed}",
13101
+ "panel.blockPosition": "第 {current}/{total} 块",
12767
13102
  "panel.selectHint": "点击每项查看整个文件的差异;选中文本后,用底部状态栏或 Ctrl+L 复制引用",
13103
+ "panel.searchPlaceholder": "搜索",
12768
13104
  "panel.missing": "文件已不存在",
12769
13105
  "panel.missingHint": "该文件已不存在,回退会恢复该文件。",
12770
13106
  "panel.createHint": "回退会删除该新建的文件。",
@@ -12783,6 +13119,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
12783
13119
  "action.langAuto": "自动",
12784
13120
  "action.langAutoDetected": "自动:{lang}",
12785
13121
  "action.langSelect": "高亮语言",
13122
+ "action.search": "搜索 (Ctrl+F)",
12786
13123
  "action.expand": "展开全屏",
12787
13124
  "action.exitFullscreen": "退出全屏",
12788
13125
  "action.close": "关闭",
@@ -12800,7 +13137,9 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
12800
13137
  "panel.group.others": "Other sessions",
12801
13138
  "panel.aria": "Pending changes",
12802
13139
  "panel.stats": "+{added} -{removed}",
13140
+ "panel.blockPosition": "{current}/{total}",
12803
13141
  "panel.selectHint": "Select an item to review its whole-file diff; select text and copy its reference from the status bar (Ctrl+L)",
13142
+ "panel.searchPlaceholder": "Search",
12804
13143
  "panel.missing": "File is gone",
12805
13144
  "panel.missingHint": "This file no longer exists; reverting restores it.",
12806
13145
  "panel.createHint": "Reverting removes this created file.",
@@ -12819,6 +13158,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
12819
13158
  "action.langAuto": "Auto",
12820
13159
  "action.langAutoDetected": "Auto: {lang}",
12821
13160
  "action.langSelect": "Highlight language",
13161
+ "action.search": "Search (Ctrl+F)",
12822
13162
  "action.expand": "Expand fullscreen",
12823
13163
  "action.exitFullscreen": "Exit fullscreen",
12824
13164
  "action.close": "Close",
@@ -12860,6 +13200,8 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
12860
13200
  },
12861
13201
  onKeep: (sessionId, path) => store.keep(sessionId, path),
12862
13202
  onRevert: (sessionId, path) => store.revert(sessionId, path),
13203
+ onBlockKeep: (sessionId, id, block) => store.blockKeep(sessionId, id, block),
13204
+ onBlockRevert: (sessionId, id, block) => store.blockRevert(sessionId, id, block),
12863
13205
  onOpen: (sessionId, id, action) => store.open(sessionId, id, action)
12864
13206
  })
12865
13207
  }, PendingPanel));