dsh-diff-approval 0.7.0 → 0.8.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
@@ -11620,8 +11620,39 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
11620
11620
  return `${referencePathOf(path, workspacePath)}:${lineRangeLabel(start, end)}`;
11621
11621
  }
11622
11622
  //#endregion
11623
+ //#region lib/types/client/settings.js
11624
+ /** Client preferences for the review panel, persisted in localStorage. */
11625
+ const PASTE_ON_COPY_KEY = "diff-approval:paste-on-copy";
11626
+ const IMPORT_UNTRACKED_KEY = "diff-approval:import-untracked";
11627
+ /**
11628
+ * Whether copying a reference should also paste it into the chat input and
11629
+ * focus it. Defaults to on; only an explicit `'0'` disables it.
11630
+ * @returns whether auto-paste is enabled.
11631
+ */
11632
+ function pasteOnCopyEnabled() {
11633
+ return localStorage.getItem(PASTE_ON_COPY_KEY) !== "0";
11634
+ }
11635
+ /** Persist the auto-paste preference. */
11636
+ function setPasteOnCopyEnabled(value) {
11637
+ localStorage.setItem(PASTE_ON_COPY_KEY, value ? "1" : "0");
11638
+ }
11639
+ /**
11640
+ * Whether importing workspace VCS changes includes new/untracked files (git
11641
+ * `??`, svn `?`, p4 unversioned). Defaults to off: collecting them scans the
11642
+ * whole workspace, which can be slow on large trees, so the preference is
11643
+ * opt-in. Only an explicit `'1'` enables it.
11644
+ * @returns whether untracked files are imported.
11645
+ */
11646
+ function includeUntrackedEnabled() {
11647
+ return localStorage.getItem(IMPORT_UNTRACKED_KEY) === "1";
11648
+ }
11649
+ /** Persist the import-untracked preference. */
11650
+ function setIncludeUntrackedEnabled(value) {
11651
+ localStorage.setItem(IMPORT_UNTRACKED_KEY, value ? "1" : "0");
11652
+ }
11653
+ //#endregion
11623
11654
  //#region \0dsh-css:/home/runner/work/dsh-diff-approval/dsh-diff-approval/src/client/PendingPanel.module.css.mjs
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}";
11655
+ const css = ".F1KBNa_layer{box-sizing:border-box;flex:none;align-items:center;width:calc(100% + 4px);height:42px;margin:4px -2px;display:flex;position:relative}.F1KBNa_footerButtons{align-items:center;width:100%;display:flex}.F1KBNa_badge{box-sizing:border-box;width:100%;height:42px;color:var(--dsw-alias-label-primary);cursor:pointer;background:0 0;border:none;border-radius:12px;align-items:center;gap:8px;padding:0 10px 0 8px;font-family:inherit;font-size:14px;line-height:22px;display:flex;overflow:hidden}.F1KBNa_badge:hover{background:var(--dsw-alias-interactive-bg-hover)}.F1KBNa_badge[data-active]{background:var(--dsw-alias-interactive-bg-hover-solid)}.F1KBNa_badge:disabled{color:var(--dsw-alias-label-tertiary);cursor:default;background:0 0}.F1KBNa_badgeLabel{text-overflow:ellipsis;white-space:nowrap;min-width:0;overflow:hidden}.F1KBNa_badgeCount{color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;flex:none;margin-left:auto;font-size:12px;line-height:16px}.F1KBNa_layer.F1KBNa_rail{width:36px;height:36px;margin:8px 0 10px}.F1KBNa_rail .F1KBNa_badge{border-radius:50%;justify-content:center;gap:0;width:36px;height:36px;padding:0}.F1KBNa_rail .F1KBNa_badgeLabel{display:none}.F1KBNa_rail .F1KBNa_badgeCount{box-sizing:border-box;background:var(--dsw-alias-state-business-primary);min-width:18px;height:18px;color:var(--dsw-alias-label-primary-foreground);font-variant-numeric:tabular-nums;border-radius:9px;justify-content:center;align-items:center;padding:0 4px;font-size:11px;line-height:18px;display:flex;position:absolute;top:-3px;right:-7px}.F1KBNa_fullscreenBackdrop{z-index:29;background:var(--dsw-specific-sidebar-fill);position:fixed;inset:0}.F1KBNa_panel{z-index:30;border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-base);width:auto;max-width:none;box-shadow:var(--dsw-shadow-lv2);--dsh-scrollbar-thumb:var(--dsw-alias-scrollbar-bg-l2);--dsh-scrollbar-thumb-hover:var(--dsw-alias-scrollbar-hover-l2);border-radius:12px;flex-direction:column;display:flex;position:fixed;inset:8px 8px 128px;overflow:hidden}.F1KBNa_header{box-sizing:border-box;border-bottom:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-base);flex:none;justify-content:space-between;align-items:center;min-height:44px;padding:10px 12px;display:flex}.F1KBNa_headerActions{align-items:center;gap:2px;display:flex}.F1KBNa_settingsPage{padding:8px 12px}.F1KBNa_settingsRow{border-bottom:1px solid var(--dsw-alias-border-l2);align-items:center;gap:8px;padding:16px 0;display:flex}.F1KBNa_settingsRowText{flex-direction:column;flex:1;gap:4px;min-width:0;padding-right:48px;display:flex}.F1KBNa_settingsRowTitle{color:var(--dsw-alias-label-primary);font-size:14px;font-weight:400;line-height:22px}.F1KBNa_settingsRowDesc{color:var(--dsw-alias-label-tertiary);font-size:12px;font-weight:400;line-height:18px}.F1KBNa_settingsSelector{background:var(--dsw-alias-bg-module-platform);height:36px;font:inherit;color:var(--dsw-alias-label-primary);cursor:pointer;border:none;border-radius:18px;align-items:center;gap:12px;padding:0 14px;font-size:14px;line-height:22px;display:inline-flex}.F1KBNa_settingsSelector:hover{background:var(--dsw-alias-interactive-bg-hover)}.F1KBNa_settingsSelectorChevron{flex:none}.F1KBNa_states{flex-direction:column;flex:1;min-height:0;padding:4px 12px 12px;display:flex;overflow-y:auto}.F1KBNa_split{flex:1;align-items:stretch;min-height:0;display:flex}.F1KBNa_fileList{box-sizing:border-box;border-right:1px solid var(--dsw-alias-border-l2);flex-direction:column;flex:none;width:240px;min-height:0;padding:4px 8px 12px;display:flex}.F1KBNa_listScroll{flex:1;min-height:0;overflow-y:auto}.F1KBNa_bulkActions{flex:none;gap:6px;padding-top:8px;display:flex}.F1KBNa_bulkActions .F1KBNa_action{text-align:center;flex:1}.F1KBNa_resizeHandle{cursor:col-resize;background:0 0;flex:none;width:5px;margin:0 -2px}.F1KBNa_resizeHandle:hover{background:var(--dsw-alias-border-l2)}.F1KBNa_detail{flex-direction:column;flex:1;min-width:0;min-height:0;display:flex}.F1KBNa_detailEmpty{color:var(--dsw-alias-label-tertiary);text-align:center;flex:1;justify-content:center;align-items:center;margin:0;padding:24px;font-size:12px;line-height:18px;display:flex}.F1KBNa_title{color:var(--dsw-alias-label-primary);font-size:13px;font-weight:500;line-height:20px}.F1KBNa_note,.F1KBNa_readError,.F1KBNa_hint{color:var(--dsw-alias-label-tertiary);margin:4px 0;font-size:12px;line-height:18px}.F1KBNa_noteCentered{text-align:center;margin:auto}.F1KBNa_emptyState{flex-direction:column;align-items:center;gap:10px;margin:auto;display:flex}.F1KBNa_importButton{border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-primary);cursor:pointer;background:0 0;border-radius:8px;padding:5px 14px;font-family:inherit;font-size:12px;line-height:18px}.F1KBNa_importButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.F1KBNa_importButton:disabled{color:var(--dsw-alias-label-tertiary);cursor:default}.F1KBNa_importNote{color:var(--dsw-alias-label-tertiary);text-align:center;margin:0;font-size:12px;line-height:18px}.F1KBNa_readError{color:var(--dsw-alias-state-error-primary)}.F1KBNa_group{color:var(--dsw-alias-label-tertiary);margin:8px 0 4px;font-size:12px;font-weight:500;line-height:16px}.F1KBNa_rows{margin:0;padding:0;list-style:none}.F1KBNa_row{border:1px solid #0000;border-radius:10px;margin:2px 0}.F1KBNa_rowHead{width:100%;color:var(--dsw-alias-label-primary);font:inherit;text-align:left;cursor:pointer;background:0 0;border:none;border-radius:10px;align-items:baseline;gap:8px;padding:6px 8px;display:flex}.F1KBNa_rowHead:hover{background:var(--dsw-alias-interactive-bg-hover-solid)}.F1KBNa_rowHead[data-selected]{background:var(--dsw-alias-interactive-bg-hover)}.F1KBNa_rowPath{text-overflow:ellipsis;white-space:nowrap;min-width:0;font:var(--dsw-font-markdown-code-block);overflow:hidden}.F1KBNa_kindTag{border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-tertiary);white-space:nowrap;border-radius:6px;flex:none;padding:1px 6px;font-size:11px;line-height:14px}.F1KBNa_kindHint{color:var(--dsw-alias-label-tertiary);flex:none;font-size:12px;line-height:16px}.F1KBNa_divergedHint,.F1KBNa_missingHint{border-bottom:1px solid var(--dsw-alias-border-l2);margin:0;padding:6px 8px;font-size:12px;line-height:18px}.F1KBNa_missing{border:1px solid var(--dsw-alias-state-warn-primary);color:var(--dsw-alias-state-warn-label,var(--dsw-alias-state-warn-primary));white-space:nowrap;border-radius:6px;flex:none;padding:1px 6px;font-size:11px;line-height:14px}.F1KBNa_rowFailed{border:1px solid var(--dsw-alias-state-error-primary);color:var(--dsw-alias-state-error-primary);white-space:nowrap;border-radius:6px;flex:none;padding:1px 6px;font-size:11px;line-height:14px}.F1KBNa_actionError{border-bottom:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-state-error-primary);overflow-wrap:anywhere;margin:0;padding:6px 8px;font-size:12px;line-height:18px}.F1KBNa_missingHint{color:var(--dsw-alias-state-warn-label,var(--dsw-alias-state-warn-primary))}.F1KBNa_rowMeta{color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;flex:none;gap:6px;margin-left:auto;font-size:12px;line-height:16px;display:inline-flex}.F1KBNa_addCount{color:color-mix(in srgb, var(--dsw-alias-state-success-primary) 60%, var(--dsw-alias-label-primary))}.F1KBNa_delCount{color:color-mix(in srgb, var(--dsw-alias-state-error-primary) 60%, var(--dsw-alias-label-primary))}.F1KBNa_diff{background:var(--dsw-alias-markdown-code-block);flex-direction:column;flex:1;min-height:0;display:flex;position:relative;overflow:hidden}.F1KBNa_diffHeader{border-bottom:1px solid var(--dsw-alias-border-l2);align-items:center;gap:8px;padding:6px 8px;display:flex}.F1KBNa_diffPath{text-overflow:ellipsis;white-space:nowrap;min-width:0;color:var(--dsw-alias-label-primary);font:var(--dsw-font-markdown-code-block);flex:1;font-size:12px;line-height:18px;overflow:hidden}.F1KBNa_diffActions{border-bottom:1px solid var(--dsw-alias-border-l2);align-items:center;gap:8px;padding:6px 8px;display:flex}.F1KBNa_diffStats{color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;font-size:12px;line-height:16px}.F1KBNa_flexSpacer{flex:1}.F1KBNa_action{border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-primary);cursor:pointer;background:0 0;border-radius:8px;padding:3px 12px;font-family:inherit;font-size:12px;line-height:18px}.F1KBNa_action:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.F1KBNa_action:disabled{color:var(--dsw-alias-label-tertiary);cursor:default}.F1KBNa_actionPrimary{background:var(--dsw-alias-state-business-primary);border-color:var(--dsw-alias-state-business-primary);color:var(--dsw-static-neutral-bluish-50)}.F1KBNa_actionPrimary:hover:not(:disabled){background:color-mix(in srgb, var(--dsw-alias-state-business-primary) 85%, var(--dsw-static-neutral-bluish-1000))}.F1KBNa_actionPrimary:disabled{background:var(--dsw-alias-state-business-primary);color:var(--dsw-static-neutral-bluish-50);opacity:.55}.F1KBNa_actionQuietDisabled:disabled{cursor:pointer;color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-border-l1);background:0 0}.F1KBNa_actionPrimary.F1KBNa_actionQuietDisabled:disabled{background:var(--dsw-alias-state-business-primary);border-color:var(--dsw-alias-state-business-primary);color:var(--dsw-static-neutral-bluish-50);opacity:1}.F1KBNa_iconAction{justify-content:center;align-items:center;min-height:25px;padding:4px 6px;display:inline-flex}.F1KBNa_close{cursor:pointer;width:28px;height:28px;color:var(--dsw-alias-label-tertiary);background:0 0;border:none;border-radius:28px;justify-content:center;align-items:center;padding:0;display:inline-flex}.F1KBNa_close:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}.F1KBNa_expand{cursor:pointer;width:28px;height:28px;color:var(--dsw-alias-label-tertiary);background:0 0;border:none;border-radius:28px;justify-content:center;align-items:center;padding:0;display:inline-flex}.F1KBNa_expand:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}.F1KBNa_expandExpanded svg{transform:rotate(180deg)}.F1KBNa_diffBodyWrap{flex:1;min-height:0;display:flex;position:relative}.F1KBNa_diffBody{min-width:0;font:var(--dsw-font-markdown-code-block);cursor:text;outline:none;flex:1;padding:0;position:relative;overflow:auto}.F1KBNa_diffBody::-webkit-scrollbar,.F1KBNa_diffBody::-webkit-scrollbar-track,.F1KBNa_diffBody::-webkit-scrollbar-thumb{cursor:default}.F1KBNa_blockActions{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-markdown-code-block);box-shadow:var(--dsw-shadow-lv1);z-index:2;border-radius:10px;align-items:center;gap:9px;padding:5px;display:flex;position:absolute;right:8px}.F1KBNa_blockPosition{color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;white-space:nowrap;margin-top:1px;margin-left:7px;font-size:12px;line-height:16px}.F1KBNa_searchBar{z-index:3;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-markdown-code-block);box-shadow:var(--dsw-shadow-lv1);border-radius:10px;align-items:center;gap:4px;padding:4px;display:flex;position:absolute;top:8px;right:8px}.F1KBNa_searchInput{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-base);width:150px;color:var(--dsw-alias-label-primary);border-radius:6px;outline:none;padding:3px 8px;font-family:inherit;font-size:12px;line-height:18px}.F1KBNa_searchInput:focus{border-color:var(--dsw-alias-state-business-primary)}.F1KBNa_searchCount{text-align:center;min-width:34px;color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;white-space:nowrap;font-size:12px;line-height:16px}.F1KBNa_diffBody [data-diff-search=hit]{background-image:linear-gradient(#facc1529,#facc1529)}.F1KBNa_diffBody [data-diff-search=current]{background-image:linear-gradient(#facc1552,#facc1552)}.F1KBNa_blockFlash{z-index:1;box-sizing:border-box;border:2px solid var(--dsw-alias-state-business-primary);pointer-events:none;border-radius:4px;animation:1s ease-out forwards F1KBNa_diffFlash;position:absolute;left:0;right:0}@keyframes F1KBNa_diffFlash{0%{opacity:1}to{opacity:0}}.F1KBNa_overviewRuler{pointer-events:none;opacity:.5;width:4px;position:absolute;top:0;bottom:0;right:0}.F1KBNa_overviewMarker{border-radius:2px;width:100%;min-height:2px;position:absolute;right:0}.F1KBNa_markerDel{background-color:var(--dsw-alias-state-error-primary)}.F1KBNa_markerAdd{background-color:var(--dsw-alias-state-success-primary)}.F1KBNa_lines{border-spacing:0;width:max-content;min-width:100%;display:table}.F1KBNa_line{height:22px;line-height:22px;display:table-row}.F1KBNa_vSpacer{display:table-row}.F1KBNa_gutter{box-sizing:border-box;text-align:right;width:44px;color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;user-select:none;cursor:default;padding-right:10px;display:table-cell}.F1KBNa_code{white-space:pre;display:table-cell}.F1KBNa_context{color:var(--dsw-alias-label-primary)}.F1KBNa_del{background-color:color-mix(in srgb, var(--dsw-alias-state-error-primary) 12%, transparent)}.F1KBNa_add{background-color:color-mix(in srgb, var(--dsw-alias-state-success-primary) 10%, transparent)}.F1KBNa_statusBar{box-sizing:border-box;border-top:1px solid var(--dsw-alias-border-l2);height:34px;color:var(--dsw-alias-label-tertiary);font-family:var(--dsw-font-markdown-code-block);font-variant-numeric:tabular-nums;flex:none;align-items:center;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}";
11625
11656
  const tagId = "dsh-diff-approval/PendingPanel.module.css";
11626
11657
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
11627
11658
  const tag = document.createElement("style");
@@ -11631,73 +11662,92 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
11631
11662
  document.head.appendChild(tag);
11632
11663
  }
11633
11664
  var PendingPanel_module_css_default = {
11634
- "gutter": "F1KBNa_gutter",
11635
- "missingHint": "F1KBNa_missingHint",
11636
- "searchCount": "F1KBNa_searchCount",
11665
+ "expandExpanded": "F1KBNa_expandExpanded",
11666
+ "panel": "F1KBNa_panel",
11667
+ "fullscreenBackdrop": "F1KBNa_fullscreenBackdrop",
11668
+ "settingsPage": "F1KBNa_settingsPage",
11637
11669
  "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",
11670
+ "row": "F1KBNa_row",
11671
+ "title": "F1KBNa_title",
11672
+ "blockActions": "F1KBNa_blockActions",
11673
+ "langLabel": "F1KBNa_langLabel",
11674
+ "detailEmpty": "F1KBNa_detailEmpty",
11648
11675
  "del": "F1KBNa_del",
11676
+ "listScroll": "F1KBNa_listScroll",
11677
+ "close": "F1KBNa_close",
11678
+ "group": "F1KBNa_group",
11679
+ "diffActions": "F1KBNa_diffActions",
11680
+ "diffBodyWrap": "F1KBNa_diffBodyWrap",
11681
+ "states": "F1KBNa_states",
11649
11682
  "searchInput": "F1KBNa_searchInput",
11650
- "expand": "F1KBNa_expand",
11651
- "searchBar": "F1KBNa_searchBar",
11652
- "markerDel": "F1KBNa_markerDel",
11653
- "title": "F1KBNa_title",
11654
- "vSpacer": "F1KBNa_vSpacer",
11683
+ "settingsRowText": "F1KBNa_settingsRowText",
11684
+ "note": "F1KBNa_note",
11685
+ "addCount": "F1KBNa_addCount",
11686
+ "layer": "F1KBNa_layer",
11687
+ "diff": "F1KBNa_diff",
11688
+ "add": "F1KBNa_add",
11689
+ "overviewMarker": "F1KBNa_overviewMarker",
11690
+ "settingsRowDesc": "F1KBNa_settingsRowDesc",
11691
+ "actionQuietDisabled": "F1KBNa_actionQuietDisabled",
11692
+ "searchCount": "F1KBNa_searchCount",
11655
11693
  "badgeCount": "F1KBNa_badgeCount",
11694
+ "context": "F1KBNa_context",
11695
+ "settingsSelectorChevron": "F1KBNa_settingsSelectorChevron",
11656
11696
  "kindHint": "F1KBNa_kindHint",
11697
+ "footerButtons": "F1KBNa_footerButtons",
11698
+ "diffHeader": "F1KBNa_diffHeader",
11699
+ "resizeHandle": "F1KBNa_resizeHandle",
11700
+ "statusBar": "F1KBNa_statusBar",
11701
+ "rowPath": "F1KBNa_rowPath",
11702
+ "settingsSelector": "F1KBNa_settingsSelector",
11703
+ "importButton": "F1KBNa_importButton",
11704
+ "markerAdd": "F1KBNa_markerAdd",
11705
+ "header": "F1KBNa_header",
11706
+ "settingsRowTitle": "F1KBNa_settingsRowTitle",
11707
+ "emptyState": "F1KBNa_emptyState",
11657
11708
  "rail": "F1KBNa_rail",
11658
- "expandExpanded": "F1KBNa_expandExpanded",
11709
+ "diffFlash": "F1KBNa_diffFlash",
11659
11710
  "action": "F1KBNa_action",
11660
- "diffHeader": "F1KBNa_diffHeader",
11661
- "fileList": "F1KBNa_fileList",
11662
- "diffActions": "F1KBNa_diffActions",
11711
+ "detail": "F1KBNa_detail",
11712
+ "rowMeta": "F1KBNa_rowMeta",
11713
+ "actionError": "F1KBNa_actionError",
11714
+ "diffStats": "F1KBNa_diffStats",
11715
+ "flexSpacer": "F1KBNa_flexSpacer",
11716
+ "diffBody": "F1KBNa_diffBody",
11717
+ "statusAction": "F1KBNa_statusAction",
11663
11718
  "readError": "F1KBNa_readError",
11664
- "note": "F1KBNa_note",
11665
- "line": "F1KBNa_line",
11719
+ "kindTag": "F1KBNa_kindTag",
11720
+ "delCount": "F1KBNa_delCount",
11721
+ "blockPosition": "F1KBNa_blockPosition",
11722
+ "overviewRuler": "F1KBNa_overviewRuler",
11723
+ "rowFailed": "F1KBNa_rowFailed",
11724
+ "headerActions": "F1KBNa_headerActions",
11666
11725
  "divergedHint": "F1KBNa_divergedHint",
11667
- "group": "F1KBNa_group",
11668
- "diff": "F1KBNa_diff",
11669
11726
  "code": "F1KBNa_code",
11670
- "headerActions": "F1KBNa_headerActions",
11671
- "detail": "F1KBNa_detail",
11727
+ "noteCentered": "F1KBNa_noteCentered",
11728
+ "expand": "F1KBNa_expand",
11729
+ "vSpacer": "F1KBNa_vSpacer",
11730
+ "importNote": "F1KBNa_importNote",
11731
+ "settingsRow": "F1KBNa_settingsRow",
11732
+ "split": "F1KBNa_split",
11672
11733
  "missing": "F1KBNa_missing",
11673
- "add": "F1KBNa_add",
11674
- "badgeLabel": "F1KBNa_badgeLabel",
11734
+ "actionPrimary": "F1KBNa_actionPrimary",
11735
+ "blockFlash": "F1KBNa_blockFlash",
11736
+ "langSelect": "F1KBNa_langSelect",
11737
+ "badge": "F1KBNa_badge",
11738
+ "diffPath": "F1KBNa_diffPath",
11675
11739
  "iconAction": "F1KBNa_iconAction",
11676
- "markerAdd": "F1KBNa_markerAdd",
11677
- "row": "F1KBNa_row",
11678
- "diffBody": "F1KBNa_diffBody",
11679
- "langLabel": "F1KBNa_langLabel",
11740
+ "markerDel": "F1KBNa_markerDel",
11741
+ "bulkActions": "F1KBNa_bulkActions",
11742
+ "gutter": "F1KBNa_gutter",
11743
+ "rowHead": "F1KBNa_rowHead",
11744
+ "searchBar": "F1KBNa_searchBar",
11745
+ "fileList": "F1KBNa_fileList",
11680
11746
  "lines": "F1KBNa_lines",
11681
- "panel": "F1KBNa_panel",
11682
- "split": "F1KBNa_split",
11683
- "badge": "F1KBNa_badge",
11684
- "delCount": "F1KBNa_delCount",
11685
- "overviewRuler": "F1KBNa_overviewRuler",
11686
- "rowMeta": "F1KBNa_rowMeta",
11687
- "blockActions": "F1KBNa_blockActions",
11688
11747
  "rows": "F1KBNa_rows",
11689
- "diffBodyWrap": "F1KBNa_diffBodyWrap",
11690
- "detailEmpty": "F1KBNa_detailEmpty",
11691
- "close": "F1KBNa_close",
11692
- "statusBar": "F1KBNa_statusBar",
11693
- "actionPrimary": "F1KBNa_actionPrimary",
11694
- "rowHead": "F1KBNa_rowHead",
11695
- "statusAction": "F1KBNa_statusAction",
11696
- "diffPath": "F1KBNa_diffPath",
11697
- "flexSpacer": "F1KBNa_flexSpacer",
11698
- "blockPosition": "F1KBNa_blockPosition",
11699
- "header": "F1KBNa_header",
11700
- "overviewMarker": "F1KBNa_overviewMarker"
11748
+ "missingHint": "F1KBNa_missingHint",
11749
+ "line": "F1KBNa_line",
11750
+ "badgeLabel": "F1KBNa_badgeLabel"
11701
11751
  };
11702
11752
  //#endregion
11703
11753
  //#region lib/types/client/PendingPanel.js
@@ -11731,23 +11781,49 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
11731
11781
  const MAX_LIST_WIDTH_PX = 560;
11732
11782
  /** Fixed diff-row height in px; the virtual window and jump math are built on it. */
11733
11783
  const ROW_HEIGHT_PX = 22;
11784
+ /** The overview ruler's width in px (mirrors `.overviewRuler`). The flash is
11785
+ * kept off it even when the scroller has no vertical scrollbar. */
11786
+ const OVERVIEW_RULER_WIDTH_PX = 4;
11734
11787
  /** Rows rendered beyond the visible window in each direction. */
11735
11788
  const OVERSCAN_ROWS = 8;
11736
11789
  /** Height of the floating per-block Keep/Revert frame in px: 26px actions +
11737
11790
  * 5px frame padding on each side + 1px border on each side, plus a little
11738
11791
  * breathing room so the bottom padding never sits flush against it. */
11739
11792
  const BLOCK_ACTIONS_FRAME_PX = 40;
11793
+ /** How long a fully-processed list stays open before the panel folds, so the
11794
+ * last action (a Keep-all/Revert-all especially) can still be undone with
11795
+ * Ctrl+Z. An undo that restores an entry cancels the pending close. */
11796
+ const EMPTY_CLOSE_GRACE_MS = 3e3;
11740
11797
  /** The trailing file-name segment of a path, used for row display. */
11741
11798
  function basenameOf(path) {
11742
11799
  const index = Math.max(path.lastIndexOf("/"), path.lastIndexOf("\\"));
11743
11800
  return index < 0 ? path : path.slice(index + 1);
11744
11801
  }
11802
+ /**
11803
+ * Open the DSH settings dialog and switch to this plugin's section. The
11804
+ * settings shell keeps its open state and the active section id as
11805
+ * component-local viewing state with no cross-plugin service, so the dialog
11806
+ * is opened by clicking the sidebar's settings trigger and then the nav cell
11807
+ * for this section. `element.click()` still fires React's delegated click
11808
+ * handlers, reaching the same state transitions a user gesture would.
11809
+ * @param sectionLabel - the nav label of this plugin's settings section.
11810
+ */
11811
+ function openSettingsSection(sectionLabel) {
11812
+ const trigger = document.querySelector("button[aria-haspopup=\"dialog\"]");
11813
+ if (trigger === null) return;
11814
+ trigger.click();
11815
+ requestAnimationFrame(() => {
11816
+ Array.from(document.querySelectorAll("button")).find((button) => button.textContent?.trim() === sectionLabel)?.click();
11817
+ });
11818
+ }
11745
11819
  /** The diff body's row class per line kind. */
11746
11820
  const ROW_CLASS = {
11747
11821
  context: PendingPanel_module_css_default.context,
11748
11822
  del: PendingPanel_module_css_default.del,
11749
11823
  add: PendingPanel_module_css_default.add
11750
11824
  };
11825
+ /** Empty failure map reused as the snapshot's canonical absent value. */
11826
+ const EMPTY_FAILED_MAP = /* @__PURE__ */ new Map();
11751
11827
  /**
11752
11828
  * One rendered diff row, memoized so a poll or an unrelated state change
11753
11829
  * does not re-render rows whose content, highlight, and focus are unchanged.
@@ -11909,7 +11985,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
11909
11985
  };
11910
11986
  }
11911
11987
  /** One row of the file list: the clickable head in the left pane. */
11912
- function PendingFileRow({ file, selected, t, onSelect }) {
11988
+ function PendingFileRow({ file, selected, failedMessage, t, onSelect }) {
11913
11989
  const stats = (0, react.useMemo)(() => computeWholeFileDiff(file.oldText, file.newText), [file.oldText, file.newText]);
11914
11990
  return (0, react_jsx_runtime.jsx)("li", {
11915
11991
  className: PendingPanel_module_css_default.row,
@@ -11938,6 +12014,11 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
11938
12014
  title: t("panel.missingHint"),
11939
12015
  children: t("panel.missing")
11940
12016
  }),
12017
+ failedMessage !== void 0 && (0, react_jsx_runtime.jsx)("span", {
12018
+ className: PendingPanel_module_css_default.rowFailed,
12019
+ title: failedMessage,
12020
+ children: t("row.failed")
12021
+ }),
11941
12022
  (0, react_jsx_runtime.jsxs)("span", {
11942
12023
  className: PendingPanel_module_css_default.rowMeta,
11943
12024
  children: [(0, react_jsx_runtime.jsx)("span", {
@@ -11954,7 +12035,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
11954
12035
  });
11955
12036
  }
11956
12037
  /** The selected file's diff, actions, jump controls, and copy toolbar. */
11957
- function PendingDiff({ file, busy, workspacePath, jumpSignal, t, onKeep, onRevert, onBlockKeep, onBlockRevert, onOpen }) {
12038
+ function PendingDiff({ file, busy, workspacePath, jumpSignal, undoFlash, failedMessage, onPasteReference, t, onKeep, onRevert, onBlockKeep, onBlockRevert, onOpen }) {
11958
12039
  const [langOverride, setLangOverride] = (0, react.useState)(void 0);
11959
12040
  const [langMenuOpen, setLangMenuOpen] = (0, react.useState)(false);
11960
12041
  const langMenuItems = (0, react.useMemo)(() => [{
@@ -12039,10 +12120,13 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
12039
12120
  const [searchQuery, setSearchQuery] = (0, react.useState)("");
12040
12121
  const [searchIndex, setSearchIndex] = (0, react.useState)(0);
12041
12122
  const searchInputRef = (0, react.useRef)(null);
12123
+ const [flashKey, setFlashKey] = (0, react.useState)(0);
12042
12124
  (0, react.useEffect)(() => {
12043
12125
  setFocus(0);
12044
12126
  setScrollTop(0);
12045
12127
  if (bodyRef.current !== null) bodyRef.current.scrollTop = 0;
12128
+ bodyRef.current?.focus();
12129
+ setFlashKey((key) => key + 1);
12046
12130
  setHoveredBlock(void 0);
12047
12131
  setSelection(void 0);
12048
12132
  setLangOverride(void 0);
@@ -12052,6 +12136,12 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
12052
12136
  setSearchQuery("");
12053
12137
  setSearchIndex(0);
12054
12138
  }, [file.id]);
12139
+ (0, react.useEffect)(() => {
12140
+ if (undoFlash === 0) return;
12141
+ setFocus(0);
12142
+ setScrollTick((tick) => tick + 1);
12143
+ setFlashKey((key) => key + 1);
12144
+ }, [undoFlash]);
12055
12145
  const blockRanges = (0, react.useMemo)(() => {
12056
12146
  return model.blocks.map((block) => blockRangesOf(model.diff.rows, block));
12057
12147
  }, [model]);
@@ -12086,7 +12176,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
12086
12176
  (0, react.useEffect)(() => {
12087
12177
  setSearchIndex(0);
12088
12178
  }, [searchQuery, file.id]);
12089
- (0, react.useEffect)(() => {
12179
+ (0, react.useLayoutEffect)(() => {
12090
12180
  const row = currentSearchRow;
12091
12181
  if (row === void 0) return;
12092
12182
  const body = bodyRef.current;
@@ -12132,7 +12222,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
12132
12222
  observer?.disconnect();
12133
12223
  };
12134
12224
  }, [file.id]);
12135
- (0, react.useEffect)(() => {
12225
+ (0, react.useLayoutEffect)(() => {
12136
12226
  if (rowCount === 0) return;
12137
12227
  const block = model.blocks[focus];
12138
12228
  if (block === void 0) return;
@@ -12161,6 +12251,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
12161
12251
  return 0;
12162
12252
  });
12163
12253
  setScrollTick((tick) => tick + 1);
12254
+ setFlashKey((key) => key + 1);
12164
12255
  };
12165
12256
  (0, react.useEffect)(() => {
12166
12257
  if (jumpSignal === 0) return;
@@ -12190,10 +12281,15 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
12190
12281
  if (selectionReference === void 0) return;
12191
12282
  if (!await (0, _deepseek_ai_dsh_client_ui_primitives.writeClipboard)(selectionReference)) return;
12192
12283
  setCopied(true);
12284
+ if (pasteOnCopyEnabled()) onPasteReference(file.sessionId, selectionReference);
12193
12285
  window.setTimeout(() => {
12194
12286
  setCopied(false);
12195
12287
  }, 1500);
12196
- }, [selectionReference]);
12288
+ }, [
12289
+ file.sessionId,
12290
+ onPasteReference,
12291
+ selectionReference
12292
+ ]);
12197
12293
  (0, react.useEffect)(() => {
12198
12294
  const onKeyDown = (event) => {
12199
12295
  if (!(event.ctrlKey || event.metaKey) || event.altKey || event.shiftKey) return;
@@ -12202,9 +12298,9 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
12202
12298
  event.preventDefault();
12203
12299
  copySelection();
12204
12300
  };
12205
- document.addEventListener("keydown", onKeyDown);
12301
+ window.addEventListener("keydown", onKeyDown, true);
12206
12302
  return () => {
12207
- document.removeEventListener("keydown", onKeyDown);
12303
+ window.removeEventListener("keydown", onKeyDown, true);
12208
12304
  };
12209
12305
  }, [copySelection]);
12210
12306
  (0, react.useEffect)(() => {
@@ -12221,8 +12317,31 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
12221
12317
  window.removeEventListener("keydown", onKeyDown, true);
12222
12318
  };
12223
12319
  }, []);
12320
+ const jumpRef = (0, react.useRef)(jump);
12321
+ jumpRef.current = jump;
12322
+ (0, react.useEffect)(() => {
12323
+ const onKeyDown = (event) => {
12324
+ if (!(event.ctrlKey || event.metaKey) || event.altKey || event.shiftKey) return;
12325
+ const key = event.key.toLowerCase();
12326
+ if (key !== "arrowup" && key !== "arrowdown") return;
12327
+ const target = event.target;
12328
+ if (target instanceof Element && target.closest("input, textarea, [contenteditable=\"true\"]") !== null) return;
12329
+ event.preventDefault();
12330
+ jumpRef.current(key === "arrowup" ? -1 : 1);
12331
+ };
12332
+ window.addEventListener("keydown", onKeyDown, true);
12333
+ return () => {
12334
+ window.removeEventListener("keydown", onKeyDown, true);
12335
+ };
12336
+ }, []);
12224
12337
  const focusedBlock = model.blocks.length > 0 ? model.blocks[focus] : void 0;
12225
12338
  const inFocusedBlock = (index) => focusedBlock !== void 0 && index >= focusedBlock.start && index <= focusedBlock.end;
12339
+ const flashWidth = (() => {
12340
+ const scroller = bodyRef.current;
12341
+ if (scroller === null) return 0;
12342
+ const scrollbarWidth = scroller.offsetWidth - scroller.clientWidth;
12343
+ return Math.max(0, scroller.clientWidth - (scrollbarWidth > 0 ? 0 : OVERVIEW_RULER_WIDTH_PX));
12344
+ })();
12226
12345
  return (0, react_jsx_runtime.jsxs)("div", {
12227
12346
  className: PendingPanel_module_css_default.diff,
12228
12347
  "data-diff-approval-diff": true,
@@ -12327,26 +12446,31 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
12327
12446
  (0, react_jsx_runtime.jsx)("span", { className: PendingPanel_module_css_default.flexSpacer }),
12328
12447
  (0, react_jsx_runtime.jsx)("button", {
12329
12448
  type: "button",
12330
- className: `${PendingPanel_module_css_default.action} ${PendingPanel_module_css_default.actionPrimary}`,
12449
+ className: `${PendingPanel_module_css_default.action} ${PendingPanel_module_css_default.actionPrimary} ${PendingPanel_module_css_default.actionQuietDisabled}`,
12331
12450
  "data-diff-keep": true,
12332
12451
  disabled: busy,
12333
12452
  onClick: () => {
12334
12453
  onKeep(file.sessionId, file.id);
12335
12454
  },
12336
- children: busy ? t("action.busy") : t("action.keep")
12455
+ children: t("action.keep")
12337
12456
  }),
12338
12457
  (0, react_jsx_runtime.jsx)("button", {
12339
12458
  type: "button",
12340
- className: PendingPanel_module_css_default.action,
12459
+ className: `${PendingPanel_module_css_default.action} ${PendingPanel_module_css_default.actionQuietDisabled}`,
12341
12460
  "data-diff-revert": true,
12342
12461
  disabled: busy,
12343
12462
  onClick: () => {
12344
12463
  onRevert(file.sessionId, file.id);
12345
12464
  },
12346
- children: busy ? t("action.busy") : t("action.revert")
12465
+ children: t("action.revert")
12347
12466
  })
12348
12467
  ]
12349
12468
  }),
12469
+ failedMessage !== void 0 && (0, react_jsx_runtime.jsx)("p", {
12470
+ className: PendingPanel_module_css_default.actionError,
12471
+ "data-diff-action-error": true,
12472
+ children: failedMessage
12473
+ }),
12350
12474
  file.missing && (0, react_jsx_runtime.jsx)("p", {
12351
12475
  className: PendingPanel_module_css_default.missingHint,
12352
12476
  children: t("panel.missingHint")
@@ -12357,6 +12481,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
12357
12481
  (0, react_jsx_runtime.jsxs)("div", {
12358
12482
  className: PendingPanel_module_css_default.diffBody,
12359
12483
  ref: bodyRef,
12484
+ tabIndex: 0,
12360
12485
  onScroll,
12361
12486
  onMouseLeave: () => {
12362
12487
  setHoveredBlock(void 0);
@@ -12430,6 +12555,15 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
12430
12555
  ]
12431
12556
  })]
12432
12557
  }),
12558
+ focusedBlock !== void 0 && flashKey > 0 && (0, react_jsx_runtime.jsx)("div", {
12559
+ className: PendingPanel_module_css_default.blockFlash,
12560
+ "data-diff-block-flash": true,
12561
+ style: {
12562
+ top: focusedBlock.start * ROW_HEIGHT_PX - scrollTop,
12563
+ height: Math.min((focusedBlock.end - focusedBlock.start + 1) * ROW_HEIGHT_PX, viewportHeight > 0 ? viewportHeight : Number.POSITIVE_INFINITY),
12564
+ width: flashWidth
12565
+ }
12566
+ }, flashKey),
12433
12567
  searchOpen && (0, react_jsx_runtime.jsxs)("div", {
12434
12568
  className: PendingPanel_module_css_default.searchBar,
12435
12569
  "data-diff-searchbar": true,
@@ -12563,14 +12697,34 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
12563
12697
  });
12564
12698
  }
12565
12699
  /** Render the pending-edit review panel and its unified footer action. */
12566
- function PendingPanel({ wide, useSessions, usePending, onRefresh, onKeep, onRevert, onBlockKeep, onBlockRevert, onOpen, t }) {
12700
+ function PendingPanel({ wide, useSessions, usePending, onRefresh, onKeep, onRevert, onBlockKeep, onBlockRevert, onOpen, onPasteReference, onUndo, onRedo, onImportVcs, t }) {
12567
12701
  const current = useSessions((state) => state.current);
12702
+ const currentBlank = useSessions((state) => {
12703
+ const id = state.current;
12704
+ return id === void 0 ? false : state.byId[id]?.blank ?? false;
12705
+ });
12706
+ const noSession = current === void 0 || currentBlank;
12568
12707
  const snapshot = usePending((snapshot) => snapshot);
12569
12708
  const [open, setOpen] = (0, react.useState)(false);
12570
12709
  const [selected, setSelected] = (0, react.useState)("");
12571
12710
  /** Bumped when the already-open file is clicked again, to jump to the next
12572
12711
  * diff block in the open file's detail pane. */
12573
12712
  const [jumpSignal, setJumpSignal] = (0, react.useState)(0);
12713
+ /** Bumped when an undo/redo affected the already-open file, so its detail
12714
+ * pane re-selects the undone diff (flash). */
12715
+ const [undoFlash, setUndoFlash] = (0, react.useState)(0);
12716
+ /** Which bulk decision (keep-all / revert-all) is running; null when idle. */
12717
+ const [bulkBusy, setBulkBusy] = (0, react.useState)(null);
12718
+ /** True while a workspace-changes import is running. */
12719
+ const [importBusy, setImportBusy] = (0, react.useState)(false);
12720
+ /** Feedback under the empty note after an import (no VCS, or a failure). */
12721
+ const [importNote, setImportNote] = (0, react.useState)(void 0);
12722
+ /** Whether the last import note is a failure (drives the alert role). */
12723
+ const [importFailed, setImportFailed] = (0, react.useState)(false);
12724
+ /** A transient banner for an import that found nothing to bring in. */
12725
+ const [importToast, setImportToast] = (0, react.useState)(null);
12726
+ /** A transient banner for a keep/revert failure. */
12727
+ const [actionToast, setActionToast] = (0, react.useState)(null);
12574
12728
  /** Pending count captured at open time; auto-close needs a list that emptied. */
12575
12729
  const [openedCount, setOpenedCount] = (0, react.useState)(0);
12576
12730
  /** Bottom offset tracking the chat composer's top edge so the input stays visible. */
@@ -12633,6 +12787,22 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
12633
12787
  };
12634
12788
  }, [open]);
12635
12789
  const files = snapshot.files.filter((file) => file.sessionId === current);
12790
+ /** Per-file keep/revert failures, surfaced inline on the row and detail. */
12791
+ const failed = snapshot.failed ?? EMPTY_FAILED_MAP;
12792
+ const failedInitialized = (0, react.useRef)(false);
12793
+ const failedRef = (0, react.useRef)(void 0);
12794
+ (0, react.useEffect)(() => {
12795
+ const current = snapshot.failed;
12796
+ if (!failedInitialized.current) {
12797
+ failedInitialized.current = true;
12798
+ failedRef.current = current;
12799
+ return;
12800
+ }
12801
+ const previous = failedRef.current;
12802
+ const fresh = [...(current ?? EMPTY_FAILED_MAP).entries()].filter(([id]) => previous === void 0 || !previous.has(id));
12803
+ if (fresh.length > 0) setActionToast(fresh[0][1]);
12804
+ failedRef.current = current;
12805
+ }, [snapshot.failed]);
12636
12806
  (0, react.useEffect)(() => {
12637
12807
  if (!open) return;
12638
12808
  if (selected !== "" && files.some((file) => file.id === selected)) return;
@@ -12645,10 +12815,15 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
12645
12815
  selected
12646
12816
  ]);
12647
12817
  (0, react.useEffect)(() => {
12648
- if (open && openedCount > 0 && snapshot.read && snapshot.error === void 0 && files.length === 0) {
12818
+ if (!open || openedCount === 0) return;
12819
+ if (!(snapshot.read && snapshot.error === void 0 && files.length === 0)) return;
12820
+ const timer = window.setTimeout(() => {
12649
12821
  setOpenedCount(0);
12650
12822
  setOpen(false);
12651
- }
12823
+ }, EMPTY_CLOSE_GRACE_MS);
12824
+ return () => {
12825
+ window.clearTimeout(timer);
12826
+ };
12652
12827
  }, [
12653
12828
  open,
12654
12829
  openedCount,
@@ -12656,13 +12831,45 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
12656
12831
  snapshot.error,
12657
12832
  files.length
12658
12833
  ]);
12834
+ const runImportVcs = async () => {
12835
+ if (current === void 0 || importBusy) return;
12836
+ setImportBusy(true);
12837
+ setImportNote(void 0);
12838
+ setImportFailed(false);
12839
+ setImportToast(null);
12840
+ try {
12841
+ const value = await onImportVcs(current, includeUntrackedEnabled());
12842
+ await onRefresh(current);
12843
+ if (!value.detected) setImportNote(t("panel.importNoVcs"));
12844
+ else if (value.imported === 0) setImportToast(t("panel.importNone"));
12845
+ } catch (error) {
12846
+ setImportFailed(true);
12847
+ setImportNote(t("panel.importFailed", { message: error instanceof Error ? error.message : String(error) }));
12848
+ } finally {
12849
+ setImportBusy(false);
12850
+ }
12851
+ };
12659
12852
  const toggleOpen = () => {
12660
12853
  if (!open) setOpenedCount(files.length);
12661
12854
  setOpen((value) => !value);
12662
12855
  };
12856
+ (0, react.useEffect)(() => {
12857
+ if (noSession) setOpen(false);
12858
+ }, [noSession]);
12859
+ /** Run the same decision over every current-session file, sequentially. */
12860
+ const runBulk = async (kind) => {
12861
+ setBulkBusy(kind);
12862
+ try {
12863
+ for (const file of files) if (kind === "keep") await onKeep(file.sessionId, file.id);
12864
+ else await onRevert(file.sessionId, file.id);
12865
+ } finally {
12866
+ setBulkBusy(null);
12867
+ }
12868
+ };
12663
12869
  const renderEntry = (entry) => (0, react_jsx_runtime.jsx)(PendingFileRow, {
12664
12870
  file: entry,
12665
12871
  selected: selected === entry.id,
12872
+ failedMessage: failed.get(entry.id),
12666
12873
  t,
12667
12874
  onSelect: (id) => {
12668
12875
  if (id === selected) setJumpSignal((signal) => signal + 1);
@@ -12670,6 +12877,42 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
12670
12877
  }
12671
12878
  }, entry.id);
12672
12879
  const selectedFile = files.find((file) => file.id === selected);
12880
+ const handleUndo = async (sessionId) => {
12881
+ const id = await onUndo(sessionId);
12882
+ if (id === void 0) return;
12883
+ if (id === selected) setUndoFlash((signal) => signal + 1);
12884
+ else setSelected(id);
12885
+ };
12886
+ const handleRedo = async (sessionId) => {
12887
+ const id = await onRedo(sessionId);
12888
+ if (id === void 0) return;
12889
+ if (id === selected) setUndoFlash((signal) => signal + 1);
12890
+ else setSelected(id);
12891
+ };
12892
+ (0, react.useEffect)(() => {
12893
+ if (!open || current === void 0) return;
12894
+ const onKeyDown = (event) => {
12895
+ if (!(event.ctrlKey || event.metaKey) || event.altKey) return;
12896
+ const key = event.key.toLowerCase();
12897
+ if (key !== "z" && key !== "y") return;
12898
+ const target = event.target;
12899
+ if (target instanceof Element && target.closest("input, textarea, [contenteditable=\"true\"]") !== null) return;
12900
+ event.preventDefault();
12901
+ if (key === "z") {
12902
+ if (event.shiftKey) handleRedo(current);
12903
+ else handleUndo(current);
12904
+ } else handleRedo(current);
12905
+ };
12906
+ window.addEventListener("keydown", onKeyDown, true);
12907
+ return () => {
12908
+ window.removeEventListener("keydown", onKeyDown, true);
12909
+ };
12910
+ }, [
12911
+ open,
12912
+ current,
12913
+ handleUndo,
12914
+ handleRedo
12915
+ ]);
12673
12916
  /** Drag the list/detail divider; width follows the pointer within its bounds. */
12674
12917
  const startResize = (event) => {
12675
12918
  if (event.button !== 0) return;
@@ -12698,127 +12941,316 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
12698
12941
  };
12699
12942
  return (0, react_jsx_runtime.jsxs)("div", {
12700
12943
  className: wide ? PendingPanel_module_css_default.layer : `${PendingPanel_module_css_default.layer} ${PendingPanel_module_css_default.rail}`,
12701
- children: [open && (0, react_jsx_runtime.jsxs)("section", {
12702
- className: PendingPanel_module_css_default.panel,
12703
- style: { bottom: expanded ? PANEL_INSET_PX : bottomPx },
12704
- "data-diff-approval-panel": true,
12705
- "aria-label": t("panel.title"),
12706
- children: [(0, react_jsx_runtime.jsxs)("header", {
12707
- className: PendingPanel_module_css_default.header,
12708
- children: [(0, react_jsx_runtime.jsx)("span", {
12709
- className: PendingPanel_module_css_default.title,
12710
- children: t("panel.title")
12711
- }), (0, react_jsx_runtime.jsxs)("div", {
12712
- className: PendingPanel_module_css_default.headerActions,
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 })
12944
+ children: [
12945
+ importToast !== null && (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Toast, {
12946
+ text: importToast,
12947
+ onDone: () => {
12948
+ setImportToast(null);
12949
+ }
12950
+ }),
12951
+ actionToast !== null && (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Toast, {
12952
+ text: actionToast,
12953
+ onDone: () => {
12954
+ setActionToast(null);
12955
+ }
12956
+ }),
12957
+ open && expanded && (0, react_jsx_runtime.jsx)("div", {
12958
+ className: PendingPanel_module_css_default.fullscreenBackdrop,
12959
+ "data-diff-fullscreen-backdrop": true
12960
+ }),
12961
+ open && (0, react_jsx_runtime.jsxs)("section", {
12962
+ className: PendingPanel_module_css_default.panel,
12963
+ style: { bottom: expanded ? PANEL_INSET_PX : bottomPx },
12964
+ "data-diff-approval-panel": true,
12965
+ "aria-label": t("panel.title"),
12966
+ children: [(0, react_jsx_runtime.jsxs)("header", {
12967
+ className: PendingPanel_module_css_default.header,
12968
+ children: [(0, react_jsx_runtime.jsx)("span", {
12969
+ className: PendingPanel_module_css_default.title,
12970
+ children: t("panel.title")
12971
+ }), (0, react_jsx_runtime.jsxs)("div", {
12972
+ className: PendingPanel_module_css_default.headerActions,
12973
+ children: [
12974
+ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
12975
+ label: t("action.settings"),
12976
+ side: "bottom",
12977
+ delayMs: 500,
12978
+ children: (0, react_jsx_runtime.jsx)("button", {
12979
+ type: "button",
12980
+ className: PendingPanel_module_css_default.expand,
12981
+ "data-diff-approval-settings": true,
12982
+ "aria-label": t("action.settings"),
12983
+ onClick: () => {
12984
+ setOpen(false);
12985
+ openSettingsSection(t("settings.tabLabel"));
12986
+ },
12987
+ children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconSettingsOutline16, { size: 14 })
12988
+ })
12989
+ }),
12990
+ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
12991
+ label: t(expanded ? "action.exitFullscreen" : "action.expand"),
12992
+ side: "bottom",
12993
+ delayMs: 500,
12994
+ children: (0, react_jsx_runtime.jsx)("button", {
12995
+ type: "button",
12996
+ className: expanded ? `${PendingPanel_module_css_default.expand} ${PendingPanel_module_css_default.expandExpanded}` : PendingPanel_module_css_default.expand,
12997
+ "data-diff-approval-expand": true,
12998
+ "aria-label": t(expanded ? "action.exitFullscreen" : "action.expand"),
12999
+ onClick: () => {
13000
+ setExpanded((value) => !value);
13001
+ },
13002
+ children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconFullscreenOutline16, { size: 14 })
13003
+ })
13004
+ }),
13005
+ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
13006
+ label: t("action.close"),
13007
+ side: "bottom",
13008
+ delayMs: 500,
13009
+ children: (0, react_jsx_runtime.jsx)("button", {
13010
+ type: "button",
13011
+ className: PendingPanel_module_css_default.close,
13012
+ "data-diff-approval-close": true,
13013
+ "aria-label": t("action.close"),
13014
+ onClick: () => {
13015
+ setOpen(false);
13016
+ },
13017
+ children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCloseOutline16, { size: 14 })
13018
+ })
13019
+ })
13020
+ ]
13021
+ })]
13022
+ }), snapshot.error !== void 0 || !snapshot.read || files.length === 0 ? (0, react_jsx_runtime.jsxs)("div", {
13023
+ className: PendingPanel_module_css_default.states,
13024
+ children: [
13025
+ snapshot.error !== void 0 && (0, react_jsx_runtime.jsx)("p", {
13026
+ className: PendingPanel_module_css_default.readError,
13027
+ role: "alert",
13028
+ children: t("panel.readFailed", { message: snapshot.error })
13029
+ }),
13030
+ !snapshot.read && snapshot.error === void 0 && (0, react_jsx_runtime.jsx)("p", {
13031
+ className: PendingPanel_module_css_default.note,
13032
+ children: t("panel.loading")
13033
+ }),
13034
+ snapshot.read && snapshot.error === void 0 && files.length === 0 && (0, react_jsx_runtime.jsxs)("div", {
13035
+ className: PendingPanel_module_css_default.emptyState,
13036
+ children: [
13037
+ (0, react_jsx_runtime.jsx)("p", {
13038
+ className: `${PendingPanel_module_css_default.note} ${PendingPanel_module_css_default.noteCentered}`,
13039
+ children: t("panel.empty")
13040
+ }),
13041
+ (0, react_jsx_runtime.jsx)("button", {
13042
+ type: "button",
13043
+ className: PendingPanel_module_css_default.importButton,
13044
+ "data-diff-import-vcs": true,
13045
+ disabled: importBusy,
13046
+ onClick: () => {
13047
+ runImportVcs();
13048
+ },
13049
+ children: importBusy ? t("action.importVcsBusy") : t("action.importVcs")
13050
+ }),
13051
+ importNote !== void 0 && (0, react_jsx_runtime.jsx)("p", {
13052
+ className: PendingPanel_module_css_default.importNote,
13053
+ role: importFailed ? "alert" : void 0,
13054
+ children: importNote
13055
+ })
13056
+ ]
12726
13057
  })
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 })
13058
+ ]
13059
+ }) : (0, react_jsx_runtime.jsxs)("div", {
13060
+ className: PendingPanel_module_css_default.split,
13061
+ children: [
13062
+ (0, react_jsx_runtime.jsxs)("nav", {
13063
+ className: PendingPanel_module_css_default.fileList,
13064
+ style: { width: listWidth },
13065
+ "data-diff-approval-file-list": true,
13066
+ children: [(0, react_jsx_runtime.jsx)("div", {
13067
+ className: PendingPanel_module_css_default.listScroll,
13068
+ children: files.length > 0 && (0, react_jsx_runtime.jsxs)("section", { children: [(0, react_jsx_runtime.jsx)("h3", {
13069
+ className: PendingPanel_module_css_default.group,
13070
+ children: t("panel.group.current")
13071
+ }), (0, react_jsx_runtime.jsx)("ul", {
13072
+ className: PendingPanel_module_css_default.rows,
13073
+ children: files.map(renderEntry)
13074
+ })] })
13075
+ }), files.length > 0 && (0, react_jsx_runtime.jsxs)("div", {
13076
+ className: PendingPanel_module_css_default.bulkActions,
13077
+ children: [(0, react_jsx_runtime.jsx)("button", {
13078
+ type: "button",
13079
+ className: `${PendingPanel_module_css_default.action} ${PendingPanel_module_css_default.actionPrimary}`,
13080
+ "data-diff-keep-all": true,
13081
+ disabled: bulkBusy !== null,
13082
+ onClick: () => {
13083
+ runBulk("keep");
13084
+ },
13085
+ children: bulkBusy === "keep" ? t("action.busy") : t("action.keepAll")
13086
+ }), (0, react_jsx_runtime.jsx)("button", {
13087
+ type: "button",
13088
+ className: PendingPanel_module_css_default.action,
13089
+ "data-diff-revert-all": true,
13090
+ disabled: bulkBusy !== null,
13091
+ onClick: () => {
13092
+ runBulk("revert");
13093
+ },
13094
+ children: bulkBusy === "revert" ? t("action.busy") : t("action.revertAll")
13095
+ })]
13096
+ })]
13097
+ }),
13098
+ (0, react_jsx_runtime.jsx)("div", {
13099
+ className: PendingPanel_module_css_default.resizeHandle,
13100
+ "data-diff-resize": true,
13101
+ onMouseDown: startResize
13102
+ }),
13103
+ (0, react_jsx_runtime.jsx)("div", {
13104
+ className: PendingPanel_module_css_default.detail,
13105
+ children: selectedFile === void 0 ? (0, react_jsx_runtime.jsx)("p", {
13106
+ className: PendingPanel_module_css_default.detailEmpty,
13107
+ children: t("panel.selectHint")
13108
+ }) : (0, react_jsx_runtime.jsx)(PendingDiff, {
13109
+ file: selectedFile,
13110
+ busy: snapshot.busy.has(selectedFile.id),
13111
+ workspacePath: snapshot.workspacePath,
13112
+ jumpSignal,
13113
+ undoFlash,
13114
+ failedMessage: failed.get(selectedFile.id),
13115
+ onPasteReference,
13116
+ t,
13117
+ onKeep,
13118
+ onRevert,
13119
+ onBlockKeep,
13120
+ onBlockRevert,
13121
+ onOpen
13122
+ })
12740
13123
  })
12741
- })]
13124
+ ]
12742
13125
  })]
12743
- }), snapshot.error !== void 0 || !snapshot.read || files.length === 0 ? (0, react_jsx_runtime.jsxs)("div", {
12744
- className: PendingPanel_module_css_default.states,
12745
- children: [
12746
- snapshot.error !== void 0 && (0, react_jsx_runtime.jsx)("p", {
12747
- className: PendingPanel_module_css_default.readError,
12748
- role: "alert",
12749
- children: t("panel.readFailed", { message: snapshot.error })
12750
- }),
12751
- !snapshot.read && snapshot.error === void 0 && (0, react_jsx_runtime.jsx)("p", {
12752
- className: PendingPanel_module_css_default.note,
12753
- children: t("panel.loading")
12754
- }),
12755
- snapshot.read && snapshot.error === void 0 && files.length === 0 && (0, react_jsx_runtime.jsx)("p", {
12756
- className: PendingPanel_module_css_default.note,
12757
- children: t("panel.empty")
12758
- })
12759
- ]
12760
- }) : (0, react_jsx_runtime.jsxs)("div", {
12761
- className: PendingPanel_module_css_default.split,
12762
- children: [
12763
- (0, react_jsx_runtime.jsx)("nav", {
12764
- className: PendingPanel_module_css_default.fileList,
12765
- style: { width: listWidth },
12766
- "data-diff-approval-file-list": true,
12767
- children: files.length > 0 && (0, react_jsx_runtime.jsxs)("section", { children: [(0, react_jsx_runtime.jsx)("h3", {
12768
- className: PendingPanel_module_css_default.group,
12769
- children: t("panel.group.current")
12770
- }), (0, react_jsx_runtime.jsx)("ul", {
12771
- className: PendingPanel_module_css_default.rows,
12772
- children: files.map(renderEntry)
12773
- })] })
12774
- }),
12775
- (0, react_jsx_runtime.jsx)("div", {
12776
- className: PendingPanel_module_css_default.resizeHandle,
12777
- "data-diff-resize": true,
12778
- onMouseDown: startResize
12779
- }),
12780
- (0, react_jsx_runtime.jsx)("div", {
12781
- className: PendingPanel_module_css_default.detail,
12782
- children: selectedFile === void 0 ? (0, react_jsx_runtime.jsx)("p", {
12783
- className: PendingPanel_module_css_default.detailEmpty,
12784
- children: t("panel.selectHint")
12785
- }) : (0, react_jsx_runtime.jsx)(PendingDiff, {
12786
- file: selectedFile,
12787
- busy: snapshot.busy.has(selectedFile.id),
12788
- workspacePath: snapshot.workspacePath,
12789
- jumpSignal,
12790
- t,
12791
- onKeep,
12792
- onRevert,
12793
- onBlockKeep,
12794
- onBlockRevert,
12795
- onOpen
13126
+ }),
13127
+ (0, react_jsx_runtime.jsx)("div", {
13128
+ className: PendingPanel_module_css_default.footerButtons,
13129
+ children: (0, react_jsx_runtime.jsxs)("button", {
13130
+ type: "button",
13131
+ className: noSession ? `${PendingPanel_module_css_default.badge} ${PendingPanel_module_css_default.badgeDisabled}` : PendingPanel_module_css_default.badge,
13132
+ "data-diff-approval-badge": files.length,
13133
+ "data-active": open ? "" : void 0,
13134
+ "aria-label": t("panel.aria"),
13135
+ "aria-expanded": open,
13136
+ disabled: noSession,
13137
+ onClick: toggleOpen,
13138
+ children: [
13139
+ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconListPenOutline16, { size: wide ? 16 : 18 }),
13140
+ wide && (0, react_jsx_runtime.jsx)("span", {
13141
+ className: PendingPanel_module_css_default.badgeLabel,
13142
+ children: t("panel.aria")
13143
+ }),
13144
+ (wide || files.length > 0) && (0, react_jsx_runtime.jsx)("span", {
13145
+ className: PendingPanel_module_css_default.badgeCount,
13146
+ children: files.length
12796
13147
  })
12797
- })
12798
- ]
12799
- })]
12800
- }), (0, react_jsx_runtime.jsx)("div", {
12801
- className: PendingPanel_module_css_default.footerButtons,
12802
- children: (0, react_jsx_runtime.jsxs)("button", {
12803
- type: "button",
12804
- className: PendingPanel_module_css_default.badge,
12805
- "data-diff-approval-badge": files.length,
12806
- "data-active": open ? "" : void 0,
12807
- "aria-label": t("panel.aria"),
12808
- "aria-expanded": open,
12809
- onClick: toggleOpen,
12810
- children: [
12811
- (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconListPenOutline16, { size: wide ? 16 : 18 }),
12812
- wide && (0, react_jsx_runtime.jsx)("span", {
12813
- className: PendingPanel_module_css_default.badgeLabel,
12814
- children: t("panel.aria")
12815
- }),
12816
- (wide || files.length > 0) && (0, react_jsx_runtime.jsx)("span", {
12817
- className: PendingPanel_module_css_default.badgeCount,
12818
- children: files.length
12819
- })
12820
- ]
13148
+ ]
13149
+ })
12821
13150
  })
13151
+ ]
13152
+ });
13153
+ }
13154
+ //#endregion
13155
+ //#region lib/types/client/SettingsTab.js
13156
+ /** DSH Settings top-level section for this plugin's preferences. */
13157
+ /** A pill picker offering the two boolean states 打开 / 关闭. */
13158
+ function OnOffPicker({ value, open, onOpenChange, onSelect, dataAttribute, t }) {
13159
+ return (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Menu, {
13160
+ open,
13161
+ onClose: () => {
13162
+ onOpenChange(false);
13163
+ },
13164
+ items: [{
13165
+ id: "on",
13166
+ label: t("action.toggleOn")
13167
+ }, {
13168
+ id: "off",
13169
+ label: t("action.toggleOff")
13170
+ }],
13171
+ selectedId: value ? "on" : "off",
13172
+ onSelect: (id) => {
13173
+ onOpenChange(false);
13174
+ onSelect(id === "on");
13175
+ },
13176
+ align: "end",
13177
+ portal: true,
13178
+ anchor: (0, react_jsx_runtime.jsxs)("button", {
13179
+ type: "button",
13180
+ className: PendingPanel_module_css_default.settingsSelector,
13181
+ "aria-haspopup": "menu",
13182
+ "aria-expanded": open,
13183
+ onClick: () => {
13184
+ onOpenChange(!open);
13185
+ },
13186
+ [dataAttribute]: true,
13187
+ children: [value ? t("action.toggleOn") : t("action.toggleOff"), (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, { className: PendingPanel_module_css_default.settingsSelectorChevron })]
13188
+ })
13189
+ });
13190
+ }
13191
+ /** One Agent-preset-style preference row: title + description, pill picker right. */
13192
+ function PreferenceRow({ title, description, value, open, onOpenChange, onSelect, dataAttribute, t }) {
13193
+ return (0, react_jsx_runtime.jsxs)("div", {
13194
+ className: PendingPanel_module_css_default.settingsRow,
13195
+ children: [(0, react_jsx_runtime.jsxs)("div", {
13196
+ className: PendingPanel_module_css_default.settingsRowText,
13197
+ children: [(0, react_jsx_runtime.jsx)("div", {
13198
+ className: PendingPanel_module_css_default.settingsRowTitle,
13199
+ children: title
13200
+ }), (0, react_jsx_runtime.jsx)("div", {
13201
+ className: PendingPanel_module_css_default.settingsRowDesc,
13202
+ children: description
13203
+ })]
13204
+ }), (0, react_jsx_runtime.jsx)(OnOffPicker, {
13205
+ value,
13206
+ open,
13207
+ onOpenChange,
13208
+ onSelect,
13209
+ dataAttribute,
13210
+ t
13211
+ })]
13212
+ });
13213
+ }
13214
+ /**
13215
+ * The plugin's preferences: auto-paste a copied reference into the input, and
13216
+ * whether importing workspace VCS changes includes untracked files. Each row
13217
+ * mirrors the harness's Agent-preset row (title + description on the left, a
13218
+ * pill picker on the right) with the two boolean states 打开 / 关闭.
13219
+ */
13220
+ function DiffApprovalSettingsTab({ t }) {
13221
+ const [pasteOnCopy, setPasteOnCopyState] = (0, react.useState)(pasteOnCopyEnabled);
13222
+ const [pasteOnCopyOpen, setPasteOnCopyOpen] = (0, react.useState)(false);
13223
+ const [includeUntracked, setIncludeUntrackedState] = (0, react.useState)(includeUntrackedEnabled);
13224
+ const [includeUntrackedOpen, setIncludeUntrackedOpen] = (0, react.useState)(false);
13225
+ const setPasteOnCopy = (value) => {
13226
+ setPasteOnCopyState(value);
13227
+ setPasteOnCopyEnabled(value);
13228
+ };
13229
+ const setIncludeUntracked = (value) => {
13230
+ setIncludeUntrackedState(value);
13231
+ setIncludeUntrackedEnabled(value);
13232
+ };
13233
+ return (0, react_jsx_runtime.jsxs)("div", {
13234
+ className: PendingPanel_module_css_default.settingsPage,
13235
+ "data-diff-settings": true,
13236
+ children: [(0, react_jsx_runtime.jsx)(PreferenceRow, {
13237
+ title: t("panel.pasteOnCopy"),
13238
+ description: t("panel.pasteOnCopyDesc"),
13239
+ value: pasteOnCopy,
13240
+ open: pasteOnCopyOpen,
13241
+ onOpenChange: setPasteOnCopyOpen,
13242
+ onSelect: setPasteOnCopy,
13243
+ dataAttribute: "data-diff-paste-on-copy-select",
13244
+ t
13245
+ }), (0, react_jsx_runtime.jsx)(PreferenceRow, {
13246
+ title: t("panel.importUntracked"),
13247
+ description: t("panel.importUntrackedDesc"),
13248
+ value: includeUntracked,
13249
+ open: includeUntrackedOpen,
13250
+ onOpenChange: setIncludeUntrackedOpen,
13251
+ onSelect: setIncludeUntracked,
13252
+ dataAttribute: "data-diff-import-untracked-select",
13253
+ t
12822
13254
  })]
12823
13255
  });
12824
13256
  }
@@ -12867,6 +13299,18 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
12867
13299
  block
12868
13300
  }));
12869
13301
  },
13302
+ async undo(sessionId) {
13303
+ return actionOf(await rpc.call(DIFF_APPROVAL_CHANNEL, "undo", { sessionId }));
13304
+ },
13305
+ async redo(sessionId) {
13306
+ return actionOf(await rpc.call(DIFF_APPROVAL_CHANNEL, "redo", { sessionId }));
13307
+ },
13308
+ async importVcs(sessionId, includeUntracked) {
13309
+ return importValueOf(await rpc.call(DIFF_APPROVAL_CHANNEL, "vcs-import", {
13310
+ sessionId,
13311
+ includeUntracked
13312
+ }));
13313
+ },
12870
13314
  async open(sessionId, id, action) {
12871
13315
  return openOf(await rpc.call(DIFF_APPROVAL_CHANNEL, "open", {
12872
13316
  sessionId,
@@ -12923,8 +13367,25 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
12923
13367
  const value = result.value;
12924
13368
  if (typeof value !== "object" || value === null) throw new Error("the action returned a malformed value");
12925
13369
  const outcome = value.outcome;
12926
- if (outcome !== "kept" && outcome !== "reverted" && outcome !== "missing") throw new Error("the action returned a malformed outcome");
12927
- return { outcome };
13370
+ if (outcome !== "kept" && outcome !== "reverted" && outcome !== "missing" && outcome !== "undone" && outcome !== "redone" && outcome !== "nothing") throw new Error("the action returned a malformed outcome");
13371
+ const id = value.id;
13372
+ return {
13373
+ outcome,
13374
+ id: typeof id === "string" && id.length > 0 ? id : void 0
13375
+ };
13376
+ }
13377
+ /** Narrow the vcs-import endpoint's value; a malformed wire value is a failure. */
13378
+ function importValueOf(result) {
13379
+ if (!result.ok) throw new Error(`${result.error.code}: ${result.error.message}`);
13380
+ const value = result.value;
13381
+ if (typeof value !== "object" || value === null) throw new Error("the import returned a malformed value");
13382
+ const imported = value.imported;
13383
+ const detected = value.detected;
13384
+ if (typeof imported !== "number" || typeof detected !== "boolean") throw new Error("the import returned a malformed value");
13385
+ return {
13386
+ imported,
13387
+ detected
13388
+ };
12928
13389
  }
12929
13390
  /** Narrow the open endpoint's value; a malformed wire value is an open failure. */
12930
13391
  function openOf(result) {
@@ -12946,6 +13407,10 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
12946
13407
  */
12947
13408
  /** An empty busy set reused as the snapshot's canonical absent value. */
12948
13409
  const EMPTY_BUSY = /* @__PURE__ */ new Set();
13410
+ /** An empty failure map reused as the snapshot's canonical absent value. */
13411
+ const EMPTY_FAILED = /* @__PURE__ */ new Map();
13412
+ /** How long a failed keep/revert hint stays visible before it auto-clears. */
13413
+ const FAILED_HINT_MS = 5e3;
12949
13414
  /**
12950
13415
  * Create the store over the review-channel port.
12951
13416
  * @param port - the typed channel port.
@@ -12962,22 +13427,51 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
12962
13427
  snapshot = next;
12963
13428
  for (const listener of [...listeners]) listener();
12964
13429
  };
13430
+ const failedOf = (value) => value.failed ?? EMPTY_FAILED;
13431
+ /** Record one entry's failed keep/revert with its message; auto-clears after a few seconds. */
13432
+ const markFailed = (id, message) => {
13433
+ publish({
13434
+ ...snapshot,
13435
+ failed: new Map([...failedOf(snapshot), [id, message]])
13436
+ });
13437
+ window.setTimeout(() => {
13438
+ if (failedOf(snapshot).get(id) !== message) return;
13439
+ const next = new Map(failedOf(snapshot));
13440
+ next.delete(id);
13441
+ publish({
13442
+ ...snapshot,
13443
+ failed: next
13444
+ });
13445
+ }, FAILED_HINT_MS);
13446
+ };
13447
+ /** Drop one entry's failure marker after a successful retry. */
13448
+ const clearFailed = (id) => {
13449
+ if (!failedOf(snapshot).has(id)) return;
13450
+ const next = new Map(failedOf(snapshot));
13451
+ next.delete(id);
13452
+ publish({
13453
+ ...snapshot,
13454
+ failed: next
13455
+ });
13456
+ };
12965
13457
  const withBusy = async (id, action) => {
12966
13458
  const { error: _cleared, ...base } = snapshot;
12967
13459
  publish({
12968
13460
  ...base,
13461
+ failed: failedOf(snapshot),
12969
13462
  busy: /* @__PURE__ */ new Set([...snapshot.busy, id])
12970
13463
  });
12971
13464
  try {
12972
13465
  await action();
12973
13466
  } catch (error) {
13467
+ markFailed(id, error instanceof Error ? error.message : String(error));
12974
13468
  publish({
12975
13469
  ...snapshot,
12976
- error: error instanceof Error ? error.message : String(error),
12977
13470
  busy: new Set([...snapshot.busy].filter((busy) => busy !== id))
12978
13471
  });
12979
13472
  return;
12980
13473
  }
13474
+ clearFailed(id);
12981
13475
  publish({
12982
13476
  ...snapshot,
12983
13477
  files: snapshot.files.filter((file) => file.id !== id),
@@ -12997,7 +13491,8 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
12997
13491
  publish({
12998
13492
  read: true,
12999
13493
  files: [],
13000
- busy: EMPTY_BUSY
13494
+ busy: EMPTY_BUSY,
13495
+ failed: failedOf(snapshot)
13001
13496
  });
13002
13497
  return;
13003
13498
  }
@@ -13007,7 +13502,8 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
13007
13502
  read: true,
13008
13503
  files,
13009
13504
  workspacePath,
13010
- busy: EMPTY_BUSY
13505
+ busy: EMPTY_BUSY,
13506
+ failed: failedOf(snapshot)
13011
13507
  });
13012
13508
  } catch (error) {
13013
13509
  publish({
@@ -13015,7 +13511,8 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
13015
13511
  files: snapshot.files,
13016
13512
  workspacePath: snapshot.workspacePath,
13017
13513
  error: error instanceof Error ? error.message : String(error),
13018
- busy: snapshot.busy
13514
+ busy: snapshot.busy,
13515
+ failed: failedOf(snapshot)
13019
13516
  });
13020
13517
  }
13021
13518
  },
@@ -13038,13 +13535,14 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
13038
13535
  try {
13039
13536
  await port.blockKeep(sessionId, id, block);
13040
13537
  } catch (error) {
13538
+ markFailed(id, error instanceof Error ? error.message : String(error));
13041
13539
  publish({
13042
13540
  ...snapshot,
13043
- error: error instanceof Error ? error.message : String(error),
13044
13541
  busy: new Set([...snapshot.busy].filter((busy) => busy !== id))
13045
13542
  });
13046
13543
  return;
13047
13544
  }
13545
+ clearFailed(id);
13048
13546
  await this.refresh(sessionId);
13049
13547
  },
13050
13548
  async blockRevert(sessionId, id, block) {
@@ -13056,15 +13554,41 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
13056
13554
  try {
13057
13555
  await port.blockRevert(sessionId, id, block);
13058
13556
  } catch (error) {
13557
+ markFailed(id, error instanceof Error ? error.message : String(error));
13059
13558
  publish({
13060
13559
  ...snapshot,
13061
- error: error instanceof Error ? error.message : String(error),
13062
13560
  busy: new Set([...snapshot.busy].filter((busy) => busy !== id))
13063
13561
  });
13064
13562
  return;
13065
13563
  }
13564
+ clearFailed(id);
13066
13565
  await this.refresh(sessionId);
13067
13566
  },
13567
+ async undo(sessionId) {
13568
+ try {
13569
+ const value = await port.undo(sessionId);
13570
+ await this.refresh(sessionId);
13571
+ const id = value.id;
13572
+ return id !== void 0 && snapshot.files.some((file) => file.id === id) ? id : void 0;
13573
+ } catch {
13574
+ return;
13575
+ }
13576
+ },
13577
+ async redo(sessionId) {
13578
+ try {
13579
+ const value = await port.redo(sessionId);
13580
+ await this.refresh(sessionId);
13581
+ const id = value.id;
13582
+ return id !== void 0 && snapshot.files.some((file) => file.id === id) ? id : void 0;
13583
+ } catch {
13584
+ return;
13585
+ }
13586
+ },
13587
+ async importVcs(sessionId, includeUntracked) {
13588
+ const value = await port.importVcs(sessionId, includeUntracked);
13589
+ await this.refresh(sessionId);
13590
+ return value;
13591
+ },
13068
13592
  async open(sessionId, id, action) {
13069
13593
  try {
13070
13594
  await port.open(sessionId, id, action);
@@ -13104,11 +13628,19 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
13104
13628
  "panel.missing": "文件已不存在",
13105
13629
  "panel.missingHint": "该文件已不存在,回退会恢复该文件。",
13106
13630
  "panel.createHint": "回退会删除该新建的文件。",
13631
+ "panel.pasteOnCopy": "复制引用后自动粘贴到消息输入框",
13632
+ "panel.pasteOnCopyDesc": "开启后,复制的引用会自动填入消息输入框,输入框会获得焦点。",
13633
+ "panel.importUntracked": "导入未跟踪的文件改动",
13634
+ "panel.importUntrackedDesc": "开启后,导入工作区改动会包含未跟踪/未版本化的新增文件(Git 未跟踪、SVN 未版本化、Perforce 未版本化)。收集改动需要扫描整个工作区,文件数量大时可能较慢。",
13635
+ "settings.tabLabel": "改动审批",
13107
13636
  "row.create": "新增文件",
13637
+ "row.failed": "失败",
13108
13638
  "row.added": "+{added}",
13109
13639
  "row.removed": "-{removed}",
13110
13640
  "action.keep": "保留",
13111
13641
  "action.revert": "回退",
13642
+ "action.keepAll": "全部保留",
13643
+ "action.revertAll": "全部回退",
13112
13644
  "action.openFile": "打开文件",
13113
13645
  "action.revealFile": "打开所在目录",
13114
13646
  "action.busy": "处理中…",
@@ -13119,7 +13651,16 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
13119
13651
  "action.langAuto": "自动",
13120
13652
  "action.langAutoDetected": "自动:{lang}",
13121
13653
  "action.langSelect": "高亮语言",
13654
+ "action.toggleOn": "打开",
13655
+ "action.toggleOff": "关闭",
13656
+ "action.importVcs": "导入工作区改动",
13657
+ "action.importVcsBusy": "导入中…",
13658
+ "panel.importNone": "没有可导入的改动",
13659
+ "panel.importDone": "已导入 {count} 个改动",
13660
+ "panel.importNoVcs": "未检测到版本控制(Git / SVN / Perforce)",
13661
+ "panel.importFailed": "导入失败:{message}",
13122
13662
  "action.search": "搜索 (Ctrl+F)",
13663
+ "action.settings": "设置",
13123
13664
  "action.expand": "展开全屏",
13124
13665
  "action.exitFullscreen": "退出全屏",
13125
13666
  "action.close": "关闭",
@@ -13143,11 +13684,19 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
13143
13684
  "panel.missing": "File is gone",
13144
13685
  "panel.missingHint": "This file no longer exists; reverting restores it.",
13145
13686
  "panel.createHint": "Reverting removes this created file.",
13687
+ "panel.pasteOnCopy": "Paste the copied reference into the composer",
13688
+ "panel.pasteOnCopyDesc": "When enabled, a copied reference is pasted into the composer, which then receives focus.",
13689
+ "panel.importUntracked": "Import changes to untracked files",
13690
+ "panel.importUntrackedDesc": "When enabled, importing workspace changes includes new/untracked files (Git-untracked, SVN-unversioned, Perforce-unversioned). Collecting changes scans the whole workspace, which can be slow on large trees.",
13691
+ "settings.tabLabel": "Diff Approval",
13146
13692
  "row.create": "New file",
13693
+ "row.failed": "Failed",
13147
13694
  "row.added": "+{added}",
13148
13695
  "row.removed": "-{removed}",
13149
13696
  "action.keep": "Keep",
13150
13697
  "action.revert": "Revert",
13698
+ "action.keepAll": "Keep all",
13699
+ "action.revertAll": "Revert all",
13151
13700
  "action.openFile": "Open file",
13152
13701
  "action.revealFile": "Reveal in folder",
13153
13702
  "action.busy": "Working…",
@@ -13158,7 +13707,16 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
13158
13707
  "action.langAuto": "Auto",
13159
13708
  "action.langAutoDetected": "Auto: {lang}",
13160
13709
  "action.langSelect": "Highlight language",
13710
+ "action.toggleOn": "On",
13711
+ "action.toggleOff": "Off",
13712
+ "action.importVcs": "Import workspace changes",
13713
+ "action.importVcsBusy": "Importing…",
13714
+ "panel.importNone": "No changes to import",
13715
+ "panel.importDone": "Imported {count} changes",
13716
+ "panel.importNoVcs": "No version control detected (Git / SVN / Perforce)",
13717
+ "panel.importFailed": "Import failed: {message}",
13161
13718
  "action.search": "Search (Ctrl+F)",
13719
+ "action.settings": "Settings",
13162
13720
  "action.expand": "Expand fullscreen",
13163
13721
  "action.exitFullscreen": "Exit fullscreen",
13164
13722
  "action.close": "Close",
@@ -13185,6 +13743,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
13185
13743
  zh,
13186
13744
  en
13187
13745
  }), "ui-diff-approval: dictionaries");
13746
+ const t = ctx.locale.bind(NS);
13188
13747
  const store = createPendingDiffStore(createDiffApprovalPort(ctx.get("connection").rpc));
13189
13748
  ctx.on("connection/reset", () => {
13190
13749
  store.reset();
@@ -13202,9 +13761,29 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
13202
13761
  onRevert: (sessionId, path) => store.revert(sessionId, path),
13203
13762
  onBlockKeep: (sessionId, id, block) => store.blockKeep(sessionId, id, block),
13204
13763
  onBlockRevert: (sessionId, id, block) => store.blockRevert(sessionId, id, block),
13205
- onOpen: (sessionId, id, action) => store.open(sessionId, id, action)
13764
+ onOpen: (sessionId, id, action) => store.open(sessionId, id, action),
13765
+ onUndo: (sessionId) => store.undo(sessionId),
13766
+ onRedo: (sessionId) => store.redo(sessionId),
13767
+ onImportVcs: (sessionId, includeUntracked) => store.importVcs(sessionId, includeUntracked),
13768
+ onPasteReference: (sessionId, reference) => {
13769
+ const actx = ctx.get("sessions")?.scope(sessionId);
13770
+ if (actx === void 0) return;
13771
+ const conversation = actx.get("conversation");
13772
+ if (conversation?.input === void 0) return;
13773
+ const textarea = document.querySelector("[data-composer-card] textarea");
13774
+ const base = textarea?.value ?? "";
13775
+ conversation.input.for(actx).setDraft(base === "" ? reference : `${base} ${reference}`);
13776
+ textarea?.focus();
13777
+ }
13206
13778
  })
13207
13779
  }, PendingPanel));
13780
+ ctx.slots.inject("settings.section", () => ctx.slots.register({
13781
+ name: "settings.section",
13782
+ id: "diff-approval",
13783
+ order: 100,
13784
+ label: () => t("settings.tabLabel"),
13785
+ locale: NS
13786
+ }, DiffApprovalSettingsTab));
13208
13787
  }
13209
13788
  //#endregion
13210
13789
  exports.DIFF_APPROVAL_CHANNEL = DIFF_APPROVAL_CHANNEL;