dsh-diff-approval 0.19.2 → 0.20.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 +2782 -726
- package/lib/index.js +521 -59
- package/lib/types/client/PathPicker.d.ts +43 -0
- package/lib/types/client/PendingPanel.d.ts +13 -17
- package/lib/types/client/highlight.d.ts +51 -8
- package/lib/types/client/lang.d.ts +8 -0
- package/lib/types/client/locales.d.ts +71 -0
- package/lib/types/client/port.d.ts +11 -5
- package/lib/types/client/search.d.ts +23 -0
- package/lib/types/client/settings.d.ts +41 -0
- package/lib/types/client/slots.d.ts +13 -5
- package/lib/types/client/split-diff.d.ts +7 -5
- package/lib/types/client/store.d.ts +12 -5
- package/lib/types/client/whole-file-diff.d.ts +21 -0
- package/lib/types/client/windowed-highlight.d.ts +66 -0
- package/lib/types/index.d.ts +1 -1
- package/lib/types/pending.d.ts +10 -0
- package/lib/types/types.d.ts +67 -0
- package/lib/types/vcs.d.ts +6 -0
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -20,6 +20,539 @@ window.__ModuleLoader__.load({
|
|
|
20
20
|
let react = require("react");
|
|
21
21
|
let react_dom = require("react-dom");
|
|
22
22
|
let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
23
|
+
//#region \0dsh-css:/home/runner/work/dsh-diff-approval/dsh-diff-approval/src/client/PendingPanel.module.css.mjs
|
|
24
|
+
const css = ".F1KBNa_layer{box-sizing:border-box;flex:none;align-items:center;width:calc(100% + 4px);height:42px;margin:4px -2px;display:flex;position:relative}.F1KBNa_footerButtons{align-items:center;width:100%;display:flex}.F1KBNa_badge{box-sizing:border-box;width:100%;height:42px;color:var(--dsw-alias-label-primary);cursor:pointer;background:0 0;border:none;border-radius:12px;align-items:center;gap:8px;padding:0 10px 0 8px;font-family:inherit;font-size:14px;line-height:22px;display:flex;overflow:hidden}.F1KBNa_badge:hover{background:var(--dsw-alias-interactive-bg-hover)}.F1KBNa_badge[data-active]{background:var(--dsw-alias-interactive-bg-hover-solid)}.F1KBNa_badge:disabled{color:var(--dsw-alias-label-tertiary);cursor:default;background:0 0}.F1KBNa_badgeLabel{text-overflow:ellipsis;white-space:nowrap;min-width:0;overflow:hidden}.F1KBNa_badgeCount{color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;flex:none;margin-left:auto;font-size:12px;line-height:16px}.F1KBNa_layer.F1KBNa_rail{width:36px;height:36px;margin:8px 0 10px}.F1KBNa_rail .F1KBNa_badge{border-radius:50%;justify-content:center;gap:0;width:36px;height:36px;padding:0}.F1KBNa_rail .F1KBNa_badgeLabel{display:none}.F1KBNa_rail .F1KBNa_badgeCount{box-sizing:border-box;background:var(--dsw-alias-state-business-primary);min-width:18px;height:18px;color:var(--dsw-alias-label-primary-foreground);font-variant-numeric:tabular-nums;border-radius:9px;justify-content:center;align-items:center;padding:0 4px;font-size:11px;line-height:18px;display:flex;position:absolute;top:-3px;right:-7px}.F1KBNa_fullscreenBackdrop{z-index:29;background:var(--dsw-specific-sidebar-fill);position:fixed;inset:0}.F1KBNa_panel{z-index:30;border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-base);width:auto;max-width:none;box-shadow:var(--dsw-shadow-lv2);--dsh-scrollbar-thumb:var(--dsw-alias-scrollbar-bg-l2);--dsh-scrollbar-thumb-hover:var(--dsw-alias-scrollbar-hover-l2);border-radius:12px;flex-direction:column;display:flex;position:fixed;inset:8px 8px 128px;overflow:hidden}.F1KBNa_header{box-sizing:border-box;border-bottom:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-base);flex:none;justify-content:space-between;align-items:center;min-height:44px;padding:10px 12px;display:flex}.F1KBNa_headerActions{align-items:center;gap:2px;display:flex}.F1KBNa_settingsPage{padding:8px 12px}.F1KBNa_settingsGroup{border-bottom:1px solid var(--dsw-alias-border-l2);margin-top:8px}.F1KBNa_settingsGroupHeader{width:100%;color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;text-align:left;background:0 0;border:none;align-items:center;gap:8px;padding:16px 0;display:flex}.F1KBNa_settingsGroupText{text-align:left;flex-direction:column;flex:1;gap:2px;min-width:0;display:flex}.F1KBNa_settingsGroupTitle{font-size:14px;font-weight:400;line-height:22px}.F1KBNa_settingsGroupDesc{color:var(--dsw-alias-label-tertiary);font-size:12px;font-weight:400;line-height:18px}.F1KBNa_settingsGroupChevron{margin-left:auto;transition:transform .15s}.F1KBNa_settingsGroup[data-open] .F1KBNa_settingsGroupChevron{transform:rotate(180deg)}.F1KBNa_settingsGroup[data-open] .F1KBNa_settingsGroupHeader{border-bottom:1px solid var(--dsw-alias-border-l2)}.F1KBNa_settingsGroupBody{flex-direction:column;display:flex}.F1KBNa_settingsGroupBody .F1KBNa_settingsRow{padding-left:16px}.F1KBNa_settingsGroupBody .F1KBNa_settingsRow:last-child,.F1KBNa_settingsPage>.F1KBNa_settingsRow:last-child{border-bottom:none}.F1KBNa_settingsRow{border-bottom:1px solid var(--dsw-alias-border-l2);align-items:center;gap:8px;padding:16px 0;display:flex}.F1KBNa_settingsRowText{flex-direction:column;flex:1;gap:4px;min-width:0;padding-right:48px;display:flex}.F1KBNa_settingsRowTitle{color:var(--dsw-alias-label-primary);font-size:14px;font-weight:400;line-height:22px}.F1KBNa_settingsRowDesc{color:var(--dsw-alias-label-tertiary);font-size:12px;font-weight:400;line-height:18px}.F1KBNa_settingsSelector{background:var(--dsw-alias-bg-module-platform);height:36px;font:inherit;color:var(--dsw-alias-label-primary);cursor:pointer;border:none;border-radius:18px;align-items:center;gap:12px;padding:0 14px;font-size:14px;line-height:22px;display:inline-flex}.F1KBNa_settingsSelector:hover{background:var(--dsw-alias-interactive-bg-hover)}.F1KBNa_settingsSelectorChevron{flex:none}.F1KBNa_colorPicker{position:relative}.F1KBNa_colorPickerTrigger{background:var(--dsw-alias-bg-module-platform);width:120px;height:36px;font:inherit;color:var(--dsw-alias-label-primary);cursor:pointer;border:none;border-radius:18px;align-items:center;gap:8px;padding:0 8px;font-size:12px;line-height:18px;display:inline-flex}.F1KBNa_colorPickerTrigger:hover{background:var(--dsw-alias-interactive-bg-hover)}.F1KBNa_colorSwatch{border:1px solid var(--dsw-alias-border-l2);border-radius:50%;flex:none;width:24px;height:24px}.F1KBNa_colorValue{text-overflow:ellipsis;white-space:nowrap;font-variant-numeric:tabular-nums;flex:1;min-width:0;overflow:hidden}.F1KBNa_colorPickerChevron{flex:none}.F1KBNa_colorBackdrop{z-index:40;position:fixed;inset:0}.F1KBNa_colorPickerPopover{z-index:41;border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-base);box-shadow:var(--dsw-shadow-lv2);border-radius:10px;padding:10px;position:absolute;top:calc(100% + 6px);right:0}.F1KBNa_colorPickerPanel{flex-direction:column;gap:8px;display:flex}.F1KBNa_colorPickerInputRow{align-items:center;gap:8px;display:flex}.F1KBNa_colorPickerPreview{border:1px solid var(--dsw-alias-border-l2);border-radius:6px;flex:none;width:22px;height:22px}.F1KBNa_colorPickerInput{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-module-platform);min-width:0;height:30px;font:inherit;color:var(--dsw-alias-label-primary);border-radius:8px;flex:1;padding:0 8px;font-size:12px;line-height:28px}.F1KBNa_colorPickerInput:focus{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:-1px}.F1KBNa_colorSv{border:1px solid var(--dsw-alias-border-l1);cursor:crosshair;touch-action:none;border-radius:8px;height:140px;position:relative}.F1KBNa_colorSvCursor{pointer-events:none;border:2px solid #fff;border-radius:50%;width:14px;height:14px;position:absolute;transform:translate(-50%,-50%);box-shadow:0 0 0 1px #00000080}.F1KBNa_colorHue{border:1px solid var(--dsw-alias-border-l1);cursor:pointer;touch-action:none;border-radius:7px;height:14px;position:relative}.F1KBNa_colorHueCursor{pointer-events:none;border:2px solid #fff;border-radius:50%;width:16px;height:16px;position:absolute;top:50%;transform:translate(-50%,-50%);box-shadow:0 0 0 1px #00000080}.F1KBNa_diffPreview{z-index:2;border:1px solid var(--dsw-alias-border-l1);font:var(--dsw-font-markdown-code-block);background:var(--dsw-alias-markdown-code-block);border-radius:8px;margin:16px 0 8px;padding:8px;position:sticky;top:0;overflow:hidden}.F1KBNa_diffPreviewTitle{color:var(--dsw-alias-label-tertiary);margin-bottom:6px;font-family:inherit;font-size:12px;line-height:18px}.F1KBNa_diffPreviewScroll{height:132px;overflow-y:auto}.F1KBNa_mdPreviewWrap{flex:1;min-width:0;min-height:0;display:flex;position:relative}.F1KBNa_mdPreviewBody{min-width:0;min-height:0;color:var(--dsw-alias-label-primary);flex:1;padding:12px 16px;position:relative;overflow:auto}.F1KBNa_mdPreviewContent{width:100%;min-width:0;margin:0 auto}.F1KBNa_mdPreviewSingle{display:block}.F1KBNa_mdPreviewDouble{display:flex}.F1KBNa_mdPreviewBody .mdBlock{border-radius:4px;margin-bottom:12px;padding:2px 6px}.F1KBNa_mdPreviewBody .mdAdd{background-color:color-mix(in srgb, var(--dsh-diff-add-color,var(--dsw-alias-state-success-primary)) 10%, transparent)}.F1KBNa_mdPreviewBody .mdDel{background-color:color-mix(in srgb, var(--dsh-diff-del-color,var(--dsw-alias-state-error-primary)) 12%, transparent)}.F1KBNa_mdPreviewBody .mdWordDel{background-color:color-mix(in srgb, var(--dsh-diff-del-color,var(--dsw-alias-state-error-primary)) 34%, transparent);border-radius:2px;padding:0 1px}.F1KBNa_mdPreviewBody .mdWordAdd{background-color:color-mix(in srgb, var(--dsh-diff-add-color,var(--dsw-alias-state-success-primary)) 34%, transparent);border-radius:2px;padding:0 1px}.F1KBNa_mdPreviewBody .mdDouble{flex-direction:column;width:100%;min-width:0;display:flex}.F1KBNa_mdPreviewBody .mdDoubleRow{align-items:flex-start;width:100%;min-width:0;display:flex}.F1KBNa_mdPreviewBody .mdDoubleCol{flex:1;min-width:0}.F1KBNa_mdPreviewBody .mdDoubleRule{background:var(--dsw-alias-border-l2);flex:none;align-self:stretch;width:1px;margin:0 12px}.F1KBNa_mdPreviewFallback{font-family:var(--dsw-font-markdown-code-block);color:var(--dsw-alias-label-tertiary);padding:12px 16px;font-size:12px;line-height:18px}.F1KBNa_mdPreviewBody{word-break:break-word;font-size:13px;line-height:20px}.F1KBNa_mdPreviewBody>:first-child{margin-top:0}.F1KBNa_mdPreviewBody>:last-child{margin-bottom:0}.F1KBNa_mdPreviewBody h1,.F1KBNa_mdPreviewBody h2,.F1KBNa_mdPreviewBody h3,.F1KBNa_mdPreviewBody h4,.F1KBNa_mdPreviewBody h5,.F1KBNa_mdPreviewBody h6{margin:16px 0 8px;font-weight:600;line-height:1.3}.F1KBNa_mdPreviewBody h1{font-size:22px}.F1KBNa_mdPreviewBody h2{font-size:19px}.F1KBNa_mdPreviewBody h3{font-size:16px}.F1KBNa_mdPreviewBody h4{font-size:15px}.F1KBNa_mdPreviewBody h5{font-size:14px}.F1KBNa_mdPreviewBody h6{font-size:13px}.F1KBNa_mdPreviewBody p{margin:8px 0}.F1KBNa_mdPreviewBody ul,.F1KBNa_mdPreviewBody ol{margin:8px 0;padding-left:22px}.F1KBNa_mdPreviewBody li{margin:2px 0}.F1KBNa_mdPreviewBody a{color:var(--dsw-alias-state-business-primary);text-decoration:none}.F1KBNa_mdPreviewBody a:hover{text-decoration:underline}.F1KBNa_mdPreviewBody code{font-family:var(--dsw-font-markdown-code-block);background:#9e9e9e40;border-radius:4px;padding:1px 5px;font-size:12.5px}.F1KBNa_mdPreviewBody pre{border:1px solid var(--dsw-alias-border-l1);font-family:var(--dsw-font-markdown-code-block);background:#9e9e9e40;border-radius:8px;margin:8px 0;padding:10px 12px;font-size:12.5px;line-height:18px;overflow:auto}.F1KBNa_mdPreviewBody pre code{background:0 0;padding:0}.F1KBNa_mdPreviewBody blockquote{border-left:3px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);margin:8px 0;padding:0 12px}.F1KBNa_mdPreviewBody hr{background:var(--dsw-alias-border-l1);border:none;height:1px;margin:16px 0}.F1KBNa_mdPreviewBody table{border-collapse:collapse;margin:8px 0}.F1KBNa_mdPreviewBody th,.F1KBNa_mdPreviewBody td{border:1px solid var(--dsw-alias-border-l1);padding:4px 10px}.F1KBNa_mdPreviewBody img{max-width:100%}.F1KBNa_mdPreviewBody .mdImageFallback{border:1px dashed var(--dsw-alias-border-l2);background:color-mix(in srgb, var(--dsw-alias-markdown-code-block) 45%, transparent);width:100%;min-height:120px;color:var(--dsw-alias-label-secondary);font-family:var(--dsw-font-markdown-code-block);text-align:center;white-space:pre-wrap;word-break:break-all;border-radius:4px;justify-content:center;align-items:center;margin:0 auto;padding:8px 12px;font-size:12px;line-height:16px;display:flex}.F1KBNa_toggle{border:1px solid var(--dsw-alias-border-l2);cursor:pointer;background:0 0;border-radius:18px;flex:none;width:60px;height:36px;padding:0;position:relative}.F1KBNa_toggle:hover{background:var(--dsw-alias-interactive-bg-hover)}.F1KBNa_toggle:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:2px}.F1KBNa_toggleOn{border-color:var(--dsw-alias-state-business-primary);background:var(--dsw-alias-state-business-primary)}.F1KBNa_toggleOn:hover{background:var(--dsw-alias-state-business-primary)}.F1KBNa_toggleThumb{background:var(--dsw-alias-label-primary);border-radius:50%;width:28px;height:28px;transition:left .12s;position:absolute;top:3px;left:3px}.F1KBNa_toggleOn .F1KBNa_toggleThumb{background:#fff;left:27px}.F1KBNa_stepper{align-items:center;gap:6px;display:inline-flex}.F1KBNa_stepperButton{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-module-platform);width:32px;height:32px;color:var(--dsw-alias-label-primary);cursor:pointer;border-radius:8px;padding:0;position:relative}.F1KBNa_stepperButton:before,.F1KBNa_stepperButton:after{content:\"\";background:currentColor;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)}.F1KBNa_stepperButton:before{width:10px;height:1px}.F1KBNa_stepperButton:after{width:1px;height:10px;display:none}.F1KBNa_stepperButtonUp:after{display:block}.F1KBNa_stepperButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.F1KBNa_stepperButton:disabled{color:var(--dsw-alias-label-tertiary);cursor:default}.F1KBNa_stepperValue{text-align:center;min-width:2ch;color:var(--dsw-alias-label-primary);font-variant-numeric:tabular-nums;font-size:14px;line-height:22px}.F1KBNa_states{flex-direction:column;flex:1;min-height:0;padding:4px 12px 12px;display:flex;overflow-y:auto}.F1KBNa_split{flex:1;align-items:stretch;min-height:0;display:flex;position:relative}.F1KBNa_fileListFloat{z-index:40;box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-base);box-shadow:var(--dsw-shadow-lv2);border-radius:12px;flex-direction:column;padding:6px 8px 10px;display:flex;position:absolute;overflow:hidden}.F1KBNa_fileList{box-sizing:border-box;border-right:1px solid var(--dsw-alias-border-l2);flex-direction:column;flex:none;width:240px;min-height:0;padding:4px 2px 12px 8px;display:flex}.F1KBNa_listScroll{scrollbar-gutter:stable;flex:1;min-height:0;overflow-y:auto}.F1KBNa_bulkActions{flex:none;gap:6px;padding:8px 8px 0 0;display:flex}.F1KBNa_bulkActions .F1KBNa_action{text-align:center;flex:1}.F1KBNa_resizeHandle{cursor:col-resize;background:0 0;flex:none;width:5px;margin:0 -2px}.F1KBNa_resizeHandle:hover{background:var(--dsw-alias-border-l2)}.F1KBNa_detail{flex-direction:column;flex:1;min-width:0;min-height:0;display:flex}.F1KBNa_detailEmpty{color:var(--dsw-alias-label-tertiary);text-align:center;flex:1;justify-content:center;align-items:center;margin:0;padding:24px;font-size:12px;line-height:18px;display:flex}.F1KBNa_confirmBackdrop{z-index:5;background:color-mix(in srgb, var(--dsw-alias-bg-base) 55%, transparent);justify-content:center;align-items:center;padding:24px;display:flex;position:absolute;inset:0}.F1KBNa_confirmCard{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-base);max-width:340px;box-shadow:var(--dsw-shadow-lv3);border-radius:12px;flex-direction:column;gap:12px;padding:16px;display:flex}.F1KBNa_confirmText{font:var(--dsw-font-caption);color:var(--dsw-alias-label-primary);overflow-wrap:anywhere;margin:0;line-height:1.5}.F1KBNa_confirmActions{flex:none;justify-content:flex-end;align-items:center;gap:8px;display:flex}.F1KBNa_pickerCard{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-base);width:420px;max-width:100%;height:70%;max-height:100%;box-shadow:var(--dsw-shadow-lv3);border-radius:12px;flex-direction:column;gap:8px;padding:14px 16px 16px;display:flex;position:relative;overflow:hidden}.F1KBNa_pickerTitle{color:var(--dsw-alias-label-primary);flex:none;font-size:13px;font-weight:500;line-height:20px}.F1KBNa_pickerClose{width:22px;height:22px;color:var(--dsw-alias-label-tertiary);cursor:pointer;background:0 0;border:0;border-radius:6px;justify-content:center;align-items:center;padding:0;display:inline-flex;position:absolute;top:10px;right:10px}.F1KBNa_pickerClose:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}.F1KBNa_pickerForm{flex:none;gap:8px;display:flex}.F1KBNa_pickerInput{border:1px solid var(--dsw-alias-border-l1);min-width:0;color:var(--dsw-alias-label-primary);background:0 0;border-radius:8px;flex:auto;padding:3px 8px;font-family:inherit;font-size:12px;line-height:18px}.F1KBNa_pickerInput:focus{border-color:var(--dsw-alias-state-business-primary);outline:none}.F1KBNa_tree{scrollbar-gutter:stable;border:1px solid var(--dsw-alias-border-l1);border-radius:8px;flex:auto;min-height:0;padding:4px 2px 4px 4px;overflow:auto}.F1KBNa_treeRow{border-radius:6px;align-items:center;gap:2px;padding-right:4px;display:flex}.F1KBNa_treeRow:hover{background:var(--dsw-alias-interactive-bg-hover)}.F1KBNa_treeRow[data-selected]{background:color-mix(in srgb, var(--dsw-alias-state-business-primary) 18%, transparent)}.F1KBNa_treeCaret{width:16px;height:16px;color:var(--dsw-alias-label-tertiary);cursor:pointer;background:0 0;border:0;border-radius:4px;flex:none;justify-content:center;align-items:center;padding:0;display:inline-flex}.F1KBNa_treeCaret:hover{color:var(--dsw-alias-label-primary)}.F1KBNa_treeName{min-width:0;color:var(--dsw-alias-label-primary);text-align:left;cursor:pointer;background:0 0;border:0;border-radius:6px;flex:auto;align-items:center;gap:6px;padding:2px 4px;font-family:inherit;font-size:12px;line-height:18px;display:flex}.F1KBNa_treeName:disabled{color:var(--dsw-alias-label-tertiary);cursor:default}.F1KBNa_treeNote{color:var(--dsw-alias-label-tertiary);margin:2px 0;font-size:12px;line-height:18px}.F1KBNa_pickerIcon{width:16px;height:16px;color:var(--dsw-alias-label-tertiary);flex:none;justify-content:center;align-items:center;display:inline-flex}.F1KBNa_pickerName{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.F1KBNa_pickerNote{color:var(--dsw-alias-label-tertiary);flex:none;margin:0;font-size:12px;line-height:18px}.F1KBNa_pickerCheck{color:var(--dsw-alias-label-secondary);cursor:pointer;align-items:center;gap:6px;margin-right:auto;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_groupHead{align-items:center;gap:4px;margin:8px 0 4px;padding-right:4px;display:flex}.F1KBNa_action.F1KBNa_addButton{box-sizing:border-box;border-radius:6px;justify-content:center;align-items:center;width:20px;height:20px;min-height:0;padding:0;display:inline-flex}.F1KBNa_groupHead>.F1KBNa_group{margin:0}.F1KBNa_emptyActions{flex-wrap:wrap;justify-content:center;gap:8px;display:flex}.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(--dsh-diff-add-color,var(--dsw-alias-state-success-primary)) 60%, var(--dsw-alias-label-primary))}.F1KBNa_delCount{color:color-mix(in srgb, var(--dsh-diff-del-color,var(--dsw-alias-state-error-primary)) 60%, var(--dsw-alias-label-primary))}.F1KBNa_diff{background:var(--dsw-alias-markdown-code-block);flex-direction:column;flex:1;min-height:0;display:flex;position:relative;overflow:hidden}.F1KBNa_diffHeader{border-bottom:1px solid var(--dsw-alias-border-l2);align-items:center;gap:8px;padding:6px 8px;display:flex}.F1KBNa_diffPath{white-space:nowrap;min-width:0;color:var(--dsw-alias-label-primary);font:var(--dsw-font-markdown-code-block);-webkit-user-select:text;user-select:text;flex:1;font-size:12px;line-height:18px;overflow:auto hidden}.F1KBNa_diffActions{border-bottom:1px solid var(--dsw-alias-border-l2);align-items:center;gap:8px;padding:6px 8px;display:flex}.F1KBNa_diffStats{color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;font-size:12px;line-height:16px}.F1KBNa_flexSpacer{flex:1}.F1KBNa_divider{background:var(--dsw-alias-border-l1);align-self:stretch;width:1px;margin:2px 0}.F1KBNa_action{border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-primary);cursor:pointer;background:0 0;border-radius:8px;padding:3px 12px;font-family:inherit;font-size:12px;line-height:18px}.F1KBNa_action:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.F1KBNa_action:disabled{color:var(--dsw-alias-label-tertiary);cursor:default}.F1KBNa_actionPrimary{background:var(--dsw-alias-state-business-primary);border-color:var(--dsw-alias-state-business-primary);color:var(--dsw-static-neutral-bluish-50)}.F1KBNa_actionPrimary:hover:not(:disabled){background:color-mix(in srgb, var(--dsw-alias-state-business-primary) 85%, var(--dsw-static-neutral-bluish-1000))}.F1KBNa_actionPrimary:disabled{background:var(--dsw-alias-state-business-primary);color:var(--dsw-static-neutral-bluish-50);opacity:.55}.F1KBNa_actionQuietDisabled:disabled{cursor:pointer;color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-border-l1);background:0 0}.F1KBNa_actionPrimary.F1KBNa_actionQuietDisabled:disabled{background:var(--dsw-alias-state-business-primary);border-color:var(--dsw-alias-state-business-primary);color:var(--dsw-static-neutral-bluish-50);opacity:1}.F1KBNa_iconAction{justify-content:center;align-items:center;min-height:25px;padding:4px 6px;display:inline-flex}.F1KBNa_close{cursor:pointer;width:28px;height:28px;color:var(--dsw-alias-label-tertiary);background:0 0;border:none;border-radius:28px;justify-content:center;align-items:center;padding:0;display:inline-flex}.F1KBNa_close:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}.F1KBNa_expand{cursor:pointer;width:28px;height:28px;color:var(--dsw-alias-label-tertiary);background:0 0;border:none;border-radius:28px;justify-content:center;align-items:center;padding:0;display:inline-flex}.F1KBNa_expand:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}.F1KBNa_expandExpanded svg{transform:rotate(180deg)}.F1KBNa_diffBodyWrap{flex:1;min-height:0;display:flex;position:relative}.F1KBNa_diffBody{min-width:0;font:var(--dsw-font-markdown-code-block);-webkit-text-size-adjust:100%;text-size-adjust:100%;cursor:text;outline:none;flex:1;padding:0;position:relative;overflow:auto}.F1KBNa_diffBody::-webkit-scrollbar,.F1KBNa_diffBody::-webkit-scrollbar-track,.F1KBNa_diffBody::-webkit-scrollbar-thumb{cursor:default}.F1KBNa_blockActions{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-markdown-code-block);box-shadow:var(--dsw-shadow-lv1);z-index:2;border-radius:10px;align-items:center;gap:9px;padding:5px;display:flex;position:absolute;right:8px}.F1KBNa_blockPosition{color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;white-space:nowrap;margin-top:1px;margin-left:7px;font-size:12px;line-height:16px}.F1KBNa_searchBar{z-index:3;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-markdown-code-block);box-shadow:var(--dsw-shadow-lv1);border-radius:10px;align-items:center;gap:4px;padding:4px;display:flex;position:absolute;top:8px;right:8px}.F1KBNa_searchInput{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-base);width:150px;color:var(--dsw-alias-label-primary);border-radius:6px;outline:none;padding:3px 8px;font-family:inherit;font-size:12px;line-height:18px}.F1KBNa_searchInput:focus{border-color:var(--dsw-alias-state-business-primary)}.F1KBNa_searchCount{text-align:center;min-width:34px;color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;white-space:nowrap;font-size:12px;line-height:16px}.F1KBNa_searchToggle{border:1px solid var(--dsw-alias-border-l1);min-height:25px;color:var(--dsw-alias-label-primary);cursor:pointer;background:0 0;border-radius:8px;justify-content:center;align-items:center;padding:4px 6px;display:inline-flex}.F1KBNa_searchToggle:hover{background:var(--dsw-alias-interactive-bg-hover)}.F1KBNa_searchToggleOn{border-color:var(--dsw-alias-state-business-primary);background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-state-business-primary)}.F1KBNa_searchMatch{color:inherit;background-color:#facc1542;border-radius:2px}.F1KBNa_searchMatchCurrent{color:inherit;background-color:#facc1599;border-radius:2px}.F1KBNa_blockFlash{z-index:1;box-sizing:border-box;border:2px solid var(--dsw-alias-state-business-primary);pointer-events:none;border-radius:4px;animation:1s ease-out forwards F1KBNa_diffFlashFade;position:absolute;left:0;right:0}.F1KBNa_blockFlashShake{animation:.7s ease-out forwards F1KBNa_diffFlashShake}@keyframes F1KBNa_diffFlashFade{0%{opacity:1}to{opacity:0}}@keyframes F1KBNa_diffFlashShake{0%{opacity:1;transform:translateY(0)}10%{transform:translateY(1px)}20%{transform:translateY(-1px)}30%{transform:translateY(1px)}40%{transform:translateY(-1px)}50%{transform:translateY(1px)}60%{transform:translateY(-1px)}70%{opacity:1;transform:translateY(0)}to{opacity:0;transform:translateY(0)}}.F1KBNa_overviewRuler{pointer-events:none;opacity:.5;width:4px;position:absolute;top:0;bottom:0;right:0}.F1KBNa_overviewMarker{border-radius:2px;width:100%;min-height:2px;position:absolute;right:0}.F1KBNa_markerDel{background-color:var(--dsw-alias-state-error-primary)}.F1KBNa_markerAdd{background-color:var(--dsw-alias-state-success-primary)}.F1KBNa_lines{border-spacing:0;width:max-content;min-width:100%;font-size:calc(1em * var(--dsh-diff-font-scale,1));display:table}.F1KBNa_line{height:var(--dsh-diff-line-height,22px);line-height:var(--dsh-diff-line-height,22px);-webkit-user-select:none;user-select:none;display:table-row}.F1KBNa_vSpacer{display:table-row}.F1KBNa_gutter{box-sizing:border-box;text-align:right;width:44px;color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;-webkit-user-select:none;user-select:none;cursor:default;padding-right:10px;display:table-cell}.F1KBNa_code{white-space:pre;-webkit-user-select:text;user-select:text;display:table-cell}.F1KBNa_context{color:var(--dsw-alias-label-primary)}.F1KBNa_del{background-color:color-mix(in srgb, var(--dsh-diff-del-color,var(--dsw-alias-state-error-primary)) 12%, transparent)}.F1KBNa_add{background-color:color-mix(in srgb, var(--dsh-diff-add-color,var(--dsw-alias-state-success-primary)) 10%, transparent)}.F1KBNa_statusBar{box-sizing:border-box;border-top:1px solid var(--dsw-alias-border-l2);height:34px;color:var(--dsw-alias-label-tertiary);font-family:var(--dsw-font-markdown-code-block);font-variant-numeric:tabular-nums;flex:none;align-items:center;gap:8px;padding:0 8px;font-size:12px;line-height:18px;display:flex}.F1KBNa_statusAction{border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-primary);white-space:nowrap;cursor:pointer;background:0 0;border-radius:8px;padding:3px 12px;font-family:inherit;font-size:12px;line-height:18px}.F1KBNa_statusAction:hover{background:var(--dsw-alias-interactive-bg-hover)}.F1KBNa_langSelect{border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-primary);cursor:pointer;background:0 0;border-radius:8px;flex:none;align-items:center;gap:4px;padding:3px 12px;font-family:inherit;font-size:12px;line-height:18px;display:inline-flex}.F1KBNa_langSelect:hover{background:var(--dsw-alias-interactive-bg-hover)}.F1KBNa_langLabel{text-overflow:ellipsis;white-space:nowrap;max-width:140px;overflow:hidden}.F1KBNa_notice{z-index:60;border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-base);max-width:360px;box-shadow:var(--dsw-shadow-lv3);border-radius:10px;align-items:flex-start;gap:12px;padding:12px 14px;display:flex;position:fixed;bottom:24px;right:24px}.F1KBNa_noticeText{font:var(--dsw-font-caption);color:var(--dsw-alias-label-primary);flex:1;margin:0;line-height:1.45}.F1KBNa_noticeButton{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary);font:var(--dsw-font-caption);cursor:pointer;border-radius:6px;flex:none;padding:4px 10px}.F1KBNa_noticeButton:hover{background:var(--dsw-alias-interactive-bg-hover-solid)}.F1KBNa_wrap .F1KBNa_line{height:auto;min-height:var(--dsh-diff-line-height,22px)}.F1KBNa_subline{height:var(--dsh-diff-line-height,22px);line-height:var(--dsh-diff-line-height,22px);white-space:pre;display:block;overflow:hidden}.F1KBNa_wrapActive{background:var(--dsw-alias-state-business-primary);border-color:var(--dsw-alias-state-business-primary);color:var(--dsw-static-neutral-bluish-50)}.F1KBNa_wrapActive:hover{background:color-mix(in srgb, var(--dsw-alias-state-business-primary) 85%, var(--dsw-static-neutral-bluish-1000))}.F1KBNa_splitRoot{flex-direction:column;flex:1;min-height:0;display:flex;position:relative}.F1KBNa_diffBodySplit{flex:1;min-height:0;overflow-x:hidden}.F1KBNa_splitCols{width:100%;min-width:0;display:flex}.F1KBNa_splitCol{box-sizing:border-box;flex:1 1 0;min-width:0;overflow:hidden}.F1KBNa_splitDivider{background:var(--dsw-alias-border-l2);flex:0 0 1px}.F1KBNa_splitCol .F1KBNa_gutter,.F1KBNa_splitCol .F1KBNa_code{vertical-align:top}.F1KBNa_splitHScrollRow{border-top:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-base);flex:none;width:100%;min-width:0;display:flex}.F1KBNa_splitHScroll{flex:1 1 0;min-width:0;height:15px;overflow:auto hidden}.F1KBNa_splitHScroll::-webkit-scrollbar{height:15px}.F1KBNa_splitHScroll::-webkit-scrollbar-track{cursor:default}.F1KBNa_splitHScroll::-webkit-scrollbar-thumb{cursor:default}.F1KBNa_splitHScrollFill{height:1px}.F1KBNa_splitLdel{background-color:color-mix(in srgb, var(--dsh-diff-del-color,var(--dsw-alias-state-error-primary)) 12%, transparent)}.F1KBNa_splitLadd{background-color:color-mix(in srgb, var(--dsh-diff-add-color,var(--dsw-alias-state-success-primary)) 10%, transparent)}.F1KBNa_splitRdel{background-color:color-mix(in srgb, var(--dsh-diff-del-color,var(--dsw-alias-state-error-primary)) 12%, transparent)}.F1KBNa_splitRadd{background-color:color-mix(in srgb, var(--dsh-diff-add-color,var(--dsw-alias-state-success-primary)) 10%, transparent)}.F1KBNa_intraDel{background-color:color-mix(in srgb, var(--dsh-diff-del-color,var(--dsw-alias-state-error-primary)) 26%, transparent);text-decoration:line-through}.F1KBNa_intraAdd{background-color:color-mix(in srgb, var(--dsh-diff-add-color,var(--dsw-alias-state-success-primary)) 24%, transparent)}";
|
|
25
|
+
const tagId = "dsh-diff-approval/PendingPanel.module.css";
|
|
26
|
+
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
|
|
27
|
+
const tag = document.createElement("style");
|
|
28
|
+
tag.dataset.plugin = "dsh-diff-approval";
|
|
29
|
+
tag.dataset.pluginCss = tagId;
|
|
30
|
+
tag.textContent = css;
|
|
31
|
+
document.head.appendChild(tag);
|
|
32
|
+
}
|
|
33
|
+
var PendingPanel_module_css_default = {
|
|
34
|
+
"fullscreenBackdrop": "F1KBNa_fullscreenBackdrop",
|
|
35
|
+
"vSpacer": "F1KBNa_vSpacer",
|
|
36
|
+
"colorPickerPopover": "F1KBNa_colorPickerPopover",
|
|
37
|
+
"pickerCard": "F1KBNa_pickerCard",
|
|
38
|
+
"colorHueCursor": "F1KBNa_colorHueCursor",
|
|
39
|
+
"overviewRuler": "F1KBNa_overviewRuler",
|
|
40
|
+
"line": "F1KBNa_line",
|
|
41
|
+
"wrap": "F1KBNa_wrap",
|
|
42
|
+
"colorPickerPreview": "F1KBNa_colorPickerPreview",
|
|
43
|
+
"searchInput": "F1KBNa_searchInput",
|
|
44
|
+
"pickerForm": "F1KBNa_pickerForm",
|
|
45
|
+
"notice": "F1KBNa_notice",
|
|
46
|
+
"splitCols": "F1KBNa_splitCols",
|
|
47
|
+
"mdPreviewContent": "F1KBNa_mdPreviewContent",
|
|
48
|
+
"pickerClose": "F1KBNa_pickerClose",
|
|
49
|
+
"diffStats": "F1KBNa_diffStats",
|
|
50
|
+
"settingsGroupText": "F1KBNa_settingsGroupText",
|
|
51
|
+
"splitRoot": "F1KBNa_splitRoot",
|
|
52
|
+
"colorBackdrop": "F1KBNa_colorBackdrop",
|
|
53
|
+
"splitHScroll": "F1KBNa_splitHScroll",
|
|
54
|
+
"states": "F1KBNa_states",
|
|
55
|
+
"addCount": "F1KBNa_addCount",
|
|
56
|
+
"iconAction": "F1KBNa_iconAction",
|
|
57
|
+
"blockPosition": "F1KBNa_blockPosition",
|
|
58
|
+
"pickerTitle": "F1KBNa_pickerTitle",
|
|
59
|
+
"readError": "F1KBNa_readError",
|
|
60
|
+
"tree": "F1KBNa_tree",
|
|
61
|
+
"diff": "F1KBNa_diff",
|
|
62
|
+
"colorPickerPanel": "F1KBNa_colorPickerPanel",
|
|
63
|
+
"divergedHint": "F1KBNa_divergedHint",
|
|
64
|
+
"statusBar": "F1KBNa_statusBar",
|
|
65
|
+
"mdPreviewBody": "F1KBNa_mdPreviewBody",
|
|
66
|
+
"rowFailed": "F1KBNa_rowFailed",
|
|
67
|
+
"blockFlash": "F1KBNa_blockFlash",
|
|
68
|
+
"diffFlashFade": "F1KBNa_diffFlashFade",
|
|
69
|
+
"diffPreviewTitle": "F1KBNa_diffPreviewTitle",
|
|
70
|
+
"splitDivider": "F1KBNa_splitDivider",
|
|
71
|
+
"divider": "F1KBNa_divider",
|
|
72
|
+
"mdPreviewFallback": "F1KBNa_mdPreviewFallback",
|
|
73
|
+
"blockActions": "F1KBNa_blockActions",
|
|
74
|
+
"treeRow": "F1KBNa_treeRow",
|
|
75
|
+
"noticeText": "F1KBNa_noticeText",
|
|
76
|
+
"delCount": "F1KBNa_delCount",
|
|
77
|
+
"expandExpanded": "F1KBNa_expandExpanded",
|
|
78
|
+
"pickerInput": "F1KBNa_pickerInput",
|
|
79
|
+
"diffBody": "F1KBNa_diffBody",
|
|
80
|
+
"splitHScrollFill": "F1KBNa_splitHScrollFill",
|
|
81
|
+
"splitRdel": "F1KBNa_splitRdel",
|
|
82
|
+
"settingsGroupBody": "F1KBNa_settingsGroupBody",
|
|
83
|
+
"settingsRowTitle": "F1KBNa_settingsRowTitle",
|
|
84
|
+
"toggle": "F1KBNa_toggle",
|
|
85
|
+
"confirmText": "F1KBNa_confirmText",
|
|
86
|
+
"searchBar": "F1KBNa_searchBar",
|
|
87
|
+
"confirmBackdrop": "F1KBNa_confirmBackdrop",
|
|
88
|
+
"action": "F1KBNa_action",
|
|
89
|
+
"settingsRowText": "F1KBNa_settingsRowText",
|
|
90
|
+
"layer": "F1KBNa_layer",
|
|
91
|
+
"colorSv": "F1KBNa_colorSv",
|
|
92
|
+
"settingsGroup": "F1KBNa_settingsGroup",
|
|
93
|
+
"listScroll": "F1KBNa_listScroll",
|
|
94
|
+
"searchCount": "F1KBNa_searchCount",
|
|
95
|
+
"code": "F1KBNa_code",
|
|
96
|
+
"stepperButton": "F1KBNa_stepperButton",
|
|
97
|
+
"footerButtons": "F1KBNa_footerButtons",
|
|
98
|
+
"settingsRow": "F1KBNa_settingsRow",
|
|
99
|
+
"settingsSelector": "F1KBNa_settingsSelector",
|
|
100
|
+
"stepperButtonUp": "F1KBNa_stepperButtonUp",
|
|
101
|
+
"intraDel": "F1KBNa_intraDel",
|
|
102
|
+
"detailEmpty": "F1KBNa_detailEmpty",
|
|
103
|
+
"rowMeta": "F1KBNa_rowMeta",
|
|
104
|
+
"flexSpacer": "F1KBNa_flexSpacer",
|
|
105
|
+
"searchMatchCurrent": "F1KBNa_searchMatchCurrent",
|
|
106
|
+
"note": "F1KBNa_note",
|
|
107
|
+
"hint": "F1KBNa_hint",
|
|
108
|
+
"row": "F1KBNa_row",
|
|
109
|
+
"langLabel": "F1KBNa_langLabel",
|
|
110
|
+
"settingsGroupTitle": "F1KBNa_settingsGroupTitle",
|
|
111
|
+
"importButton": "F1KBNa_importButton",
|
|
112
|
+
"splitHScrollRow": "F1KBNa_splitHScrollRow",
|
|
113
|
+
"confirmActions": "F1KBNa_confirmActions",
|
|
114
|
+
"markerAdd": "F1KBNa_markerAdd",
|
|
115
|
+
"colorPickerInput": "F1KBNa_colorPickerInput",
|
|
116
|
+
"mdPreviewWrap": "F1KBNa_mdPreviewWrap",
|
|
117
|
+
"kindTag": "F1KBNa_kindTag",
|
|
118
|
+
"actionError": "F1KBNa_actionError",
|
|
119
|
+
"headerActions": "F1KBNa_headerActions",
|
|
120
|
+
"actionQuietDisabled": "F1KBNa_actionQuietDisabled",
|
|
121
|
+
"expand": "F1KBNa_expand",
|
|
122
|
+
"blockFlashShake": "F1KBNa_blockFlashShake",
|
|
123
|
+
"del": "F1KBNa_del",
|
|
124
|
+
"splitCol": "F1KBNa_splitCol",
|
|
125
|
+
"panel": "F1KBNa_panel",
|
|
126
|
+
"mdPreviewSingle": "F1KBNa_mdPreviewSingle",
|
|
127
|
+
"searchMatch": "F1KBNa_searchMatch",
|
|
128
|
+
"mdPreviewDouble": "F1KBNa_mdPreviewDouble",
|
|
129
|
+
"missing": "F1KBNa_missing",
|
|
130
|
+
"badgeLabel": "F1KBNa_badgeLabel",
|
|
131
|
+
"treeNote": "F1KBNa_treeNote",
|
|
132
|
+
"groupHead": "F1KBNa_groupHead",
|
|
133
|
+
"addButton": "F1KBNa_addButton",
|
|
134
|
+
"kindHint": "F1KBNa_kindHint",
|
|
135
|
+
"diffFlashShake": "F1KBNa_diffFlashShake",
|
|
136
|
+
"markerDel": "F1KBNa_markerDel",
|
|
137
|
+
"splitLadd": "F1KBNa_splitLadd",
|
|
138
|
+
"treeCaret": "F1KBNa_treeCaret",
|
|
139
|
+
"settingsRowDesc": "F1KBNa_settingsRowDesc",
|
|
140
|
+
"stepper": "F1KBNa_stepper",
|
|
141
|
+
"emptyActions": "F1KBNa_emptyActions",
|
|
142
|
+
"pickerIcon": "F1KBNa_pickerIcon",
|
|
143
|
+
"pickerName": "F1KBNa_pickerName",
|
|
144
|
+
"close": "F1KBNa_close",
|
|
145
|
+
"confirmCard": "F1KBNa_confirmCard",
|
|
146
|
+
"settingsGroupChevron": "F1KBNa_settingsGroupChevron",
|
|
147
|
+
"rowPath": "F1KBNa_rowPath",
|
|
148
|
+
"rail": "F1KBNa_rail",
|
|
149
|
+
"stepperValue": "F1KBNa_stepperValue",
|
|
150
|
+
"toggleOn": "F1KBNa_toggleOn",
|
|
151
|
+
"treeName": "F1KBNa_treeName",
|
|
152
|
+
"statusAction": "F1KBNa_statusAction",
|
|
153
|
+
"badge": "F1KBNa_badge",
|
|
154
|
+
"actionPrimary": "F1KBNa_actionPrimary",
|
|
155
|
+
"splitRadd": "F1KBNa_splitRadd",
|
|
156
|
+
"diffPreviewScroll": "F1KBNa_diffPreviewScroll",
|
|
157
|
+
"colorValue": "F1KBNa_colorValue",
|
|
158
|
+
"pickerCheck": "F1KBNa_pickerCheck",
|
|
159
|
+
"title": "F1KBNa_title",
|
|
160
|
+
"group": "F1KBNa_group",
|
|
161
|
+
"diffBodyWrap": "F1KBNa_diffBodyWrap",
|
|
162
|
+
"pickerNote": "F1KBNa_pickerNote",
|
|
163
|
+
"intraAdd": "F1KBNa_intraAdd",
|
|
164
|
+
"settingsPage": "F1KBNa_settingsPage",
|
|
165
|
+
"settingsGroupHeader": "F1KBNa_settingsGroupHeader",
|
|
166
|
+
"colorPickerInputRow": "F1KBNa_colorPickerInputRow",
|
|
167
|
+
"diffPreview": "F1KBNa_diffPreview",
|
|
168
|
+
"colorPickerTrigger": "F1KBNa_colorPickerTrigger",
|
|
169
|
+
"colorPickerChevron": "F1KBNa_colorPickerChevron",
|
|
170
|
+
"diffHeader": "F1KBNa_diffHeader",
|
|
171
|
+
"gutter": "F1KBNa_gutter",
|
|
172
|
+
"add": "F1KBNa_add",
|
|
173
|
+
"fileListFloat": "F1KBNa_fileListFloat",
|
|
174
|
+
"rows": "F1KBNa_rows",
|
|
175
|
+
"context": "F1KBNa_context",
|
|
176
|
+
"noteCentered": "F1KBNa_noteCentered",
|
|
177
|
+
"colorPicker": "F1KBNa_colorPicker",
|
|
178
|
+
"overviewMarker": "F1KBNa_overviewMarker",
|
|
179
|
+
"bulkActions": "F1KBNa_bulkActions",
|
|
180
|
+
"diffBodySplit": "F1KBNa_diffBodySplit",
|
|
181
|
+
"missingHint": "F1KBNa_missingHint",
|
|
182
|
+
"searchToggle": "F1KBNa_searchToggle",
|
|
183
|
+
"langSelect": "F1KBNa_langSelect",
|
|
184
|
+
"fileList": "F1KBNa_fileList",
|
|
185
|
+
"colorSvCursor": "F1KBNa_colorSvCursor",
|
|
186
|
+
"settingsGroupDesc": "F1KBNa_settingsGroupDesc",
|
|
187
|
+
"split": "F1KBNa_split",
|
|
188
|
+
"splitLdel": "F1KBNa_splitLdel",
|
|
189
|
+
"noticeButton": "F1KBNa_noticeButton",
|
|
190
|
+
"detail": "F1KBNa_detail",
|
|
191
|
+
"settingsSelectorChevron": "F1KBNa_settingsSelectorChevron",
|
|
192
|
+
"importNote": "F1KBNa_importNote",
|
|
193
|
+
"rowHead": "F1KBNa_rowHead",
|
|
194
|
+
"wrapActive": "F1KBNa_wrapActive",
|
|
195
|
+
"emptyState": "F1KBNa_emptyState",
|
|
196
|
+
"searchToggleOn": "F1KBNa_searchToggleOn",
|
|
197
|
+
"colorSwatch": "F1KBNa_colorSwatch",
|
|
198
|
+
"header": "F1KBNa_header",
|
|
199
|
+
"resizeHandle": "F1KBNa_resizeHandle",
|
|
200
|
+
"colorHue": "F1KBNa_colorHue",
|
|
201
|
+
"lines": "F1KBNa_lines",
|
|
202
|
+
"diffPath": "F1KBNa_diffPath",
|
|
203
|
+
"diffActions": "F1KBNa_diffActions",
|
|
204
|
+
"badgeCount": "F1KBNa_badgeCount",
|
|
205
|
+
"toggleThumb": "F1KBNa_toggleThumb",
|
|
206
|
+
"subline": "F1KBNa_subline"
|
|
207
|
+
};
|
|
208
|
+
//#endregion
|
|
209
|
+
//#region lib/types/client/PathPicker.js
|
|
210
|
+
/**
|
|
211
|
+
* The add-path dialog: pick one path in a lazily-loaded workspace tree — or type
|
|
212
|
+
* it outright — then press Add. Nothing is added by pointing at it: the path and
|
|
213
|
+
* the no-change box are prepared first, and the host judges them once the button
|
|
214
|
+
* is pressed.
|
|
215
|
+
*
|
|
216
|
+
* The path box is the single source of truth for the selection: a tree row is
|
|
217
|
+
* highlighted while its path is the box's text, and editing the text by hand
|
|
218
|
+
* simply leaves no row highlighted.
|
|
219
|
+
*
|
|
220
|
+
* The dialog is a modal inside the panel: it owns Escape while it is on screen
|
|
221
|
+
* (the panel's own chords stand down, see `pathPickerOpen`).
|
|
222
|
+
* @module dsh-diff-approval/client/PathPicker
|
|
223
|
+
*/
|
|
224
|
+
/** Whether this panel's add-path dialog is on screen. The panel's global chords
|
|
225
|
+
* consult this so the modal owns the keyboard while it is open. */
|
|
226
|
+
function pathPickerOpen() {
|
|
227
|
+
return typeof document !== "undefined" && document.querySelector("[data-diff-path-picker]") !== null;
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* The file glyph the icon library lacks (it ships folders only): a page with a
|
|
231
|
+
* folded corner, drawn on the same 16px grid as the folder icons.
|
|
232
|
+
* @returns the 16px file glyph.
|
|
233
|
+
*/
|
|
234
|
+
function FileIcon() {
|
|
235
|
+
return (0, react_jsx_runtime.jsx)("svg", {
|
|
236
|
+
width: 16,
|
|
237
|
+
height: 16,
|
|
238
|
+
viewBox: "0 0 16 16",
|
|
239
|
+
fill: "none",
|
|
240
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
241
|
+
"aria-hidden": "true",
|
|
242
|
+
"data-diff-file-icon": true,
|
|
243
|
+
children: (0, react_jsx_runtime.jsx)("path", {
|
|
244
|
+
fill: "currentColor",
|
|
245
|
+
fillRule: "evenodd",
|
|
246
|
+
clipRule: "evenodd",
|
|
247
|
+
d: "M3.5 1.5h5l4 4v9h-9zM8.5 1.5v4h4z"
|
|
248
|
+
})
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* The add-path dialog.
|
|
253
|
+
* @param props - the workspace root, the host calls, and the close/toast sinks.
|
|
254
|
+
* @returns the modal (rendered only while open by its parent).
|
|
255
|
+
*/
|
|
256
|
+
function PathPicker({ rootPath, onBrowse, onAdd, onToast, onClose, t }) {
|
|
257
|
+
/** The path box: the target, and the tree's selection, in one string. */
|
|
258
|
+
const [path, setPath] = (0, react.useState)("");
|
|
259
|
+
const [levels, setLevels] = (0, react.useState)({});
|
|
260
|
+
const [expanded, setExpanded] = (0, react.useState)(() => /* @__PURE__ */ new Set([""]));
|
|
261
|
+
/** Every open starts unticked: the box is a per-add decision. */
|
|
262
|
+
const [includeUnchanged, setIncludeUnchanged] = (0, react.useState)(false);
|
|
263
|
+
const [busy, setBusy] = (0, react.useState)(false);
|
|
264
|
+
const inputRef = (0, react.useRef)(null);
|
|
265
|
+
const browseRef = (0, react.useRef)(onBrowse);
|
|
266
|
+
browseRef.current = onBrowse;
|
|
267
|
+
const addRef = (0, react.useRef)(onAdd);
|
|
268
|
+
addRef.current = onAdd;
|
|
269
|
+
const toastRef = (0, react.useRef)(onToast);
|
|
270
|
+
toastRef.current = onToast;
|
|
271
|
+
const closeRef = (0, react.useRef)(onClose);
|
|
272
|
+
closeRef.current = onClose;
|
|
273
|
+
/** Load one directory level; `dir` is workspace-relative (`''` is the root). */
|
|
274
|
+
const load = (0, react.useCallback)(async (dir) => {
|
|
275
|
+
setLevels((prev) => ({
|
|
276
|
+
...prev,
|
|
277
|
+
[dir]: { status: "loading" }
|
|
278
|
+
}));
|
|
279
|
+
try {
|
|
280
|
+
const value = await browseRef.current(dir === "" ? void 0 : dir);
|
|
281
|
+
setLevels((prev) => ({
|
|
282
|
+
...prev,
|
|
283
|
+
[dir]: {
|
|
284
|
+
status: "ready",
|
|
285
|
+
entries: value.entries,
|
|
286
|
+
truncated: value.truncated
|
|
287
|
+
}
|
|
288
|
+
}));
|
|
289
|
+
} catch (error) {
|
|
290
|
+
setLevels((prev) => ({
|
|
291
|
+
...prev,
|
|
292
|
+
[dir]: {
|
|
293
|
+
status: "failed",
|
|
294
|
+
error: error instanceof Error ? error.message : String(error)
|
|
295
|
+
}
|
|
296
|
+
}));
|
|
297
|
+
}
|
|
298
|
+
}, []);
|
|
299
|
+
(0, react.useEffect)(() => {
|
|
300
|
+
load("");
|
|
301
|
+
inputRef.current?.focus();
|
|
302
|
+
}, [load]);
|
|
303
|
+
(0, react.useEffect)(() => {
|
|
304
|
+
const onKeyDown = (event) => {
|
|
305
|
+
if (event.key !== "Escape") return;
|
|
306
|
+
event.preventDefault();
|
|
307
|
+
closeRef.current();
|
|
308
|
+
};
|
|
309
|
+
window.addEventListener("keydown", onKeyDown, true);
|
|
310
|
+
return () => {
|
|
311
|
+
window.removeEventListener("keydown", onKeyDown, true);
|
|
312
|
+
};
|
|
313
|
+
}, []);
|
|
314
|
+
/** Report one add outcome; only a landed add closes the dialog. */
|
|
315
|
+
const report = (value) => {
|
|
316
|
+
const toast = toastRef.current;
|
|
317
|
+
if (value.outcome === "added") {
|
|
318
|
+
toast(value.truncated === true ? t("panel.addDoneTruncated", { count: value.added }) : t("panel.addDone", { count: value.added }));
|
|
319
|
+
closeRef.current();
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
if (value.outcome === "duplicate") toast(t("panel.addDuplicate"));
|
|
323
|
+
else if (value.outcome === "unchanged") toast(t("panel.addUnchanged"));
|
|
324
|
+
else if (value.outcome === "empty") toast(t("panel.addEmpty"));
|
|
325
|
+
else if (value.outcome === "missing") toast(t("panel.addMissing"));
|
|
326
|
+
else if (value.outcome === "outside") toast(t("panel.addOutside"));
|
|
327
|
+
else if (value.outcome === "no-vcs") toast(t("panel.importNoVcs"));
|
|
328
|
+
else toast(t("panel.addFailed", { message: value.message ?? "" }));
|
|
329
|
+
};
|
|
330
|
+
/** The dialog's one action: the host judges the path here, not on selection. */
|
|
331
|
+
const submit = async () => {
|
|
332
|
+
const target = path.trim();
|
|
333
|
+
if (target === "" || busy) return;
|
|
334
|
+
setBusy(true);
|
|
335
|
+
try {
|
|
336
|
+
report(await addRef.current(target, includeUnchanged));
|
|
337
|
+
} catch (error) {
|
|
338
|
+
toastRef.current(t("panel.addFailed", { message: error instanceof Error ? error.message : String(error) }));
|
|
339
|
+
} finally {
|
|
340
|
+
setBusy(false);
|
|
341
|
+
}
|
|
342
|
+
};
|
|
343
|
+
/** Expand or collapse one directory, loading its level the first time. */
|
|
344
|
+
const toggle = (dir) => {
|
|
345
|
+
setExpanded((prev) => {
|
|
346
|
+
const next = new Set(prev);
|
|
347
|
+
if (next.has(dir)) next.delete(dir);
|
|
348
|
+
else next.add(dir);
|
|
349
|
+
return next;
|
|
350
|
+
});
|
|
351
|
+
if (levels[dir] === void 0) load(dir);
|
|
352
|
+
};
|
|
353
|
+
/** One tree row: caret, icon, name. Selecting only fills the path box. */
|
|
354
|
+
const row = (entry, depth) => {
|
|
355
|
+
const isDirectory = entry.type === "directory";
|
|
356
|
+
const isOpen = expanded.has(entry.path);
|
|
357
|
+
const selected = path === entry.path;
|
|
358
|
+
return (0, react_jsx_runtime.jsxs)("div", {
|
|
359
|
+
className: PendingPanel_module_css_default.treeRow,
|
|
360
|
+
style: { paddingLeft: `${4 + depth * 14}px` },
|
|
361
|
+
"data-diff-picker-row": entry.path,
|
|
362
|
+
"data-selected": selected ? "" : void 0,
|
|
363
|
+
children: [isDirectory ? (0, react_jsx_runtime.jsx)("button", {
|
|
364
|
+
type: "button",
|
|
365
|
+
className: PendingPanel_module_css_default.treeCaret,
|
|
366
|
+
"data-diff-picker-toggle": entry.path,
|
|
367
|
+
"aria-expanded": isOpen,
|
|
368
|
+
"aria-label": isOpen ? t("action.collapseRow") : t("action.expandRow"),
|
|
369
|
+
onClick: () => {
|
|
370
|
+
toggle(entry.path);
|
|
371
|
+
},
|
|
372
|
+
children: isOpen ? (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, { size: 14 }) : (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronRightOutline14, { size: 14 })
|
|
373
|
+
}) : (0, react_jsx_runtime.jsx)("span", {
|
|
374
|
+
className: PendingPanel_module_css_default.treeCaret,
|
|
375
|
+
"aria-hidden": "true"
|
|
376
|
+
}), (0, react_jsx_runtime.jsxs)("button", {
|
|
377
|
+
type: "button",
|
|
378
|
+
className: PendingPanel_module_css_default.treeName,
|
|
379
|
+
"data-diff-picker-select": entry.path,
|
|
380
|
+
"aria-pressed": selected,
|
|
381
|
+
onClick: () => {
|
|
382
|
+
setPath(entry.path);
|
|
383
|
+
},
|
|
384
|
+
children: [isDirectory ? isOpen ? (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconFolderOpen16, {
|
|
385
|
+
size: 16,
|
|
386
|
+
className: PendingPanel_module_css_default.pickerIcon
|
|
387
|
+
}) : (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconFolderClose16, {
|
|
388
|
+
size: 16,
|
|
389
|
+
className: PendingPanel_module_css_default.pickerIcon
|
|
390
|
+
}) : (0, react_jsx_runtime.jsx)("span", {
|
|
391
|
+
className: PendingPanel_module_css_default.pickerIcon,
|
|
392
|
+
children: (0, react_jsx_runtime.jsx)(FileIcon, {})
|
|
393
|
+
}), (0, react_jsx_runtime.jsx)("span", {
|
|
394
|
+
className: PendingPanel_module_css_default.pickerName,
|
|
395
|
+
children: entry.name
|
|
396
|
+
})]
|
|
397
|
+
})]
|
|
398
|
+
}, entry.path);
|
|
399
|
+
};
|
|
400
|
+
/** One loaded level's children, expanded directories recursing into their own.
|
|
401
|
+
* `trail` carries the branch's ancestors: a directory that lists itself (a
|
|
402
|
+
* symlink loop) must stop the walk rather than recurse forever. */
|
|
403
|
+
const levelRows = (dir, depth, trail) => {
|
|
404
|
+
const level = levels[dir];
|
|
405
|
+
if (level === void 0 || level.status === "loading") return (0, react_jsx_runtime.jsx)("p", {
|
|
406
|
+
className: PendingPanel_module_css_default.treeNote,
|
|
407
|
+
style: { paddingLeft: `${10 + depth * 14}px` },
|
|
408
|
+
children: t("panel.loading")
|
|
409
|
+
});
|
|
410
|
+
if (level.status === "failed") return (0, react_jsx_runtime.jsx)("p", {
|
|
411
|
+
className: PendingPanel_module_css_default.treeNote,
|
|
412
|
+
style: { paddingLeft: `${10 + depth * 14}px` },
|
|
413
|
+
role: "alert",
|
|
414
|
+
children: level.error
|
|
415
|
+
});
|
|
416
|
+
if (level.entries.length === 0) return (0, react_jsx_runtime.jsx)("p", {
|
|
417
|
+
className: PendingPanel_module_css_default.treeNote,
|
|
418
|
+
style: { paddingLeft: `${10 + depth * 14}px` },
|
|
419
|
+
children: t("panel.addNoEntries")
|
|
420
|
+
});
|
|
421
|
+
return (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [level.entries.map((entry) => {
|
|
422
|
+
const descends = entry.type === "directory" && expanded.has(entry.path) && !trail.has(entry.path);
|
|
423
|
+
return (0, react_jsx_runtime.jsxs)("div", { children: [row(entry, depth), descends ? levelRows(entry.path, depth + 1, /* @__PURE__ */ new Set([...trail, entry.path])) : null] }, entry.path);
|
|
424
|
+
}), level.truncated && (0, react_jsx_runtime.jsx)("p", {
|
|
425
|
+
className: PendingPanel_module_css_default.treeNote,
|
|
426
|
+
style: { paddingLeft: `${10 + depth * 14}px` },
|
|
427
|
+
children: t("panel.addTruncated", { count: level.entries.length })
|
|
428
|
+
})] });
|
|
429
|
+
};
|
|
430
|
+
const rootSelected = rootPath !== void 0 && path === rootPath;
|
|
431
|
+
return (0, react_jsx_runtime.jsx)("div", {
|
|
432
|
+
className: PendingPanel_module_css_default.confirmBackdrop,
|
|
433
|
+
"data-diff-path-picker": true,
|
|
434
|
+
children: (0, react_jsx_runtime.jsxs)("div", {
|
|
435
|
+
className: PendingPanel_module_css_default.pickerCard,
|
|
436
|
+
role: "dialog",
|
|
437
|
+
"aria-modal": "true",
|
|
438
|
+
"aria-label": t("panel.addTitle"),
|
|
439
|
+
children: [
|
|
440
|
+
(0, react_jsx_runtime.jsx)("span", {
|
|
441
|
+
className: PendingPanel_module_css_default.pickerTitle,
|
|
442
|
+
children: t("panel.addTitle")
|
|
443
|
+
}),
|
|
444
|
+
(0, react_jsx_runtime.jsx)("div", {
|
|
445
|
+
className: PendingPanel_module_css_default.pickerForm,
|
|
446
|
+
children: (0, react_jsx_runtime.jsx)("input", {
|
|
447
|
+
ref: inputRef,
|
|
448
|
+
className: PendingPanel_module_css_default.pickerInput,
|
|
449
|
+
"data-diff-picker-input": true,
|
|
450
|
+
value: path,
|
|
451
|
+
placeholder: t("panel.addPathPlaceholder"),
|
|
452
|
+
onChange: (event) => {
|
|
453
|
+
setPath(event.target.value);
|
|
454
|
+
},
|
|
455
|
+
onKeyDown: (event) => {
|
|
456
|
+
if (event.key !== "Enter") return;
|
|
457
|
+
event.preventDefault();
|
|
458
|
+
submit();
|
|
459
|
+
}
|
|
460
|
+
})
|
|
461
|
+
}),
|
|
462
|
+
(0, react_jsx_runtime.jsxs)("div", {
|
|
463
|
+
className: PendingPanel_module_css_default.tree,
|
|
464
|
+
"data-diff-picker-tree": true,
|
|
465
|
+
children: [(0, react_jsx_runtime.jsxs)("div", {
|
|
466
|
+
className: PendingPanel_module_css_default.treeRow,
|
|
467
|
+
style: { paddingLeft: "4px" },
|
|
468
|
+
"data-diff-picker-row": rootPath ?? "",
|
|
469
|
+
"data-selected": rootSelected ? "" : void 0,
|
|
470
|
+
children: [(0, react_jsx_runtime.jsx)("button", {
|
|
471
|
+
type: "button",
|
|
472
|
+
className: PendingPanel_module_css_default.treeCaret,
|
|
473
|
+
"data-diff-picker-toggle": "",
|
|
474
|
+
"aria-expanded": expanded.has(""),
|
|
475
|
+
"aria-label": expanded.has("") ? t("action.collapseRow") : t("action.expandRow"),
|
|
476
|
+
onClick: () => {
|
|
477
|
+
toggle("");
|
|
478
|
+
},
|
|
479
|
+
children: expanded.has("") ? (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, { size: 14 }) : (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronRightOutline14, { size: 14 })
|
|
480
|
+
}), (0, react_jsx_runtime.jsxs)("button", {
|
|
481
|
+
type: "button",
|
|
482
|
+
className: PendingPanel_module_css_default.treeName,
|
|
483
|
+
"data-diff-picker-select": "",
|
|
484
|
+
"aria-pressed": rootSelected,
|
|
485
|
+
disabled: rootPath === void 0,
|
|
486
|
+
onClick: () => {
|
|
487
|
+
setPath(rootPath ?? "");
|
|
488
|
+
},
|
|
489
|
+
children: [expanded.has("") ? (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconFolderOpen16, {
|
|
490
|
+
size: 16,
|
|
491
|
+
className: PendingPanel_module_css_default.pickerIcon
|
|
492
|
+
}) : (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconFolderClose16, {
|
|
493
|
+
size: 16,
|
|
494
|
+
className: PendingPanel_module_css_default.pickerIcon
|
|
495
|
+
}), (0, react_jsx_runtime.jsx)("span", {
|
|
496
|
+
className: PendingPanel_module_css_default.pickerName,
|
|
497
|
+
children: t("panel.addRoot")
|
|
498
|
+
})]
|
|
499
|
+
})]
|
|
500
|
+
}), expanded.has("") ? levelRows("", 1, /* @__PURE__ */ new Set([""])) : null]
|
|
501
|
+
}),
|
|
502
|
+
(0, react_jsx_runtime.jsx)("p", {
|
|
503
|
+
className: PendingPanel_module_css_default.pickerNote,
|
|
504
|
+
children: t("panel.addHint")
|
|
505
|
+
}),
|
|
506
|
+
(0, react_jsx_runtime.jsxs)("div", {
|
|
507
|
+
className: PendingPanel_module_css_default.confirmActions,
|
|
508
|
+
children: [
|
|
509
|
+
(0, react_jsx_runtime.jsxs)("label", {
|
|
510
|
+
className: PendingPanel_module_css_default.pickerCheck,
|
|
511
|
+
children: [(0, react_jsx_runtime.jsx)("input", {
|
|
512
|
+
type: "checkbox",
|
|
513
|
+
"data-diff-picker-unchanged": true,
|
|
514
|
+
checked: includeUnchanged,
|
|
515
|
+
onChange: (event) => {
|
|
516
|
+
setIncludeUnchanged(event.target.checked);
|
|
517
|
+
}
|
|
518
|
+
}), t("panel.addIncludeUnchanged")]
|
|
519
|
+
}),
|
|
520
|
+
(0, react_jsx_runtime.jsx)("button", {
|
|
521
|
+
type: "button",
|
|
522
|
+
className: PendingPanel_module_css_default.action,
|
|
523
|
+
"data-diff-picker-cancel": true,
|
|
524
|
+
onClick: () => {
|
|
525
|
+
closeRef.current();
|
|
526
|
+
},
|
|
527
|
+
children: t("action.cancel")
|
|
528
|
+
}),
|
|
529
|
+
(0, react_jsx_runtime.jsx)("button", {
|
|
530
|
+
type: "button",
|
|
531
|
+
className: `${PendingPanel_module_css_default.action} ${PendingPanel_module_css_default.actionPrimary}`,
|
|
532
|
+
"data-diff-picker-submit": true,
|
|
533
|
+
disabled: busy || path.trim() === "",
|
|
534
|
+
onClick: () => {
|
|
535
|
+
submit();
|
|
536
|
+
},
|
|
537
|
+
children: t("panel.addPathGo")
|
|
538
|
+
})
|
|
539
|
+
]
|
|
540
|
+
}),
|
|
541
|
+
(0, react_jsx_runtime.jsx)("button", {
|
|
542
|
+
type: "button",
|
|
543
|
+
className: PendingPanel_module_css_default.pickerClose,
|
|
544
|
+
"data-diff-picker-close": true,
|
|
545
|
+
"aria-label": t("action.close"),
|
|
546
|
+
onClick: () => {
|
|
547
|
+
closeRef.current();
|
|
548
|
+
},
|
|
549
|
+
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCloseOutline16, { size: 14 })
|
|
550
|
+
})
|
|
551
|
+
]
|
|
552
|
+
})
|
|
553
|
+
});
|
|
554
|
+
}
|
|
555
|
+
//#endregion
|
|
23
556
|
//#region node_modules/.pnpm/diff@9.0.0/node_modules/diff/libesm/diff/base.js
|
|
24
557
|
var Diff = class {
|
|
25
558
|
diff(oldStr, newStr, options = {}) {
|
|
@@ -625,25 +1158,91 @@ window.__ModuleLoader__.load({
|
|
|
625
1158
|
}
|
|
626
1159
|
}
|
|
627
1160
|
/**
|
|
628
|
-
* Split `text` into an array of lines, including the trailing newline character (where present)
|
|
629
|
-
*/
|
|
630
|
-
function splitLines$1(text) {
|
|
631
|
-
const hasTrailingNl = text.endsWith("\n");
|
|
632
|
-
const result = text.split("\n").map((line) => line + "\n");
|
|
633
|
-
if (hasTrailingNl) result.pop();
|
|
634
|
-
else result.push(result.pop().slice(0, -1));
|
|
635
|
-
return result;
|
|
636
|
-
}
|
|
637
|
-
//#endregion
|
|
638
|
-
//#region lib/types/client/whole-file-diff.js
|
|
639
|
-
/**
|
|
640
|
-
* Whole-file diff model for the pending review viewer: one unified row list
|
|
641
|
-
* over the complete old and new contents, so the viewer renders the entire
|
|
642
|
-
* file with changed lines marked and unchanged lines as context. Pure
|
|
643
|
-
* derivation; the panel owns rendering. The `diff` package is browser-safe.
|
|
644
|
-
* @module dsh-diff-approval/client/whole-file-diff
|
|
645
|
-
*/
|
|
646
|
-
/**
|
|
1161
|
+
* Split `text` into an array of lines, including the trailing newline character (where present)
|
|
1162
|
+
*/
|
|
1163
|
+
function splitLines$1(text) {
|
|
1164
|
+
const hasTrailingNl = text.endsWith("\n");
|
|
1165
|
+
const result = text.split("\n").map((line) => line + "\n");
|
|
1166
|
+
if (hasTrailingNl) result.pop();
|
|
1167
|
+
else result.push(result.pop().slice(0, -1));
|
|
1168
|
+
return result;
|
|
1169
|
+
}
|
|
1170
|
+
//#endregion
|
|
1171
|
+
//#region lib/types/client/whole-file-diff.js
|
|
1172
|
+
/**
|
|
1173
|
+
* Whole-file diff model for the pending review viewer: one unified row list
|
|
1174
|
+
* over the complete old and new contents, so the viewer renders the entire
|
|
1175
|
+
* file with changed lines marked and unchanged lines as context. Pure
|
|
1176
|
+
* derivation; the panel owns rendering. The `diff` package is browser-safe.
|
|
1177
|
+
* @module dsh-diff-approval/client/whole-file-diff
|
|
1178
|
+
*/
|
|
1179
|
+
/** Split a row list into maximal runs of non-context rows. */
|
|
1180
|
+
function changeBlocksOf(diff) {
|
|
1181
|
+
const blocks = [];
|
|
1182
|
+
let start = -1;
|
|
1183
|
+
diff.rows.forEach((row, index) => {
|
|
1184
|
+
if (row.kind !== "context") {
|
|
1185
|
+
if (start === -1) start = index;
|
|
1186
|
+
} else if (start !== -1) {
|
|
1187
|
+
blocks.push({
|
|
1188
|
+
start,
|
|
1189
|
+
end: index - 1
|
|
1190
|
+
});
|
|
1191
|
+
start = -1;
|
|
1192
|
+
}
|
|
1193
|
+
});
|
|
1194
|
+
if (start !== -1) blocks.push({
|
|
1195
|
+
start,
|
|
1196
|
+
end: diff.rows.length - 1
|
|
1197
|
+
});
|
|
1198
|
+
return blocks;
|
|
1199
|
+
}
|
|
1200
|
+
/**
|
|
1201
|
+
* One diff block's old/new line ranges, 1-based inclusive, for block-level
|
|
1202
|
+
* keep/revert. A side with no lines (a pure addition or deletion) is empty; its
|
|
1203
|
+
* start is that side's insertion point — the line after the surrounding context
|
|
1204
|
+
* — so the host can insert there.
|
|
1205
|
+
*
|
|
1206
|
+
* Shared by the source view and the Markdown preview: both derive blocks from
|
|
1207
|
+
* the same rows, so a preview element and a source row name the same target.
|
|
1208
|
+
* @param rows - the whole-file diff rows.
|
|
1209
|
+
* @param block - the block's row range.
|
|
1210
|
+
* @returns the old and new line ranges.
|
|
1211
|
+
*/
|
|
1212
|
+
function blockRangesOf(rows, block) {
|
|
1213
|
+
let oldStart = Infinity;
|
|
1214
|
+
let oldEnd = -Infinity;
|
|
1215
|
+
let newStart = Infinity;
|
|
1216
|
+
let newEnd = -Infinity;
|
|
1217
|
+
for (let index = block.start; index <= block.end; index++) {
|
|
1218
|
+
const row = rows[index];
|
|
1219
|
+
if (row === void 0) continue;
|
|
1220
|
+
if (row.oldLine !== void 0) {
|
|
1221
|
+
oldStart = Math.min(oldStart, row.oldLine);
|
|
1222
|
+
oldEnd = Math.max(oldEnd, row.oldLine);
|
|
1223
|
+
}
|
|
1224
|
+
if (row.newLine !== void 0) {
|
|
1225
|
+
newStart = Math.min(newStart, row.newLine);
|
|
1226
|
+
newEnd = Math.max(newEnd, row.newLine);
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
const before = rows[block.start - 1];
|
|
1230
|
+
if (oldStart === Infinity) {
|
|
1231
|
+
oldStart = (before?.oldLine ?? 0) + 1;
|
|
1232
|
+
oldEnd = oldStart - 1;
|
|
1233
|
+
}
|
|
1234
|
+
if (newStart === Infinity) {
|
|
1235
|
+
newStart = (before?.newLine ?? 0) + 1;
|
|
1236
|
+
newEnd = newStart - 1;
|
|
1237
|
+
}
|
|
1238
|
+
return {
|
|
1239
|
+
oldStart,
|
|
1240
|
+
oldEnd,
|
|
1241
|
+
newStart,
|
|
1242
|
+
newEnd
|
|
1243
|
+
};
|
|
1244
|
+
}
|
|
1245
|
+
/**
|
|
647
1246
|
* Compute the whole-file view between two contents. The context budget is the
|
|
648
1247
|
* side lengths, so every hunk covers the file and unchanged lines survive as
|
|
649
1248
|
* context rows; the `` patch marker is annotation
|
|
@@ -5224,21 +5823,28 @@ Please report this to https://github.com/markedjs/marked.`, e) {
|
|
|
5224
5823
|
function computeDiffOf(oldText, newText) {
|
|
5225
5824
|
return computeWholeFileDiff(oldText, newText);
|
|
5226
5825
|
}
|
|
5227
|
-
/** Group the whole-file diff rows into contiguous runs
|
|
5826
|
+
/** Group the whole-file diff rows into contiguous same-kind runs, tagged with
|
|
5827
|
+
* their change block (a run never spans two blocks: context separates them). */
|
|
5228
5828
|
function diffRuns(oldText, newText) {
|
|
5229
5829
|
const diff = computeDiffOf(oldText, newText);
|
|
5830
|
+
const blockOfRow = /* @__PURE__ */ new Map();
|
|
5831
|
+
changeBlocksOf(diff).forEach((block, index) => {
|
|
5832
|
+
for (let row = block.start; row <= block.end; row++) blockOfRow.set(row, index);
|
|
5833
|
+
});
|
|
5230
5834
|
const runs = [];
|
|
5231
5835
|
let current = null;
|
|
5232
|
-
|
|
5836
|
+
diff.rows.forEach((row, index) => {
|
|
5233
5837
|
const kind = row.kind === "add" ? "add" : row.kind === "del" ? "del" : "context";
|
|
5234
|
-
|
|
5838
|
+
const block = blockOfRow.get(index);
|
|
5839
|
+
if (current === null || current.kind !== kind || current.block !== block) {
|
|
5235
5840
|
current = {
|
|
5236
5841
|
kind,
|
|
5237
|
-
text: row.text
|
|
5842
|
+
text: row.text,
|
|
5843
|
+
block
|
|
5238
5844
|
};
|
|
5239
5845
|
runs.push(current);
|
|
5240
5846
|
} else current.text += `\n${row.text}`;
|
|
5241
|
-
}
|
|
5847
|
+
});
|
|
5242
5848
|
return runs;
|
|
5243
5849
|
}
|
|
5244
5850
|
/** Render one text run to sanitized HTML (Markdown). */
|
|
@@ -5291,9 +5897,15 @@ Please report this to https://github.com/markedjs/marked.`, e) {
|
|
|
5291
5897
|
add: wrapChanged(runs.add, "add")
|
|
5292
5898
|
};
|
|
5293
5899
|
}
|
|
5294
|
-
/**
|
|
5900
|
+
/** The change-block attributes one block-carrying run renders with; empty for
|
|
5901
|
+
* context, which belongs to no block and takes no keep/revert action. */
|
|
5902
|
+
function blockAttrs(run) {
|
|
5903
|
+
return run.block === void 0 ? "" : ` data-md-block="${run.block}" data-md-kind="${run.kind}"`;
|
|
5904
|
+
}
|
|
5905
|
+
/** One run rendered as a block carrying its diff-kind class and, for a changed
|
|
5906
|
+
* run, the change block it belongs to. */
|
|
5295
5907
|
function renderBlock(run) {
|
|
5296
|
-
return `<div class="${run.kind === "add" ? "mdBlock mdAdd" : run.kind === "del" ? "mdBlock mdDel" : "mdBlock"}">${renderRun(run)}</div>`;
|
|
5908
|
+
return `<div class="${run.kind === "add" ? "mdBlock mdAdd" : run.kind === "del" ? "mdBlock mdDel" : "mdBlock"}"${blockAttrs(run)}>${renderRun(run)}</div>`;
|
|
5297
5909
|
}
|
|
5298
5910
|
/**
|
|
5299
5911
|
* Pair the diff runs into aligned rows so a deletion and the addition that
|
|
@@ -5351,8 +5963,8 @@ Please report this to https://github.com/markedjs/marked.`, e) {
|
|
|
5351
5963
|
if (before !== void 0 && after !== void 0 && before.kind === "del" && after.kind === "add") {
|
|
5352
5964
|
const hl = wordHighlight(before.text, after.text);
|
|
5353
5965
|
if (hl !== void 0) {
|
|
5354
|
-
beforeHtml = `<div class="mdBlock mdDel">${renderMarkdownInline(hl.del)}</div>`;
|
|
5355
|
-
afterHtml = `<div class="mdBlock mdAdd">${renderMarkdownInline(hl.add)}</div>`;
|
|
5966
|
+
beforeHtml = `<div class="mdBlock mdDel"${blockAttrs(before)}>${renderMarkdownInline(hl.del)}</div>`;
|
|
5967
|
+
afterHtml = `<div class="mdBlock mdAdd"${blockAttrs(after)}>${renderMarkdownInline(hl.add)}</div>`;
|
|
5356
5968
|
}
|
|
5357
5969
|
}
|
|
5358
5970
|
return `<div class="mdDoubleRow"><div class="mdDoubleCol">${beforeHtml}</div><div class="mdDoubleRule"></div><div class="mdDoubleCol">${afterHtml}</div></div>`;
|
|
@@ -5448,6 +6060,51 @@ Please report this to https://github.com/markedjs/marked.`, e) {
|
|
|
5448
6060
|
}
|
|
5449
6061
|
}
|
|
5450
6062
|
//#endregion
|
|
6063
|
+
//#region lib/types/client/search.js
|
|
6064
|
+
/**
|
|
6065
|
+
* The in-file search matcher, shared by the unified diff and the split view so
|
|
6066
|
+
* both highlight and count the same occurrences.
|
|
6067
|
+
* @module dsh-diff-approval/client/search
|
|
6068
|
+
*/
|
|
6069
|
+
/** The plain search: case-insensitive, substring. */
|
|
6070
|
+
const DEFAULT_SEARCH_OPTIONS = {
|
|
6071
|
+
caseSensitive: false,
|
|
6072
|
+
wholeWord: false
|
|
6073
|
+
};
|
|
6074
|
+
/**
|
|
6075
|
+
* Whether `at` holds an identifier character (the ASCII set editors use), or
|
|
6076
|
+
* false outside the string, so a match at either edge counts as a whole word.
|
|
6077
|
+
* @param text - the searched line.
|
|
6078
|
+
* @param at - the character index to test.
|
|
6079
|
+
* @returns whether that position is part of a word.
|
|
6080
|
+
*/
|
|
6081
|
+
function isWordCharAt(text, at) {
|
|
6082
|
+
if (at < 0 || at >= text.length) return false;
|
|
6083
|
+
return /[A-Za-z0-9_]/.test(text.charAt(at));
|
|
6084
|
+
}
|
|
6085
|
+
/**
|
|
6086
|
+
* Character ranges of every occurrence of `query` in `text` that the options
|
|
6087
|
+
* accept. Case-insensitive and substring-based unless narrowed.
|
|
6088
|
+
* @param text - the line to search.
|
|
6089
|
+
* @param query - the search text; empty matches nothing.
|
|
6090
|
+
* @param options - case and whole-word narrowing.
|
|
6091
|
+
* @returns `[start, end)` ranges in ascending order.
|
|
6092
|
+
*/
|
|
6093
|
+
function matchRangesOf(text, query, options = DEFAULT_SEARCH_OPTIONS) {
|
|
6094
|
+
if (query === "") return [];
|
|
6095
|
+
const haystack = options.caseSensitive ? text : text.toLowerCase();
|
|
6096
|
+
const needle = options.caseSensitive ? query : query.toLowerCase();
|
|
6097
|
+
const out = [];
|
|
6098
|
+
let from = 0;
|
|
6099
|
+
for (;;) {
|
|
6100
|
+
const at = haystack.indexOf(needle, from);
|
|
6101
|
+
if (at === -1) return out;
|
|
6102
|
+
const end = at + needle.length;
|
|
6103
|
+
if (!options.wholeWord || !isWordCharAt(text, at - 1) && !isWordCharAt(text, end)) out.push([at, end]);
|
|
6104
|
+
from = end;
|
|
6105
|
+
}
|
|
6106
|
+
}
|
|
6107
|
+
//#endregion
|
|
5451
6108
|
//#region lib/types/client/split-diff.js
|
|
5452
6109
|
/**
|
|
5453
6110
|
* Side-by-side (split) diff model: the unified whole-file diff rows are
|
|
@@ -5553,23 +6210,23 @@ Please report this to https://github.com/markedjs/marked.`, e) {
|
|
|
5553
6210
|
};
|
|
5554
6211
|
}
|
|
5555
6212
|
/**
|
|
5556
|
-
* Pair indices whose left or right text
|
|
5557
|
-
*
|
|
5558
|
-
*
|
|
5559
|
-
*
|
|
6213
|
+
* Pair indices whose left or right text matches the query. A pair counts once
|
|
6214
|
+
* however many times the query appears, so split search highlights the whole pair
|
|
6215
|
+
* on both columns and a single "current" pair is stepped through — not each
|
|
6216
|
+
* individual left/right occurrence.
|
|
5560
6217
|
* @param pairs - the split pairs.
|
|
5561
6218
|
* @param query - the query text; an empty query matches nothing.
|
|
6219
|
+
* @param options - case and whole-word narrowing.
|
|
5562
6220
|
* @returns matching pair indices, in file order.
|
|
5563
6221
|
*/
|
|
5564
|
-
function searchPairs(pairs, query) {
|
|
6222
|
+
function searchPairs(pairs, query, options = DEFAULT_SEARCH_OPTIONS) {
|
|
5565
6223
|
if (query === "") return [];
|
|
5566
|
-
const lower = query.toLowerCase();
|
|
5567
6224
|
const matches = [];
|
|
5568
6225
|
for (let index = 0; index < pairs.length; index++) {
|
|
5569
6226
|
const p = pairs[index];
|
|
5570
6227
|
if (p === void 0) continue;
|
|
5571
|
-
if (p.left !== void 0 && p.left.text
|
|
5572
|
-
else if (p.right !== void 0 && p.right.text
|
|
6228
|
+
if (p.left !== void 0 && matchRangesOf(p.left.text, query, options).length > 0) matches.push(index);
|
|
6229
|
+
else if (p.right !== void 0 && matchRangesOf(p.right.text, query, options).length > 0) matches.push(index);
|
|
5573
6230
|
}
|
|
5574
6231
|
return matches;
|
|
5575
6232
|
}
|
|
@@ -16182,31 +16839,25 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
16182
16839
|
/**
|
|
16183
16840
|
* Tokenize guards: the viewer must never let the synchronous JS-regex engine
|
|
16184
16841
|
* block the main thread on a hostile file. Lines above the length cap are
|
|
16185
|
-
* returned plain by shiki, the per-line budget caps a single pathological
|
|
16186
|
-
*
|
|
16187
|
-
*
|
|
16188
|
-
*
|
|
16189
|
-
*
|
|
16842
|
+
* returned plain by shiki, the per-line budget caps a single pathological line,
|
|
16843
|
+
* and a window whose own text is enormous degrades to plain. Windows are what
|
|
16844
|
+
* keeps this bounded in the first place — the viewer never tokenizes more than
|
|
16845
|
+
* the lines it is about to show (see the windowed highlight hook), so a huge
|
|
16846
|
+
* file is no longer skipped wholesale: it is highlighted a screen at a time.
|
|
16190
16847
|
*/
|
|
16191
16848
|
const MAX_LINE_LENGTH = 2e3;
|
|
16192
16849
|
const TOKENIZE_TIME_LIMIT_MS = 100;
|
|
16193
|
-
const
|
|
16194
|
-
const MAX_HIGHLIGHT_LINES = 1e4;
|
|
16850
|
+
const MAX_WINDOW_CHARS = 5e5;
|
|
16195
16851
|
/**
|
|
16196
|
-
* Bounded tokenize cache keyed by the
|
|
16197
|
-
*
|
|
16198
|
-
*
|
|
16199
|
-
*
|
|
16852
|
+
* Bounded tokenize cache keyed by the window's own text and language: scrolling
|
|
16853
|
+
* back over a window that was just highlighted reuses its runs instead of
|
|
16854
|
+
* re-running the engine. Insertion-ordered, so the oldest entry is evicted first;
|
|
16855
|
+
* 24 entries bounds both memory and the eviction cost. Windows highlighted from a
|
|
16856
|
+
* saved grammar state are not cached (the state is opaque, so it cannot be part
|
|
16857
|
+
* of the key).
|
|
16200
16858
|
*/
|
|
16201
|
-
const TOKENIZE_CACHE_MAX =
|
|
16859
|
+
const TOKENIZE_CACHE_MAX = 24;
|
|
16202
16860
|
const tokenizeCache = /* @__PURE__ */ new Map();
|
|
16203
|
-
/** Whether `code` is too large to highlight safely (short-circuits fast). */
|
|
16204
|
-
function tooLargeToHighlight(code) {
|
|
16205
|
-
if (code.length > MAX_HIGHLIGHT_CHARS) return true;
|
|
16206
|
-
let newlines = 0;
|
|
16207
|
-
for (let index = 0; index < code.length; index++) if (code.charCodeAt(index) === 10 && ++newlines > MAX_HIGHLIGHT_LINES) return true;
|
|
16208
|
-
return false;
|
|
16209
|
-
}
|
|
16210
16861
|
/** All grammars this bundle registers; each entry's own `name` is the tokenize id. */
|
|
16211
16862
|
const LANGS = [
|
|
16212
16863
|
c_default,
|
|
@@ -16347,43 +16998,316 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
16347
16998
|
highlighter();
|
|
16348
16999
|
}, 0).unref?.();
|
|
16349
17000
|
/**
|
|
16350
|
-
* Tokenize `
|
|
16351
|
-
* registered grammar; `undefined` means the caller renders its plain
|
|
16352
|
-
* Each run's color is a `--shiki-*` custom property, keeping token
|
|
16353
|
-
* the harness theme's sheets. The trailing newline shiki appends as a
|
|
16354
|
-
* empty line is dropped so the run count matches the caller's own
|
|
16355
|
-
*
|
|
17001
|
+
* Tokenize the lines `[from, to)` into per-line highlighted runs when `lang`
|
|
17002
|
+
* names a registered grammar; `undefined` means the caller renders its plain
|
|
17003
|
+
* fallback. Each run's color is a `--shiki-*` custom property, keeping token
|
|
17004
|
+
* colors on the harness theme's sheets. The trailing newline shiki appends as a
|
|
17005
|
+
* final empty line is dropped so the run count matches the caller's own array.
|
|
17006
|
+
*
|
|
17007
|
+
* This is the viewer's only entry point, and it is deliberately windowed: the
|
|
17008
|
+
* code view renders a virtual window of rows, so highlighting a whole file to
|
|
17009
|
+
* show one screenful is almost all waste (measured on a 3818-line file: ~945 ms
|
|
17010
|
+
* for both sides whole-file against ~8 ms for one window). A window is continued
|
|
17011
|
+
* *exactly* from a previous one's `state`, or approximately from `context` lines
|
|
17012
|
+
* when no state is known yet — which is what lets a jump straight to the middle
|
|
17013
|
+
* of a file colour correctly without tokenizing anything above it.
|
|
17014
|
+
* @param lines - the side's lines, indexed from 0 as the caller's line numbers are.
|
|
16356
17015
|
* @param lang - the Shiki grammar id, or `undefined` for plain text.
|
|
16357
|
-
* @
|
|
17016
|
+
* @param from - the first line index to highlight (inclusive, clamped).
|
|
17017
|
+
* @param to - one past the last line index to highlight (clamped).
|
|
17018
|
+
* @param options - the grammar state to resume from and/or context lines for the grammar.
|
|
17019
|
+
* @returns the window's runs and its end state, or `undefined` when the language
|
|
17020
|
+
* is unknown, the range is empty, or the window's own text is too large.
|
|
16358
17021
|
*/
|
|
16359
|
-
function
|
|
16360
|
-
if (lang === void 0
|
|
17022
|
+
function highlightWindow(lines, lang, from, to, options = {}) {
|
|
17023
|
+
if (lang === void 0) return void 0;
|
|
16361
17024
|
if (!highlighter().getLoadedLanguages().includes(lang)) return void 0;
|
|
16362
|
-
|
|
16363
|
-
const
|
|
16364
|
-
if (
|
|
16365
|
-
const
|
|
17025
|
+
const start = Math.max(0, Math.min(from, lines.length));
|
|
17026
|
+
const end = Math.max(start, Math.min(to, lines.length));
|
|
17027
|
+
if (end === start) return void 0;
|
|
17028
|
+
const context = options.state === void 0 ? Math.max(0, options.context ?? 0) : 0;
|
|
17029
|
+
const contextFrom = Math.max(0, start - context);
|
|
17030
|
+
const text = lines.slice(contextFrom, end).join("\n");
|
|
17031
|
+
if (text.length > MAX_WINDOW_CHARS) return void 0;
|
|
17032
|
+
const spans = end - start;
|
|
17033
|
+
const cacheKey = options.state === void 0 ? `${lang}\u0000${contextFrom}\u0000${text}` : void 0;
|
|
17034
|
+
const cached = cacheKey === void 0 ? void 0 : tokenizeCache.get(cacheKey);
|
|
17035
|
+
if (cached !== void 0 && cached.length === spans) return {
|
|
17036
|
+
runs: cached,
|
|
17037
|
+
state: void 0
|
|
17038
|
+
};
|
|
17039
|
+
const { tokens, grammarState } = highlighter().codeToTokens(text, {
|
|
16366
17040
|
lang,
|
|
16367
17041
|
theme: "css-variables",
|
|
16368
17042
|
tokenizeTimeLimit: TOKENIZE_TIME_LIMIT_MS,
|
|
16369
|
-
tokenizeMaxLineLength: MAX_LINE_LENGTH
|
|
17043
|
+
tokenizeMaxLineLength: MAX_LINE_LENGTH,
|
|
17044
|
+
...options.state === void 0 ? {} : { grammarState: options.state }
|
|
16370
17045
|
});
|
|
16371
|
-
const
|
|
16372
|
-
const runs = (tokens.length > 1 && last !== void 0 && last.length === 0 ? tokens.slice(0, -1) : tokens).map((line) => line.map((token) => ({
|
|
17046
|
+
const runs = tokens.slice(start - contextFrom).map((line) => line.map((token) => ({
|
|
16373
17047
|
text: token.content,
|
|
16374
17048
|
style: { color: token.color }
|
|
16375
17049
|
})));
|
|
16376
|
-
|
|
16377
|
-
if (byLang === void 0) {
|
|
17050
|
+
if (cacheKey !== void 0) {
|
|
16378
17051
|
if (tokenizeCache.size >= TOKENIZE_CACHE_MAX) {
|
|
16379
17052
|
const oldest = tokenizeCache.keys().next().value;
|
|
16380
17053
|
if (oldest !== void 0) tokenizeCache.delete(oldest);
|
|
16381
17054
|
}
|
|
16382
|
-
|
|
16383
|
-
tokenizeCache.set(code, byLang);
|
|
17055
|
+
tokenizeCache.set(cacheKey, runs);
|
|
16384
17056
|
}
|
|
16385
|
-
|
|
16386
|
-
|
|
17057
|
+
return {
|
|
17058
|
+
runs,
|
|
17059
|
+
state: grammarState
|
|
17060
|
+
};
|
|
17061
|
+
}
|
|
17062
|
+
//#endregion
|
|
17063
|
+
//#region lib/types/client/windowed-highlight.js
|
|
17064
|
+
/**
|
|
17065
|
+
* Windowed syntax highlighting for the diff viewer.
|
|
17066
|
+
*
|
|
17067
|
+
* The code view renders a virtual window of rows, so the viewer only ever needs
|
|
17068
|
+
* the colors of the lines near the viewport. Highlighting a whole file to show
|
|
17069
|
+
* one screenful is almost all waste — measured on a 3818-line file, the two
|
|
17070
|
+
* sides whole-file cost ~945 ms of main-thread time against ~8 ms for one window
|
|
17071
|
+
* — so this hook keeps a per-side store of highlighted lines and fills it:
|
|
17072
|
+
*
|
|
17073
|
+
* 1. **The window first.** Whenever the visible line range changes, the lines
|
|
17074
|
+
* that are missing are highlighted (with a margin either side, debounced, so a
|
|
17075
|
+
* fast scroll computes the window it lands on instead of every window it
|
|
17076
|
+
* passes). A window with no state yet gets `context` lines above it so a
|
|
17077
|
+
* construct that started earlier still colours correctly.
|
|
17078
|
+
* 2. **Already-highlighted lines are kept.** Scrolling back, or nudging the
|
|
17079
|
+
* window by a few rows, finds the lines present and does no work at all.
|
|
17080
|
+
* 3. **An idle backfill** walks the file top-down in chunks when the pane reports
|
|
17081
|
+
* a real viewport, recording the exact grammar state at each chunk boundary.
|
|
17082
|
+
* Windows inside a backfilled region then resume exactly (no context guess),
|
|
17083
|
+
* and a random scroll position is coloured before the user gets there.
|
|
17084
|
+
*
|
|
17085
|
+
* Lines that have never been highlighted are holes in the returned arrays: the
|
|
17086
|
+
* viewer renders them plain, which is the honest state of the work done so far.
|
|
17087
|
+
* @module dsh-diff-approval/client/windowed-highlight
|
|
17088
|
+
*/
|
|
17089
|
+
/** Lines either side of the visible window to highlight, so a small scroll reuses
|
|
17090
|
+
* what is already there instead of starting a new pass. */
|
|
17091
|
+
const WINDOW_MARGIN_LINES = 40;
|
|
17092
|
+
/** Ceiling on one pass. A huge viewport (or a pane that reports no viewport at
|
|
17093
|
+
* all, where the virtual window falls back to the whole file) must not turn one
|
|
17094
|
+
* pass into a whole-file tokenize. */
|
|
17095
|
+
const MAX_WINDOW_LINES = 400;
|
|
17096
|
+
/** Lines above a window that only inform the grammar when no state is saved. */
|
|
17097
|
+
const CONTEXT_LINES = 40;
|
|
17098
|
+
/** A window pass waits this long, so scrolling computes where it lands, not every
|
|
17099
|
+
* window it crosses (a crossed window costs ~8 ms, a swept file ~1.3 s). */
|
|
17100
|
+
const DEBOUNCE_MS = 90;
|
|
17101
|
+
/** How much of the file the idle backfill tokenizes per step. */
|
|
17102
|
+
const BACKFILL_CHUNK_LINES = 400;
|
|
17103
|
+
/** Where the idle backfill stops, per side. A few screens ahead is where the
|
|
17104
|
+
* payoff is; past that the window pass colours whatever is looked at anyway, and
|
|
17105
|
+
* a huge file must not churn in the background indefinitely. */
|
|
17106
|
+
const BACKFILL_MAX_LINES = 6e3;
|
|
17107
|
+
/** Idle delay before the backfill's next chunk (also its `requestIdleCallback`
|
|
17108
|
+
* timeout, so a busy page still makes progress). */
|
|
17109
|
+
const BACKFILL_DELAY_MS = 120;
|
|
17110
|
+
function emptySide() {
|
|
17111
|
+
return {
|
|
17112
|
+
runs: [],
|
|
17113
|
+
states: /* @__PURE__ */ new Map(),
|
|
17114
|
+
filled: 0
|
|
17115
|
+
};
|
|
17116
|
+
}
|
|
17117
|
+
function emptyStore(key) {
|
|
17118
|
+
return {
|
|
17119
|
+
key,
|
|
17120
|
+
old: emptySide(),
|
|
17121
|
+
new: emptySide()
|
|
17122
|
+
};
|
|
17123
|
+
}
|
|
17124
|
+
/** The greatest saved state at or before `line`, or undefined when the file's own
|
|
17125
|
+
* top is the only known state. States are only written at chunk boundaries, so
|
|
17126
|
+
* the map stays tiny; the scan is over those, never over the file. */
|
|
17127
|
+
function stateAt(side, line) {
|
|
17128
|
+
let best = -1;
|
|
17129
|
+
let state;
|
|
17130
|
+
for (const [at, value] of side.states) if (at <= line && at > best) {
|
|
17131
|
+
best = at;
|
|
17132
|
+
state = value;
|
|
17133
|
+
}
|
|
17134
|
+
return state;
|
|
17135
|
+
}
|
|
17136
|
+
/**
|
|
17137
|
+
* Whether every line of `[from, to)` already has runs.
|
|
17138
|
+
* @param side - the side's store.
|
|
17139
|
+
* @param from - first line index.
|
|
17140
|
+
* @param to - one past the last line index.
|
|
17141
|
+
* @returns whether the range is fully covered.
|
|
17142
|
+
*/
|
|
17143
|
+
function covered(side, from, to) {
|
|
17144
|
+
for (let line = from; line < to; line++) if (side.runs[line] === void 0) return false;
|
|
17145
|
+
return true;
|
|
17146
|
+
}
|
|
17147
|
+
/**
|
|
17148
|
+
* Highlight one side's line range into its store, resuming exactly from any saved
|
|
17149
|
+
* state at or above it (and from context lines when there is none).
|
|
17150
|
+
* @param side - the side's store.
|
|
17151
|
+
* @param lines - the side's lines.
|
|
17152
|
+
* @param lang - the grammar id.
|
|
17153
|
+
* @param from - first line index.
|
|
17154
|
+
* @param to - one past the last line index.
|
|
17155
|
+
* @returns whether anything was written.
|
|
17156
|
+
*/
|
|
17157
|
+
function fill(side, lines, lang, from, to) {
|
|
17158
|
+
let start = Math.max(0, Math.min(from, lines.length));
|
|
17159
|
+
let end = Math.max(0, Math.min(to, lines.length));
|
|
17160
|
+
if (end <= start) return false;
|
|
17161
|
+
while (start < end && side.runs[start] !== void 0) start++;
|
|
17162
|
+
while (end > start && side.runs[end - 1] !== void 0) end--;
|
|
17163
|
+
if (end <= start) return false;
|
|
17164
|
+
const state = stateAt(side, start);
|
|
17165
|
+
const result = highlightWindow(lines, lang, start, end, { ...state === void 0 ? { context: CONTEXT_LINES } : { state } });
|
|
17166
|
+
if (result === void 0) return false;
|
|
17167
|
+
result.runs.forEach((runs, index) => {
|
|
17168
|
+
side.runs[start + index] = runs;
|
|
17169
|
+
});
|
|
17170
|
+
side.states.set(end, result.state);
|
|
17171
|
+
return true;
|
|
17172
|
+
}
|
|
17173
|
+
/**
|
|
17174
|
+
* Keep syntax highlighting for the pane's visible window, plus (with a live
|
|
17175
|
+
* viewport) an idle top-down backfill of the rest.
|
|
17176
|
+
* @param input - the file's lines, language, and the visible line ranges.
|
|
17177
|
+
* @returns the runs per side, indexed by line - 1, with holes for unhighlighted lines.
|
|
17178
|
+
*/
|
|
17179
|
+
function useWindowedHighlight(input) {
|
|
17180
|
+
const storeRef = (0, react.useRef)(emptyStore(input.key));
|
|
17181
|
+
const [sides, setSides] = (0, react.useState)({
|
|
17182
|
+
oldRuns: [],
|
|
17183
|
+
newRuns: []
|
|
17184
|
+
});
|
|
17185
|
+
const windowPendingRef = (0, react.useRef)(false);
|
|
17186
|
+
const publish = () => {
|
|
17187
|
+
const store = storeRef.current;
|
|
17188
|
+
setSides({
|
|
17189
|
+
oldRuns: [...store.old.runs],
|
|
17190
|
+
newRuns: [...store.new.runs]
|
|
17191
|
+
});
|
|
17192
|
+
};
|
|
17193
|
+
(0, react.useEffect)(() => {
|
|
17194
|
+
if (Object.is(storeRef.current.key, input.key)) return;
|
|
17195
|
+
storeRef.current = emptyStore(input.key);
|
|
17196
|
+
setSides({
|
|
17197
|
+
oldRuns: [],
|
|
17198
|
+
newRuns: []
|
|
17199
|
+
});
|
|
17200
|
+
}, [input.key]);
|
|
17201
|
+
const { oldLines, newLines, lang, oldRange, newRange, live } = input;
|
|
17202
|
+
const oldFrom = oldRange?.from;
|
|
17203
|
+
const oldTo = oldRange?.to;
|
|
17204
|
+
const newFrom = newRange?.from;
|
|
17205
|
+
const newTo = newRange?.to;
|
|
17206
|
+
(0, react.useEffect)(() => {
|
|
17207
|
+
const store = storeRef.current;
|
|
17208
|
+
if (!Object.is(store.key, input.key)) return;
|
|
17209
|
+
const pending = [{
|
|
17210
|
+
side: store.old,
|
|
17211
|
+
lines: oldLines,
|
|
17212
|
+
range: oldRange
|
|
17213
|
+
}, {
|
|
17214
|
+
side: store.new,
|
|
17215
|
+
lines: newLines,
|
|
17216
|
+
range: newRange
|
|
17217
|
+
}].filter(({ side, lines, range }) => {
|
|
17218
|
+
if (range === void 0) return false;
|
|
17219
|
+
return !covered(side, Math.max(0, range.from - 1), Math.min(lines.length, range.to));
|
|
17220
|
+
});
|
|
17221
|
+
if (pending.length === 0) {
|
|
17222
|
+
windowPendingRef.current = false;
|
|
17223
|
+
return;
|
|
17224
|
+
}
|
|
17225
|
+
windowPendingRef.current = true;
|
|
17226
|
+
const first = store.old.runs.length === 0 && store.new.runs.length === 0;
|
|
17227
|
+
const timer = window.setTimeout(() => {
|
|
17228
|
+
windowPendingRef.current = false;
|
|
17229
|
+
if (!Object.is(storeRef.current.key, input.key)) return;
|
|
17230
|
+
let wrote = false;
|
|
17231
|
+
for (const { side, lines, range } of pending) {
|
|
17232
|
+
if (range === void 0) continue;
|
|
17233
|
+
const visibleFrom = Math.max(0, range.from - 1);
|
|
17234
|
+
const visibleTo = Math.min(lines.length, range.to);
|
|
17235
|
+
const from = Math.max(0, visibleFrom - WINDOW_MARGIN_LINES);
|
|
17236
|
+
const to = Math.min(lines.length, visibleTo + WINDOW_MARGIN_LINES, from + MAX_WINDOW_LINES);
|
|
17237
|
+
wrote = fill(side, lines, lang, from, to) || wrote;
|
|
17238
|
+
}
|
|
17239
|
+
if (wrote) publish();
|
|
17240
|
+
}, first ? 0 : DEBOUNCE_MS);
|
|
17241
|
+
return () => {
|
|
17242
|
+
window.clearTimeout(timer);
|
|
17243
|
+
windowPendingRef.current = false;
|
|
17244
|
+
};
|
|
17245
|
+
}, [
|
|
17246
|
+
input.key,
|
|
17247
|
+
oldLines,
|
|
17248
|
+
newLines,
|
|
17249
|
+
lang,
|
|
17250
|
+
oldFrom,
|
|
17251
|
+
oldTo,
|
|
17252
|
+
newFrom,
|
|
17253
|
+
newTo,
|
|
17254
|
+
oldRange,
|
|
17255
|
+
newRange
|
|
17256
|
+
]);
|
|
17257
|
+
(0, react.useEffect)(() => {
|
|
17258
|
+
if (!live) return;
|
|
17259
|
+
let cancelled = false;
|
|
17260
|
+
let timer;
|
|
17261
|
+
let idleHandle;
|
|
17262
|
+
const requestIdle = window.requestIdleCallback;
|
|
17263
|
+
const step = () => {
|
|
17264
|
+
if (cancelled || windowPendingRef.current) {
|
|
17265
|
+
schedule();
|
|
17266
|
+
return;
|
|
17267
|
+
}
|
|
17268
|
+
const store = storeRef.current;
|
|
17269
|
+
if (!Object.is(store.key, input.key)) return;
|
|
17270
|
+
const next = [{
|
|
17271
|
+
side: store.old,
|
|
17272
|
+
lines: oldLines
|
|
17273
|
+
}, {
|
|
17274
|
+
side: store.new,
|
|
17275
|
+
lines: newLines
|
|
17276
|
+
}].sort((a, b) => a.side.filled / (a.lines.length || 1) - b.side.filled / (b.lines.length || 1)).find(({ side, lines }) => side.filled < Math.min(lines.length, BACKFILL_MAX_LINES));
|
|
17277
|
+
if (next === void 0) return;
|
|
17278
|
+
const to = Math.min(next.lines.length, next.side.filled + BACKFILL_CHUNK_LINES, BACKFILL_MAX_LINES);
|
|
17279
|
+
const state = stateAt(next.side, next.side.filled);
|
|
17280
|
+
const result = highlightWindow(next.lines, lang, next.side.filled, to, { state });
|
|
17281
|
+
if (result !== void 0) {
|
|
17282
|
+
result.runs.forEach((runs, index) => {
|
|
17283
|
+
next.side.runs[next.side.filled + index] = runs;
|
|
17284
|
+
});
|
|
17285
|
+
next.side.states.set(to, result.state);
|
|
17286
|
+
next.side.filled = to;
|
|
17287
|
+
publish();
|
|
17288
|
+
} else next.side.filled = to;
|
|
17289
|
+
schedule();
|
|
17290
|
+
};
|
|
17291
|
+
const schedule = () => {
|
|
17292
|
+
if (cancelled) return;
|
|
17293
|
+
if (requestIdle !== void 0) idleHandle = requestIdle(step, { timeout: BACKFILL_DELAY_MS });
|
|
17294
|
+
else timer = window.setTimeout(step, BACKFILL_DELAY_MS);
|
|
17295
|
+
};
|
|
17296
|
+
schedule();
|
|
17297
|
+
return () => {
|
|
17298
|
+
cancelled = true;
|
|
17299
|
+
if (timer !== void 0) window.clearTimeout(timer);
|
|
17300
|
+
const cancelIdle = window.cancelIdleCallback;
|
|
17301
|
+
if (idleHandle !== void 0 && cancelIdle !== void 0) cancelIdle(idleHandle);
|
|
17302
|
+
};
|
|
17303
|
+
}, [
|
|
17304
|
+
input.key,
|
|
17305
|
+
live,
|
|
17306
|
+
oldLines,
|
|
17307
|
+
newLines,
|
|
17308
|
+
lang
|
|
17309
|
+
]);
|
|
17310
|
+
return sides;
|
|
16387
17311
|
}
|
|
16388
17312
|
//#endregion
|
|
16389
17313
|
//#region lib/types/client/lang.js
|
|
@@ -16442,10 +17366,19 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
16442
17366
|
* @returns the language id, or `undefined` when unknown.
|
|
16443
17367
|
*/
|
|
16444
17368
|
function langFromPath(path) {
|
|
16445
|
-
const
|
|
16446
|
-
|
|
16447
|
-
|
|
16448
|
-
|
|
17369
|
+
const suffix = suffixOfPath(path);
|
|
17370
|
+
return suffix === void 0 ? void 0 : EXTENSION_LANGS[suffix];
|
|
17371
|
+
}
|
|
17372
|
+
/**
|
|
17373
|
+
* The lowercase extension of a file path, without the dot — the key a manual
|
|
17374
|
+
* language choice is remembered under. A name with no dot has no suffix, so a
|
|
17375
|
+
* per-suffix preference never leaks onto every extension-less file at once.
|
|
17376
|
+
* @param path - the file path, any separator style.
|
|
17377
|
+
* @returns the suffix, or `undefined` when the name carries none.
|
|
17378
|
+
*/
|
|
17379
|
+
function suffixOfPath(path) {
|
|
17380
|
+
const suffix = /\.([^./\\]+)$/.exec(path)?.[1];
|
|
17381
|
+
return suffix === void 0 || suffix === "" ? void 0 : suffix.toLowerCase();
|
|
16449
17382
|
}
|
|
16450
17383
|
//#endregion
|
|
16451
17384
|
//#region lib/types/client/reference.js
|
|
@@ -16697,6 +17630,9 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
16697
17630
|
/** Client preferences for the review panel, persisted in localStorage. */
|
|
16698
17631
|
const PASTE_ON_COPY_KEY = "diff-approval:paste-on-copy";
|
|
16699
17632
|
const IMPORT_UNTRACKED_KEY = "diff-approval:import-untracked";
|
|
17633
|
+
const CONFIRM_FILE_REMOVE_KEY = "diff-approval:confirm-file-remove";
|
|
17634
|
+
const SEARCH_CASE_KEY = "diff-approval:search-case";
|
|
17635
|
+
const SEARCH_WORD_KEY = "diff-approval:search-word";
|
|
16700
17636
|
const TAB_WIDTH_KEY = "diff-approval:tab-size";
|
|
16701
17637
|
const SPLIT_MODE_KEY = "diff-approval:split-mode";
|
|
16702
17638
|
const MD_PREVIEW_KEY = "diff-approval:md-preview";
|
|
@@ -16706,6 +17642,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
16706
17642
|
const DIFF_LINE_HEIGHT_KEY = "diff-approval:diff-line-height";
|
|
16707
17643
|
const DIFF_ADD_COLOR_KEY = "diff-approval:diff-add-color";
|
|
16708
17644
|
const DIFF_DEL_COLOR_KEY = "diff-approval:diff-del-color";
|
|
17645
|
+
const LANG_BY_SUFFIX_KEY = "diff-approval:lang-by-suffix";
|
|
16709
17646
|
const WRAP_PREFIX = "diff-approval:wrap:";
|
|
16710
17647
|
const MD_MAX_WIDTH_MAX = 1600;
|
|
16711
17648
|
/**
|
|
@@ -16735,374 +17672,289 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
16735
17672
|
localStorage.setItem(IMPORT_UNTRACKED_KEY, value ? "1" : "0");
|
|
16736
17673
|
}
|
|
16737
17674
|
/**
|
|
16738
|
-
* Whether
|
|
16739
|
-
* Defaults to
|
|
16740
|
-
*
|
|
16741
|
-
* @
|
|
16742
|
-
* @returns whether lines wrap.
|
|
16743
|
-
*/
|
|
16744
|
-
function wrapEnabled(lang) {
|
|
16745
|
-
return localStorage.getItem(`${WRAP_PREFIX}${lang}`) === "1";
|
|
16746
|
-
}
|
|
16747
|
-
/** Persist the per-language auto-wrap preference. */
|
|
16748
|
-
function setWrapEnabled(lang, value) {
|
|
16749
|
-
localStorage.setItem(`${WRAP_PREFIX}${lang}`, value ? "1" : "0");
|
|
16750
|
-
}
|
|
16751
|
-
/**
|
|
16752
|
-
* The diff's tab width in spaces. Defaults to 4; the settings UI offers 2/4/8,
|
|
16753
|
-
* but any positive integer is accepted. This drives both the rendered
|
|
16754
|
-
* `tab-size` and the wrapped-line tab measurement, so they always agree.
|
|
16755
|
-
* @returns the number of spaces one tab advances.
|
|
16756
|
-
*/
|
|
16757
|
-
function tabWidth() {
|
|
16758
|
-
const value = Number.parseInt(localStorage.getItem(TAB_WIDTH_KEY) ?? "", 10);
|
|
16759
|
-
return Number.isInteger(value) && value > 0 ? value : 4;
|
|
16760
|
-
}
|
|
16761
|
-
/** Persist the diff's tab width (in spaces). */
|
|
16762
|
-
function setTabWidth(value) {
|
|
16763
|
-
localStorage.setItem(TAB_WIDTH_KEY, String(value));
|
|
16764
|
-
}
|
|
16765
|
-
/**
|
|
16766
|
-
* The diff code font size as a percentage of the current (theme) size. Defaults
|
|
16767
|
-
* to 100 (the current look); the settings UI steps it by ±10.
|
|
16768
|
-
* @returns the font-size scale, as a percentage (e.g. 100, 110, 90).
|
|
16769
|
-
*/
|
|
16770
|
-
function diffFontScale() {
|
|
16771
|
-
const raw = Number.parseInt(localStorage.getItem(DIFF_FONT_SCALE_KEY) ?? "", 10);
|
|
16772
|
-
if (!Number.isFinite(raw)) return 100;
|
|
16773
|
-
return Math.max(50, Math.min(200, raw));
|
|
16774
|
-
}
|
|
16775
|
-
/** Persist the diff's code font-size scale (a percentage). */
|
|
16776
|
-
function setDiffFontScale(value) {
|
|
16777
|
-
localStorage.setItem(DIFF_FONT_SCALE_KEY, String(Math.max(50, Math.min(200, value))));
|
|
16778
|
-
}
|
|
16779
|
-
/**
|
|
16780
|
-
* The diff code line height in px (the fixed row height the virtual window and
|
|
16781
|
-
* jump math are keyed to). Defaults to 22 (the pre-customization value).
|
|
16782
|
-
* @returns the line height in px.
|
|
16783
|
-
*/
|
|
16784
|
-
function diffLineHeight() {
|
|
16785
|
-
const raw = Number.parseInt(localStorage.getItem(DIFF_LINE_HEIGHT_KEY) ?? "", 10);
|
|
16786
|
-
if (!Number.isFinite(raw)) return 22;
|
|
16787
|
-
return Math.max(10, Math.min(36, raw));
|
|
16788
|
-
}
|
|
16789
|
-
/** Persist the diff's code line height (px). */
|
|
16790
|
-
function setDiffLineHeight(value) {
|
|
16791
|
-
localStorage.setItem(DIFF_LINE_HEIGHT_KEY, String(Math.max(10, Math.min(36, value))));
|
|
16792
|
-
}
|
|
16793
|
-
/** Read a theme CSS custom property, with a fallback when it is unavailable. */
|
|
16794
|
-
function themeColor(name, fallback) {
|
|
16795
|
-
if (typeof document !== "undefined") {
|
|
16796
|
-
const v = getComputedStyle(document.documentElement).getPropertyValue(name).trim();
|
|
16797
|
-
if (/^#[0-9a-f]{6}$/i.test(v)) return v;
|
|
16798
|
-
}
|
|
16799
|
-
return fallback;
|
|
16800
|
-
}
|
|
16801
|
-
/** The added-line base color the user chose, or `undefined` (theme color). */
|
|
16802
|
-
function diffAddColor() {
|
|
16803
|
-
const v = localStorage.getItem(DIFF_ADD_COLOR_KEY);
|
|
16804
|
-
return v !== null && v.length > 0 ? v : void 0;
|
|
16805
|
-
}
|
|
16806
|
-
/** Persist the added-line base color (a hex like `#22c55e`). */
|
|
16807
|
-
function setDiffAddColor(value) {
|
|
16808
|
-
localStorage.setItem(DIFF_ADD_COLOR_KEY, value);
|
|
16809
|
-
}
|
|
16810
|
-
/** The removed-line base color the user chose, or `undefined` (theme color). */
|
|
16811
|
-
function diffDelColor() {
|
|
16812
|
-
const v = localStorage.getItem(DIFF_DEL_COLOR_KEY);
|
|
16813
|
-
return v !== null && v.length > 0 ? v : void 0;
|
|
16814
|
-
}
|
|
16815
|
-
/** Persist the removed-line base color (a hex like `#ef4444`). */
|
|
16816
|
-
function setDiffDelColor(value) {
|
|
16817
|
-
localStorage.setItem(DIFF_DEL_COLOR_KEY, value);
|
|
16818
|
-
}
|
|
16819
|
-
/** The theme's added-line base color, for the settings swatch default. */
|
|
16820
|
-
function currentDiffAddColor() {
|
|
16821
|
-
return themeColor("--dsw-alias-state-success-primary", "#22c55e");
|
|
16822
|
-
}
|
|
16823
|
-
/** The theme's removed-line base color, for the settings swatch default. */
|
|
16824
|
-
function currentDiffDelColor() {
|
|
16825
|
-
return themeColor("--dsw-alias-state-error-primary", "#ef4444");
|
|
16826
|
-
}
|
|
16827
|
-
/**
|
|
16828
|
-
* Whether the whole-file diff view uses the two-column (side-by-side) layout.
|
|
16829
|
-
* Default off (single column): the unified diff. Only an explicit `'1'` enables
|
|
16830
|
-
* split mode.
|
|
16831
|
-
* @returns whether the split (two-column) diff view is used.
|
|
17675
|
+
* Whether a whole-file keep/revert asks before dropping the resolved file from
|
|
17676
|
+
* the list. Defaults to on; only an explicit `'0'` disables it, which then
|
|
17677
|
+
* removes the file straight away.
|
|
17678
|
+
* @returns whether the remove prompt is enabled.
|
|
16832
17679
|
*/
|
|
16833
|
-
function
|
|
16834
|
-
return localStorage.getItem(
|
|
17680
|
+
function confirmFileRemoveEnabled() {
|
|
17681
|
+
return localStorage.getItem(CONFIRM_FILE_REMOVE_KEY) !== "0";
|
|
16835
17682
|
}
|
|
16836
|
-
/** Persist the
|
|
16837
|
-
function
|
|
16838
|
-
localStorage.setItem(
|
|
17683
|
+
/** Persist the whole-file remove-prompt preference. */
|
|
17684
|
+
function setConfirmFileRemoveEnabled(value) {
|
|
17685
|
+
localStorage.setItem(CONFIRM_FILE_REMOVE_KEY, value ? "1" : "0");
|
|
16839
17686
|
}
|
|
16840
17687
|
/**
|
|
16841
|
-
* Whether the
|
|
16842
|
-
*
|
|
16843
|
-
*
|
|
16844
|
-
* @returns whether the Markdown preview default is enabled.
|
|
17688
|
+
* Whether the in-file search matches letter case exactly. Defaults to off, so a
|
|
17689
|
+
* plain query keeps matching either case; only an explicit `'1'` turns it on.
|
|
17690
|
+
* @returns whether the search is case-sensitive.
|
|
16845
17691
|
*/
|
|
16846
|
-
function
|
|
16847
|
-
return localStorage.getItem(
|
|
17692
|
+
function searchCaseSensitive() {
|
|
17693
|
+
return localStorage.getItem(SEARCH_CASE_KEY) === "1";
|
|
16848
17694
|
}
|
|
16849
|
-
/** Persist the
|
|
16850
|
-
function
|
|
16851
|
-
localStorage.setItem(
|
|
17695
|
+
/** Persist the search case-sensitivity preference. */
|
|
17696
|
+
function setSearchCaseSensitive(value) {
|
|
17697
|
+
localStorage.setItem(SEARCH_CASE_KEY, value ? "1" : "0");
|
|
16852
17698
|
}
|
|
16853
17699
|
/**
|
|
16854
|
-
*
|
|
16855
|
-
*
|
|
16856
|
-
*
|
|
16857
|
-
* @returns the max width in pixels.
|
|
17700
|
+
* Whether the in-file search only matches whole words. Defaults to off; only an
|
|
17701
|
+
* explicit `'1'` enables it.
|
|
17702
|
+
* @returns whether the search matches whole words only.
|
|
16858
17703
|
*/
|
|
16859
|
-
function
|
|
16860
|
-
|
|
16861
|
-
if (!Number.isInteger(raw)) return 800;
|
|
16862
|
-
return Math.max(480, Math.min(MD_MAX_WIDTH_MAX, raw));
|
|
17704
|
+
function searchWholeWord() {
|
|
17705
|
+
return localStorage.getItem(SEARCH_WORD_KEY) === "1";
|
|
16863
17706
|
}
|
|
16864
|
-
/** Persist the
|
|
16865
|
-
function
|
|
16866
|
-
localStorage.setItem(
|
|
17707
|
+
/** Persist the search whole-word preference. */
|
|
17708
|
+
function setSearchWholeWord(value) {
|
|
17709
|
+
localStorage.setItem(SEARCH_WORD_KEY, value ? "1" : "0");
|
|
16867
17710
|
}
|
|
16868
17711
|
/**
|
|
16869
|
-
*
|
|
16870
|
-
*
|
|
16871
|
-
*
|
|
16872
|
-
* @
|
|
16873
|
-
|
|
16874
|
-
|
|
16875
|
-
|
|
16876
|
-
|
|
16877
|
-
return Math.max(0, Math.min(10, raw));
|
|
17712
|
+
* Whether lines wrap (auto-wrap) in the diff for one highlight language.
|
|
17713
|
+
* Defaults to off; only an explicit `'1'` enables it. Stored per language, so
|
|
17714
|
+
* a language's preference never leaks into another's.
|
|
17715
|
+
* @param lang - the highlight language (or `''` for the auto/default bucket).
|
|
17716
|
+
* @returns whether lines wrap.
|
|
17717
|
+
*/
|
|
17718
|
+
function wrapEnabled(lang) {
|
|
17719
|
+
return localStorage.getItem(`${WRAP_PREFIX}${lang}`) === "1";
|
|
16878
17720
|
}
|
|
16879
|
-
/** Persist the
|
|
16880
|
-
function
|
|
16881
|
-
localStorage.setItem(
|
|
17721
|
+
/** Persist the per-language auto-wrap preference. */
|
|
17722
|
+
function setWrapEnabled(lang, value) {
|
|
17723
|
+
localStorage.setItem(`${WRAP_PREFIX}${lang}`, value ? "1" : "0");
|
|
16882
17724
|
}
|
|
16883
|
-
const QUICK_SUMMON_KEY = "diff-approval:quick-summon-key";
|
|
16884
17725
|
/**
|
|
16885
|
-
* The
|
|
16886
|
-
*
|
|
16887
|
-
*
|
|
17726
|
+
* The diff's tab width in spaces. Defaults to 4; the settings UI offers 2/4/8,
|
|
17727
|
+
* but any positive integer is accepted. This drives both the rendered
|
|
17728
|
+
* `tab-size` and the wrapped-line tab measurement, so they always agree.
|
|
17729
|
+
* @returns the number of spaces one tab advances.
|
|
16888
17730
|
*/
|
|
16889
|
-
function
|
|
16890
|
-
|
|
17731
|
+
function tabWidth() {
|
|
17732
|
+
const value = Number.parseInt(localStorage.getItem(TAB_WIDTH_KEY) ?? "", 10);
|
|
17733
|
+
return Number.isInteger(value) && value > 0 ? value : 4;
|
|
16891
17734
|
}
|
|
16892
|
-
/** Persist the
|
|
16893
|
-
function
|
|
16894
|
-
localStorage.setItem(
|
|
17735
|
+
/** Persist the diff's tab width (in spaces). */
|
|
17736
|
+
function setTabWidth(value) {
|
|
17737
|
+
localStorage.setItem(TAB_WIDTH_KEY, String(value));
|
|
16895
17738
|
}
|
|
16896
|
-
|
|
16897
|
-
|
|
16898
|
-
*
|
|
16899
|
-
|
|
16900
|
-
|
|
16901
|
-
|
|
16902
|
-
|
|
16903
|
-
|
|
16904
|
-
|
|
16905
|
-
searchPrev: "Shift+F3",
|
|
16906
|
-
undo: "Ctrl+Z",
|
|
16907
|
-
redo: "Ctrl+Shift+Z",
|
|
16908
|
-
cycleNext: "Ctrl+Tab",
|
|
16909
|
-
cyclePrev: "Ctrl+Shift+Tab"
|
|
16910
|
-
};
|
|
16911
|
-
/** The currently configured chord for one action; falls back to its default. */
|
|
16912
|
-
function keybindingOf(action) {
|
|
16913
|
-
return localStorage.getItem(`${KEY_PREFIX}${action}`) ?? DEFAULT_KEYBINDINGS[action] ?? "";
|
|
17739
|
+
/**
|
|
17740
|
+
* The diff code font size as a percentage of the current (theme) size. Defaults
|
|
17741
|
+
* to 100 (the current look); the settings UI steps it by ±10.
|
|
17742
|
+
* @returns the font-size scale, as a percentage (e.g. 100, 110, 90).
|
|
17743
|
+
*/
|
|
17744
|
+
function diffFontScale() {
|
|
17745
|
+
const raw = Number.parseInt(localStorage.getItem(DIFF_FONT_SCALE_KEY) ?? "", 10);
|
|
17746
|
+
if (!Number.isFinite(raw)) return 100;
|
|
17747
|
+
return Math.max(50, Math.min(200, raw));
|
|
16914
17748
|
}
|
|
16915
|
-
/** Persist
|
|
16916
|
-
function
|
|
16917
|
-
localStorage.setItem(
|
|
17749
|
+
/** Persist the diff's code font-size scale (a percentage). */
|
|
17750
|
+
function setDiffFontScale(value) {
|
|
17751
|
+
localStorage.setItem(DIFF_FONT_SCALE_KEY, String(Math.max(50, Math.min(200, value))));
|
|
16918
17752
|
}
|
|
16919
17753
|
/**
|
|
16920
|
-
*
|
|
16921
|
-
*
|
|
16922
|
-
*
|
|
16923
|
-
* modifier set is required (extra modifiers do not match).
|
|
16924
|
-
* @param event - the keydown event.
|
|
16925
|
-
* @param shortcut - the chord string.
|
|
16926
|
-
* @returns whether the event matches.
|
|
17754
|
+
* The diff code line height in px (the fixed row height the virtual window and
|
|
17755
|
+
* jump math are keyed to). Defaults to 22 (the pre-customization value).
|
|
17756
|
+
* @returns the line height in px.
|
|
16927
17757
|
*/
|
|
16928
|
-
function
|
|
16929
|
-
const
|
|
16930
|
-
|
|
16931
|
-
|
|
16932
|
-
const mods = new Set(parts);
|
|
16933
|
-
const ctrl = mods.has("ctrl") || mods.has("control");
|
|
16934
|
-
const alt = mods.has("alt") || mods.has("option");
|
|
16935
|
-
const shift = mods.has("shift");
|
|
16936
|
-
const meta = mods.has("meta") || mods.has("cmd") || mods.has("command") || mods.has("win");
|
|
16937
|
-
return event.key.toLowerCase() === key && event.ctrlKey === ctrl && event.altKey === alt && event.shiftKey === shift && event.metaKey === meta;
|
|
17758
|
+
function diffLineHeight() {
|
|
17759
|
+
const raw = Number.parseInt(localStorage.getItem(DIFF_LINE_HEIGHT_KEY) ?? "", 10);
|
|
17760
|
+
if (!Number.isFinite(raw)) return 22;
|
|
17761
|
+
return Math.max(10, Math.min(36, raw));
|
|
16938
17762
|
}
|
|
16939
|
-
|
|
16940
|
-
|
|
16941
|
-
const css = ".F1KBNa_layer{box-sizing:border-box;flex:none;align-items:center;width:calc(100% + 4px);height:42px;margin:4px -2px;display:flex;position:relative}.F1KBNa_footerButtons{align-items:center;width:100%;display:flex}.F1KBNa_badge{box-sizing:border-box;width:100%;height:42px;color:var(--dsw-alias-label-primary);cursor:pointer;background:0 0;border:none;border-radius:12px;align-items:center;gap:8px;padding:0 10px 0 8px;font-family:inherit;font-size:14px;line-height:22px;display:flex;overflow:hidden}.F1KBNa_badge:hover{background:var(--dsw-alias-interactive-bg-hover)}.F1KBNa_badge[data-active]{background:var(--dsw-alias-interactive-bg-hover-solid)}.F1KBNa_badge:disabled{color:var(--dsw-alias-label-tertiary);cursor:default;background:0 0}.F1KBNa_badgeLabel{text-overflow:ellipsis;white-space:nowrap;min-width:0;overflow:hidden}.F1KBNa_badgeCount{color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;flex:none;margin-left:auto;font-size:12px;line-height:16px}.F1KBNa_layer.F1KBNa_rail{width:36px;height:36px;margin:8px 0 10px}.F1KBNa_rail .F1KBNa_badge{border-radius:50%;justify-content:center;gap:0;width:36px;height:36px;padding:0}.F1KBNa_rail .F1KBNa_badgeLabel{display:none}.F1KBNa_rail .F1KBNa_badgeCount{box-sizing:border-box;background:var(--dsw-alias-state-business-primary);min-width:18px;height:18px;color:var(--dsw-alias-label-primary-foreground);font-variant-numeric:tabular-nums;border-radius:9px;justify-content:center;align-items:center;padding:0 4px;font-size:11px;line-height:18px;display:flex;position:absolute;top:-3px;right:-7px}.F1KBNa_fullscreenBackdrop{z-index:29;background:var(--dsw-specific-sidebar-fill);position:fixed;inset:0}.F1KBNa_panel{z-index:30;border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-base);width:auto;max-width:none;box-shadow:var(--dsw-shadow-lv2);--dsh-scrollbar-thumb:var(--dsw-alias-scrollbar-bg-l2);--dsh-scrollbar-thumb-hover:var(--dsw-alias-scrollbar-hover-l2);border-radius:12px;flex-direction:column;display:flex;position:fixed;inset:8px 8px 128px;overflow:hidden}.F1KBNa_header{box-sizing:border-box;border-bottom:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-base);flex:none;justify-content:space-between;align-items:center;min-height:44px;padding:10px 12px;display:flex}.F1KBNa_headerActions{align-items:center;gap:2px;display:flex}.F1KBNa_settingsPage{padding:8px 12px}.F1KBNa_settingsGroup{border-bottom:1px solid var(--dsw-alias-border-l2);margin-top:8px}.F1KBNa_settingsGroupHeader{width:100%;color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;text-align:left;background:0 0;border:none;align-items:center;gap:8px;padding:12px 0;display:flex}.F1KBNa_settingsGroupText{text-align:left;flex-direction:column;flex:1;gap:2px;min-width:0;display:flex}.F1KBNa_settingsGroupTitle{font-size:14px;font-weight:400;line-height:22px}.F1KBNa_settingsGroupDesc{color:var(--dsw-alias-label-tertiary);font-size:12px;font-weight:400;line-height:18px}.F1KBNa_settingsGroupChevron{margin-left:auto;transition:transform .15s}.F1KBNa_settingsGroup[data-open] .F1KBNa_settingsGroupChevron{transform:rotate(180deg)}.F1KBNa_settingsGroup[data-open] .F1KBNa_settingsGroupHeader{border-bottom:1px solid var(--dsw-alias-border-l2)}.F1KBNa_settingsGroupBody{flex-direction:column;display:flex}.F1KBNa_settingsGroupBody .F1KBNa_settingsRow{padding-left:16px}.F1KBNa_settingsGroupBody .F1KBNa_settingsRow:last-child,.F1KBNa_settingsPage>.F1KBNa_settingsRow:last-child{border-bottom:none}.F1KBNa_settingsRow{border-bottom:1px solid var(--dsw-alias-border-l2);align-items:center;gap:8px;padding:16px 0;display:flex}.F1KBNa_settingsRowText{flex-direction:column;flex:1;gap:4px;min-width:0;padding-right:48px;display:flex}.F1KBNa_settingsRowTitle{color:var(--dsw-alias-label-primary);font-size:14px;font-weight:400;line-height:22px}.F1KBNa_settingsRowDesc{color:var(--dsw-alias-label-tertiary);font-size:12px;font-weight:400;line-height:18px}.F1KBNa_settingsSelector{background:var(--dsw-alias-bg-module-platform);height:36px;font:inherit;color:var(--dsw-alias-label-primary);cursor:pointer;border:none;border-radius:18px;align-items:center;gap:12px;padding:0 14px;font-size:14px;line-height:22px;display:inline-flex}.F1KBNa_settingsSelector:hover{background:var(--dsw-alias-interactive-bg-hover)}.F1KBNa_settingsSelectorChevron{flex:none}.F1KBNa_colorPicker{position:relative}.F1KBNa_colorPickerTrigger{background:var(--dsw-alias-bg-module-platform);width:120px;height:36px;font:inherit;color:var(--dsw-alias-label-primary);cursor:pointer;border:none;border-radius:18px;align-items:center;gap:8px;padding:0 8px;font-size:12px;line-height:18px;display:inline-flex}.F1KBNa_colorPickerTrigger:hover{background:var(--dsw-alias-interactive-bg-hover)}.F1KBNa_colorSwatch{border:1px solid var(--dsw-alias-border-l2);border-radius:50%;flex:none;width:24px;height:24px}.F1KBNa_colorValue{text-overflow:ellipsis;white-space:nowrap;font-variant-numeric:tabular-nums;flex:1;min-width:0;overflow:hidden}.F1KBNa_colorPickerChevron{flex:none}.F1KBNa_colorBackdrop{z-index:40;position:fixed;inset:0}.F1KBNa_colorPickerPopover{z-index:41;border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-base);box-shadow:var(--dsw-shadow-lv2);border-radius:10px;padding:10px;position:absolute;top:calc(100% + 6px);right:0}.F1KBNa_colorPickerPanel{flex-direction:column;gap:8px;display:flex}.F1KBNa_colorPickerInputRow{align-items:center;gap:8px;display:flex}.F1KBNa_colorPickerPreview{border:1px solid var(--dsw-alias-border-l2);border-radius:6px;flex:none;width:22px;height:22px}.F1KBNa_colorPickerInput{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-module-platform);min-width:0;height:30px;font:inherit;color:var(--dsw-alias-label-primary);border-radius:8px;flex:1;padding:0 8px;font-size:12px;line-height:28px}.F1KBNa_colorPickerInput:focus{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:-1px}.F1KBNa_colorSv{border:1px solid var(--dsw-alias-border-l1);cursor:crosshair;touch-action:none;border-radius:8px;height:140px;position:relative}.F1KBNa_colorSvCursor{pointer-events:none;border:2px solid #fff;border-radius:50%;width:14px;height:14px;position:absolute;transform:translate(-50%,-50%);box-shadow:0 0 0 1px #00000080}.F1KBNa_colorHue{border:1px solid var(--dsw-alias-border-l1);cursor:pointer;touch-action:none;border-radius:7px;height:14px;position:relative}.F1KBNa_colorHueCursor{pointer-events:none;border:2px solid #fff;border-radius:50%;width:16px;height:16px;position:absolute;top:50%;transform:translate(-50%,-50%);box-shadow:0 0 0 1px #00000080}.F1KBNa_diffPreview{z-index:2;border:1px solid var(--dsw-alias-border-l1);font:var(--dsw-font-markdown-code-block);background:var(--dsw-alias-markdown-code-block);border-radius:8px;margin-bottom:8px;padding:8px;position:sticky;top:0;overflow:hidden}.F1KBNa_diffPreviewTitle{color:var(--dsw-alias-label-tertiary);margin-bottom:6px;font-family:inherit;font-size:12px;line-height:18px}.F1KBNa_diffPreviewScroll{height:132px;overflow-y:auto}.F1KBNa_mdPreviewWrap{flex:1;min-width:0;min-height:0;display:flex;position:relative}.F1KBNa_mdPreviewBody{min-width:0;min-height:0;color:var(--dsw-alias-label-primary);flex:1;padding:12px 16px;position:relative;overflow:auto}.F1KBNa_mdPreviewContent{width:100%;min-width:0;margin:0 auto}.F1KBNa_mdPreviewSingle{display:block}.F1KBNa_mdPreviewDouble{display:flex}.F1KBNa_mdPreviewBody .mdBlock{border-radius:4px;margin-bottom:12px;padding:2px 6px}.F1KBNa_mdPreviewBody .mdAdd{background-color:color-mix(in srgb, var(--dsh-diff-add-color,var(--dsw-alias-state-success-primary)) 10%, transparent)}.F1KBNa_mdPreviewBody .mdDel{background-color:color-mix(in srgb, var(--dsh-diff-del-color,var(--dsw-alias-state-error-primary)) 12%, transparent)}.F1KBNa_mdPreviewBody .mdWordDel{background-color:color-mix(in srgb, var(--dsh-diff-del-color,var(--dsw-alias-state-error-primary)) 34%, transparent);border-radius:2px;padding:0 1px}.F1KBNa_mdPreviewBody .mdWordAdd{background-color:color-mix(in srgb, var(--dsh-diff-add-color,var(--dsw-alias-state-success-primary)) 34%, transparent);border-radius:2px;padding:0 1px}.F1KBNa_mdPreviewBody .mdDouble{flex-direction:column;width:100%;min-width:0;display:flex}.F1KBNa_mdPreviewBody .mdDoubleRow{align-items:flex-start;width:100%;min-width:0;display:flex}.F1KBNa_mdPreviewBody .mdDoubleCol{flex:1;min-width:0}.F1KBNa_mdPreviewBody .mdDoubleRule{background:var(--dsw-alias-border-l2);flex:none;align-self:stretch;width:1px;margin:0 12px}.F1KBNa_mdPreviewFallback{font-family:var(--dsw-font-markdown-code-block);color:var(--dsw-alias-label-tertiary);padding:12px 16px;font-size:12px;line-height:18px}.F1KBNa_mdPreviewBody{word-break:break-word;font-size:13px;line-height:20px}.F1KBNa_mdPreviewBody>:first-child{margin-top:0}.F1KBNa_mdPreviewBody>:last-child{margin-bottom:0}.F1KBNa_mdPreviewBody h1,.F1KBNa_mdPreviewBody h2,.F1KBNa_mdPreviewBody h3,.F1KBNa_mdPreviewBody h4,.F1KBNa_mdPreviewBody h5,.F1KBNa_mdPreviewBody h6{margin:16px 0 8px;font-weight:600;line-height:1.3}.F1KBNa_mdPreviewBody h1{font-size:22px}.F1KBNa_mdPreviewBody h2{font-size:19px}.F1KBNa_mdPreviewBody h3{font-size:16px}.F1KBNa_mdPreviewBody h4{font-size:15px}.F1KBNa_mdPreviewBody h5{font-size:14px}.F1KBNa_mdPreviewBody h6{font-size:13px}.F1KBNa_mdPreviewBody p{margin:8px 0}.F1KBNa_mdPreviewBody ul,.F1KBNa_mdPreviewBody ol{margin:8px 0;padding-left:22px}.F1KBNa_mdPreviewBody li{margin:2px 0}.F1KBNa_mdPreviewBody a{color:var(--dsw-alias-state-business-primary);text-decoration:none}.F1KBNa_mdPreviewBody a:hover{text-decoration:underline}.F1KBNa_mdPreviewBody code{font-family:var(--dsw-font-markdown-code-block);background:#9e9e9e40;border-radius:4px;padding:1px 5px;font-size:12.5px}.F1KBNa_mdPreviewBody pre{border:1px solid var(--dsw-alias-border-l1);font-family:var(--dsw-font-markdown-code-block);background:#9e9e9e40;border-radius:8px;margin:8px 0;padding:10px 12px;font-size:12.5px;line-height:18px;overflow:auto}.F1KBNa_mdPreviewBody pre code{background:0 0;padding:0}.F1KBNa_mdPreviewBody blockquote{border-left:3px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);margin:8px 0;padding:0 12px}.F1KBNa_mdPreviewBody hr{background:var(--dsw-alias-border-l1);border:none;height:1px;margin:16px 0}.F1KBNa_mdPreviewBody table{border-collapse:collapse;margin:8px 0}.F1KBNa_mdPreviewBody th,.F1KBNa_mdPreviewBody td{border:1px solid var(--dsw-alias-border-l1);padding:4px 10px}.F1KBNa_mdPreviewBody img{max-width:100%}.F1KBNa_mdPreviewBody .mdImageFallback{border:1px dashed var(--dsw-alias-border-l2);background:color-mix(in srgb, var(--dsw-alias-markdown-code-block) 45%, transparent);width:100%;min-height:120px;color:var(--dsw-alias-label-secondary);font-family:var(--dsw-font-markdown-code-block);text-align:center;white-space:pre-wrap;word-break:break-all;border-radius:4px;justify-content:center;align-items:center;margin:0 auto;padding:8px 12px;font-size:12px;line-height:16px;display:flex}.F1KBNa_toggle{border:1px solid var(--dsw-alias-border-l2);cursor:pointer;background:0 0;border-radius:18px;flex:none;width:60px;height:36px;padding:0;position:relative}.F1KBNa_toggle:hover{background:var(--dsw-alias-interactive-bg-hover)}.F1KBNa_toggle:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:2px}.F1KBNa_toggleOn{border-color:var(--dsw-alias-state-business-primary);background:var(--dsw-alias-state-business-primary)}.F1KBNa_toggleOn:hover{background:var(--dsw-alias-state-business-primary)}.F1KBNa_toggleThumb{background:var(--dsw-alias-label-primary);border-radius:50%;width:28px;height:28px;transition:left .12s;position:absolute;top:3px;left:3px}.F1KBNa_toggleOn .F1KBNa_toggleThumb{background:#fff;left:27px}.F1KBNa_stepper{align-items:center;gap:6px;display:inline-flex}.F1KBNa_stepperButton{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-module-platform);width:32px;height:32px;color:var(--dsw-alias-label-primary);cursor:pointer;border-radius:8px;padding:0;position:relative}.F1KBNa_stepperButton:before,.F1KBNa_stepperButton:after{content:\"\";background:currentColor;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)}.F1KBNa_stepperButton:before{width:10px;height:1px}.F1KBNa_stepperButton:after{width:1px;height:10px;display:none}.F1KBNa_stepperButtonUp:after{display:block}.F1KBNa_stepperButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.F1KBNa_stepperButton:disabled{color:var(--dsw-alias-label-tertiary);cursor:default}.F1KBNa_stepperValue{text-align:center;min-width:2ch;color:var(--dsw-alias-label-primary);font-variant-numeric:tabular-nums;font-size:14px;line-height:22px}.F1KBNa_states{flex-direction:column;flex:1;min-height:0;padding:4px 12px 12px;display:flex;overflow-y:auto}.F1KBNa_split{flex:1;align-items:stretch;min-height:0;display:flex;position:relative}.F1KBNa_fileListFloat{z-index:40;box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-base);box-shadow:var(--dsw-shadow-lv2);border-radius:12px;flex-direction:column;padding:6px 8px 10px;display:flex;position:absolute;overflow:hidden}.F1KBNa_fileList{box-sizing:border-box;border-right:1px solid var(--dsw-alias-border-l2);flex-direction:column;flex:none;width:240px;min-height:0;padding:4px 8px 12px;display:flex}.F1KBNa_listScroll{flex:1;min-height:0;overflow-y:auto}.F1KBNa_bulkActions{flex:none;gap:6px;padding-top:8px;display:flex}.F1KBNa_bulkActions .F1KBNa_action{text-align:center;flex:1}.F1KBNa_resizeHandle{cursor:col-resize;background:0 0;flex:none;width:5px;margin:0 -2px}.F1KBNa_resizeHandle:hover{background:var(--dsw-alias-border-l2)}.F1KBNa_detail{flex-direction:column;flex:1;min-width:0;min-height:0;display:flex}.F1KBNa_detailEmpty{color:var(--dsw-alias-label-tertiary);text-align:center;flex:1;justify-content:center;align-items:center;margin:0;padding:24px;font-size:12px;line-height:18px;display:flex}.F1KBNa_confirmBackdrop{z-index:5;background:color-mix(in srgb, var(--dsw-alias-bg-base) 55%, transparent);justify-content:center;align-items:center;padding:24px;display:flex;position:absolute;inset:0}.F1KBNa_confirmCard{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-base);max-width:340px;box-shadow:var(--dsw-shadow-lv3);border-radius:12px;flex-direction:column;gap:12px;padding:16px;display:flex}.F1KBNa_confirmText{font:var(--dsw-font-caption);color:var(--dsw-alias-label-primary);overflow-wrap:anywhere;margin:0;line-height:1.5}.F1KBNa_confirmActions{justify-content:flex-end;gap:8px;display:flex}.F1KBNa_title{color:var(--dsw-alias-label-primary);font-size:13px;font-weight:500;line-height:20px}.F1KBNa_note,.F1KBNa_readError,.F1KBNa_hint{color:var(--dsw-alias-label-tertiary);margin:4px 0;font-size:12px;line-height:18px}.F1KBNa_noteCentered{text-align:center;margin:auto}.F1KBNa_emptyState{flex-direction:column;align-items:center;gap:10px;margin:auto;display:flex}.F1KBNa_importButton{border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-primary);cursor:pointer;background:0 0;border-radius:8px;padding:5px 14px;font-family:inherit;font-size:12px;line-height:18px}.F1KBNa_importButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.F1KBNa_importButton:disabled{color:var(--dsw-alias-label-tertiary);cursor:default}.F1KBNa_importNote{color:var(--dsw-alias-label-tertiary);text-align:center;margin:0;font-size:12px;line-height:18px}.F1KBNa_readError{color:var(--dsw-alias-state-error-primary)}.F1KBNa_group{color:var(--dsw-alias-label-tertiary);margin:8px 0 4px;font-size:12px;font-weight:500;line-height:16px}.F1KBNa_rows{margin:0;padding:0;list-style:none}.F1KBNa_row{border:1px solid #0000;border-radius:10px;margin:2px 0}.F1KBNa_rowHead{width:100%;color:var(--dsw-alias-label-primary);font:inherit;text-align:left;cursor:pointer;background:0 0;border:none;border-radius:10px;align-items:baseline;gap:8px;padding:6px 8px;display:flex}.F1KBNa_rowHead:hover{background:var(--dsw-alias-interactive-bg-hover-solid)}.F1KBNa_rowHead[data-selected]{background:var(--dsw-alias-interactive-bg-hover)}.F1KBNa_rowPath{text-overflow:ellipsis;white-space:nowrap;min-width:0;font:var(--dsw-font-markdown-code-block);overflow:hidden}.F1KBNa_kindTag{border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-tertiary);white-space:nowrap;border-radius:6px;flex:none;padding:1px 6px;font-size:11px;line-height:14px}.F1KBNa_kindHint{color:var(--dsw-alias-label-tertiary);flex:none;font-size:12px;line-height:16px}.F1KBNa_divergedHint,.F1KBNa_missingHint{border-bottom:1px solid var(--dsw-alias-border-l2);margin:0;padding:6px 8px;font-size:12px;line-height:18px}.F1KBNa_missing{border:1px solid var(--dsw-alias-state-warn-primary);color:var(--dsw-alias-state-warn-label,var(--dsw-alias-state-warn-primary));white-space:nowrap;border-radius:6px;flex:none;padding:1px 6px;font-size:11px;line-height:14px}.F1KBNa_rowFailed{border:1px solid var(--dsw-alias-state-error-primary);color:var(--dsw-alias-state-error-primary);white-space:nowrap;border-radius:6px;flex:none;padding:1px 6px;font-size:11px;line-height:14px}.F1KBNa_actionError{border-bottom:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-state-error-primary);overflow-wrap:anywhere;margin:0;padding:6px 8px;font-size:12px;line-height:18px}.F1KBNa_missingHint{color:var(--dsw-alias-state-warn-label,var(--dsw-alias-state-warn-primary))}.F1KBNa_rowMeta{color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;flex:none;gap:6px;margin-left:auto;font-size:12px;line-height:16px;display:inline-flex}.F1KBNa_addCount{color:color-mix(in srgb, var(--dsh-diff-add-color,var(--dsw-alias-state-success-primary)) 60%, var(--dsw-alias-label-primary))}.F1KBNa_delCount{color:color-mix(in srgb, var(--dsh-diff-del-color,var(--dsw-alias-state-error-primary)) 60%, var(--dsw-alias-label-primary))}.F1KBNa_diff{background:var(--dsw-alias-markdown-code-block);flex-direction:column;flex:1;min-height:0;display:flex;position:relative;overflow:hidden}.F1KBNa_diffHeader{border-bottom:1px solid var(--dsw-alias-border-l2);align-items:center;gap:8px;padding:6px 8px;display:flex}.F1KBNa_diffPath{white-space:nowrap;min-width:0;color:var(--dsw-alias-label-primary);font:var(--dsw-font-markdown-code-block);-webkit-user-select:text;user-select:text;flex:1;font-size:12px;line-height:18px;overflow:auto hidden}.F1KBNa_diffActions{border-bottom:1px solid var(--dsw-alias-border-l2);align-items:center;gap:8px;padding:6px 8px;display:flex}.F1KBNa_diffStats{color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;font-size:12px;line-height:16px}.F1KBNa_flexSpacer{flex:1}.F1KBNa_divider{background:var(--dsw-alias-border-l1);align-self:stretch;width:1px;margin:2px 0}.F1KBNa_action{border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-primary);cursor:pointer;background:0 0;border-radius:8px;padding:3px 12px;font-family:inherit;font-size:12px;line-height:18px}.F1KBNa_action:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.F1KBNa_action:disabled{color:var(--dsw-alias-label-tertiary);cursor:default}.F1KBNa_actionPrimary{background:var(--dsw-alias-state-business-primary);border-color:var(--dsw-alias-state-business-primary);color:var(--dsw-static-neutral-bluish-50)}.F1KBNa_actionPrimary:hover:not(:disabled){background:color-mix(in srgb, var(--dsw-alias-state-business-primary) 85%, var(--dsw-static-neutral-bluish-1000))}.F1KBNa_actionPrimary:disabled{background:var(--dsw-alias-state-business-primary);color:var(--dsw-static-neutral-bluish-50);opacity:.55}.F1KBNa_actionQuietDisabled:disabled{cursor:pointer;color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-border-l1);background:0 0}.F1KBNa_actionPrimary.F1KBNa_actionQuietDisabled:disabled{background:var(--dsw-alias-state-business-primary);border-color:var(--dsw-alias-state-business-primary);color:var(--dsw-static-neutral-bluish-50);opacity:1}.F1KBNa_iconAction{justify-content:center;align-items:center;min-height:25px;padding:4px 6px;display:inline-flex}.F1KBNa_close{cursor:pointer;width:28px;height:28px;color:var(--dsw-alias-label-tertiary);background:0 0;border:none;border-radius:28px;justify-content:center;align-items:center;padding:0;display:inline-flex}.F1KBNa_close:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}.F1KBNa_expand{cursor:pointer;width:28px;height:28px;color:var(--dsw-alias-label-tertiary);background:0 0;border:none;border-radius:28px;justify-content:center;align-items:center;padding:0;display:inline-flex}.F1KBNa_expand:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}.F1KBNa_expandExpanded svg{transform:rotate(180deg)}.F1KBNa_diffBodyWrap{flex:1;min-height:0;display:flex;position:relative}.F1KBNa_diffBody{min-width:0;font:var(--dsw-font-markdown-code-block);-webkit-text-size-adjust:100%;text-size-adjust:100%;cursor:text;outline:none;flex:1;padding:0;position:relative;overflow:auto}.F1KBNa_diffBody::-webkit-scrollbar,.F1KBNa_diffBody::-webkit-scrollbar-track,.F1KBNa_diffBody::-webkit-scrollbar-thumb{cursor:default}.F1KBNa_blockActions{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-markdown-code-block);box-shadow:var(--dsw-shadow-lv1);z-index:2;border-radius:10px;align-items:center;gap:9px;padding:5px;display:flex;position:absolute;right:8px}.F1KBNa_blockPosition{color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;white-space:nowrap;margin-top:1px;margin-left:7px;font-size:12px;line-height:16px}.F1KBNa_searchBar{z-index:3;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-markdown-code-block);box-shadow:var(--dsw-shadow-lv1);border-radius:10px;align-items:center;gap:4px;padding:4px;display:flex;position:absolute;top:8px;right:8px}.F1KBNa_searchInput{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-base);width:150px;color:var(--dsw-alias-label-primary);border-radius:6px;outline:none;padding:3px 8px;font-family:inherit;font-size:12px;line-height:18px}.F1KBNa_searchInput:focus{border-color:var(--dsw-alias-state-business-primary)}.F1KBNa_searchCount{text-align:center;min-width:34px;color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;white-space:nowrap;font-size:12px;line-height:16px}.F1KBNa_searchMatch{background-color:#facc1542;border-radius:2px}.F1KBNa_searchMatchCurrent{background-color:#facc1599;border-radius:2px}.F1KBNa_blockFlash{z-index:1;box-sizing:border-box;border:2px solid var(--dsw-alias-state-business-primary);pointer-events:none;border-radius:4px;animation:1s ease-out forwards F1KBNa_diffFlashFade;position:absolute;left:0;right:0}.F1KBNa_blockFlashShake{animation:.7s ease-out forwards F1KBNa_diffFlashShake}@keyframes F1KBNa_diffFlashFade{0%{opacity:1}to{opacity:0}}@keyframes F1KBNa_diffFlashShake{0%{opacity:1;transform:translateY(0)}10%{transform:translateY(1px)}20%{transform:translateY(-1px)}30%{transform:translateY(1px)}40%{transform:translateY(-1px)}50%{transform:translateY(1px)}60%{transform:translateY(-1px)}70%{opacity:1;transform:translateY(0)}to{opacity:0;transform:translateY(0)}}.F1KBNa_overviewRuler{pointer-events:none;opacity:.5;width:4px;position:absolute;top:0;bottom:0;right:0}.F1KBNa_overviewMarker{border-radius:2px;width:100%;min-height:2px;position:absolute;right:0}.F1KBNa_markerDel{background-color:var(--dsw-alias-state-error-primary)}.F1KBNa_markerAdd{background-color:var(--dsw-alias-state-success-primary)}.F1KBNa_lines{border-spacing:0;width:max-content;min-width:100%;font-size:calc(1em * var(--dsh-diff-font-scale,1));display:table}.F1KBNa_line{height:var(--dsh-diff-line-height,22px);line-height:var(--dsh-diff-line-height,22px);-webkit-user-select:none;user-select:none;display:table-row}.F1KBNa_vSpacer{display:table-row}.F1KBNa_gutter{box-sizing:border-box;text-align:right;width:44px;color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;-webkit-user-select:none;user-select:none;cursor:default;padding-right:10px;display:table-cell}.F1KBNa_code{white-space:pre;-webkit-user-select:text;user-select:text;display:table-cell}.F1KBNa_context{color:var(--dsw-alias-label-primary)}.F1KBNa_del{background-color:color-mix(in srgb, var(--dsh-diff-del-color,var(--dsw-alias-state-error-primary)) 12%, transparent)}.F1KBNa_add{background-color:color-mix(in srgb, var(--dsh-diff-add-color,var(--dsw-alias-state-success-primary)) 10%, transparent)}.F1KBNa_statusBar{box-sizing:border-box;border-top:1px solid var(--dsw-alias-border-l2);height:34px;color:var(--dsw-alias-label-tertiary);font-family:var(--dsw-font-markdown-code-block);font-variant-numeric:tabular-nums;flex:none;align-items:center;gap:8px;padding:0 8px;font-size:12px;line-height:18px;display:flex}.F1KBNa_statusAction{border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-primary);white-space:nowrap;cursor:pointer;background:0 0;border-radius:8px;padding:3px 12px;font-family:inherit;font-size:12px;line-height:18px}.F1KBNa_statusAction:hover{background:var(--dsw-alias-interactive-bg-hover)}.F1KBNa_langSelect{border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-primary);cursor:pointer;background:0 0;border-radius:8px;flex:none;align-items:center;gap:4px;padding:3px 12px;font-family:inherit;font-size:12px;line-height:18px;display:inline-flex}.F1KBNa_langSelect:hover{background:var(--dsw-alias-interactive-bg-hover)}.F1KBNa_langLabel{text-overflow:ellipsis;white-space:nowrap;max-width:140px;overflow:hidden}.F1KBNa_notice{z-index:60;border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-base);max-width:360px;box-shadow:var(--dsw-shadow-lv3);border-radius:10px;align-items:flex-start;gap:12px;padding:12px 14px;display:flex;position:fixed;bottom:24px;right:24px}.F1KBNa_noticeText{font:var(--dsw-font-caption);color:var(--dsw-alias-label-primary);flex:1;margin:0;line-height:1.45}.F1KBNa_noticeButton{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary);font:var(--dsw-font-caption);cursor:pointer;border-radius:6px;flex:none;padding:4px 10px}.F1KBNa_noticeButton:hover{background:var(--dsw-alias-interactive-bg-hover-solid)}.F1KBNa_wrap .F1KBNa_line{height:auto;min-height:var(--dsh-diff-line-height,22px)}.F1KBNa_subline{height:var(--dsh-diff-line-height,22px);line-height:var(--dsh-diff-line-height,22px);white-space:pre;display:block;overflow:hidden}.F1KBNa_wrapActive{background:var(--dsw-alias-state-business-primary);border-color:var(--dsw-alias-state-business-primary);color:var(--dsw-static-neutral-bluish-50)}.F1KBNa_wrapActive:hover{background:color-mix(in srgb, var(--dsw-alias-state-business-primary) 85%, var(--dsw-static-neutral-bluish-1000))}.F1KBNa_splitRoot{flex-direction:column;flex:1;min-height:0;display:flex;position:relative}.F1KBNa_diffBodySplit{flex:1;min-height:0;overflow-x:hidden}.F1KBNa_splitCols{width:100%;min-width:0;display:flex}.F1KBNa_splitCol{box-sizing:border-box;flex:1 1 0;min-width:0;overflow:hidden}.F1KBNa_splitDivider{background:var(--dsw-alias-border-l2);flex:0 0 1px}.F1KBNa_splitCol .F1KBNa_gutter,.F1KBNa_splitCol .F1KBNa_code{vertical-align:top}.F1KBNa_splitHScrollRow{border-top:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-base);flex:none;width:100%;min-width:0;display:flex}.F1KBNa_splitHScroll{flex:1 1 0;min-width:0;height:15px;overflow:auto hidden}.F1KBNa_splitHScroll::-webkit-scrollbar{height:15px}.F1KBNa_splitHScroll::-webkit-scrollbar-track{cursor:default}.F1KBNa_splitHScroll::-webkit-scrollbar-thumb{cursor:default}.F1KBNa_splitHScrollFill{height:1px}.F1KBNa_splitLdel{background-color:color-mix(in srgb, var(--dsh-diff-del-color,var(--dsw-alias-state-error-primary)) 12%, transparent)}.F1KBNa_splitLadd{background-color:color-mix(in srgb, var(--dsh-diff-add-color,var(--dsw-alias-state-success-primary)) 10%, transparent)}.F1KBNa_splitRdel{background-color:color-mix(in srgb, var(--dsh-diff-del-color,var(--dsw-alias-state-error-primary)) 12%, transparent)}.F1KBNa_splitRadd{background-color:color-mix(in srgb, var(--dsh-diff-add-color,var(--dsw-alias-state-success-primary)) 10%, transparent)}.F1KBNa_intraDel{background-color:color-mix(in srgb, var(--dsh-diff-del-color,var(--dsw-alias-state-error-primary)) 26%, transparent);text-decoration:line-through}.F1KBNa_intraAdd{background-color:color-mix(in srgb, var(--dsh-diff-add-color,var(--dsw-alias-state-success-primary)) 24%, transparent)}";
|
|
16942
|
-
const tagId = "dsh-diff-approval/PendingPanel.module.css";
|
|
16943
|
-
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
|
|
16944
|
-
const tag = document.createElement("style");
|
|
16945
|
-
tag.dataset.plugin = "dsh-diff-approval";
|
|
16946
|
-
tag.dataset.pluginCss = tagId;
|
|
16947
|
-
tag.textContent = css;
|
|
16948
|
-
document.head.appendChild(tag);
|
|
17763
|
+
/** Persist the diff's code line height (px). */
|
|
17764
|
+
function setDiffLineHeight(value) {
|
|
17765
|
+
localStorage.setItem(DIFF_LINE_HEIGHT_KEY, String(Math.max(10, Math.min(36, value))));
|
|
16949
17766
|
}
|
|
16950
|
-
|
|
16951
|
-
|
|
16952
|
-
"
|
|
16953
|
-
|
|
16954
|
-
|
|
16955
|
-
|
|
16956
|
-
|
|
16957
|
-
|
|
16958
|
-
|
|
16959
|
-
|
|
16960
|
-
|
|
16961
|
-
|
|
16962
|
-
|
|
16963
|
-
|
|
16964
|
-
|
|
16965
|
-
|
|
16966
|
-
|
|
16967
|
-
|
|
16968
|
-
|
|
16969
|
-
|
|
16970
|
-
|
|
16971
|
-
|
|
16972
|
-
|
|
16973
|
-
|
|
16974
|
-
|
|
16975
|
-
|
|
16976
|
-
|
|
16977
|
-
|
|
16978
|
-
"
|
|
16979
|
-
|
|
16980
|
-
|
|
16981
|
-
|
|
16982
|
-
"
|
|
16983
|
-
|
|
16984
|
-
|
|
16985
|
-
|
|
16986
|
-
|
|
16987
|
-
|
|
16988
|
-
|
|
16989
|
-
|
|
16990
|
-
|
|
16991
|
-
|
|
16992
|
-
|
|
16993
|
-
|
|
16994
|
-
"
|
|
16995
|
-
|
|
16996
|
-
|
|
16997
|
-
|
|
16998
|
-
|
|
16999
|
-
|
|
17000
|
-
|
|
17001
|
-
|
|
17002
|
-
|
|
17003
|
-
|
|
17004
|
-
|
|
17005
|
-
|
|
17006
|
-
|
|
17007
|
-
|
|
17008
|
-
|
|
17009
|
-
|
|
17010
|
-
|
|
17011
|
-
|
|
17012
|
-
|
|
17013
|
-
|
|
17014
|
-
|
|
17015
|
-
|
|
17016
|
-
|
|
17017
|
-
|
|
17018
|
-
|
|
17019
|
-
|
|
17020
|
-
|
|
17021
|
-
|
|
17022
|
-
|
|
17023
|
-
|
|
17024
|
-
|
|
17025
|
-
|
|
17026
|
-
|
|
17027
|
-
|
|
17028
|
-
|
|
17029
|
-
|
|
17030
|
-
|
|
17031
|
-
|
|
17032
|
-
|
|
17033
|
-
|
|
17034
|
-
|
|
17035
|
-
|
|
17036
|
-
|
|
17037
|
-
|
|
17038
|
-
"
|
|
17039
|
-
|
|
17040
|
-
|
|
17041
|
-
|
|
17042
|
-
|
|
17043
|
-
|
|
17044
|
-
|
|
17045
|
-
|
|
17046
|
-
|
|
17047
|
-
|
|
17048
|
-
|
|
17049
|
-
|
|
17050
|
-
|
|
17051
|
-
"
|
|
17052
|
-
|
|
17053
|
-
|
|
17054
|
-
|
|
17055
|
-
|
|
17056
|
-
|
|
17057
|
-
|
|
17058
|
-
|
|
17059
|
-
|
|
17060
|
-
|
|
17061
|
-
|
|
17062
|
-
|
|
17063
|
-
|
|
17064
|
-
|
|
17065
|
-
|
|
17066
|
-
|
|
17067
|
-
|
|
17068
|
-
|
|
17069
|
-
|
|
17070
|
-
|
|
17071
|
-
|
|
17072
|
-
|
|
17073
|
-
|
|
17074
|
-
|
|
17075
|
-
|
|
17076
|
-
|
|
17077
|
-
|
|
17078
|
-
|
|
17079
|
-
|
|
17080
|
-
|
|
17081
|
-
|
|
17082
|
-
|
|
17083
|
-
|
|
17084
|
-
|
|
17085
|
-
|
|
17086
|
-
|
|
17087
|
-
|
|
17088
|
-
|
|
17089
|
-
|
|
17090
|
-
|
|
17091
|
-
|
|
17092
|
-
|
|
17093
|
-
|
|
17094
|
-
|
|
17095
|
-
|
|
17096
|
-
|
|
17097
|
-
|
|
17098
|
-
|
|
17099
|
-
|
|
17100
|
-
|
|
17101
|
-
|
|
17102
|
-
|
|
17103
|
-
|
|
17104
|
-
|
|
17767
|
+
/** Read a theme CSS custom property, with a fallback when it is unavailable. */
|
|
17768
|
+
function themeColor(name, fallback) {
|
|
17769
|
+
if (typeof document !== "undefined") {
|
|
17770
|
+
const v = getComputedStyle(document.documentElement).getPropertyValue(name).trim();
|
|
17771
|
+
if (/^#[0-9a-f]{6}$/i.test(v)) return v;
|
|
17772
|
+
}
|
|
17773
|
+
return fallback;
|
|
17774
|
+
}
|
|
17775
|
+
/** The added-line base color the user chose, or `undefined` (theme color). */
|
|
17776
|
+
function diffAddColor() {
|
|
17777
|
+
const v = localStorage.getItem(DIFF_ADD_COLOR_KEY);
|
|
17778
|
+
return v !== null && v.length > 0 ? v : void 0;
|
|
17779
|
+
}
|
|
17780
|
+
/** Persist the added-line base color (a hex like `#22c55e`). */
|
|
17781
|
+
function setDiffAddColor(value) {
|
|
17782
|
+
localStorage.setItem(DIFF_ADD_COLOR_KEY, value);
|
|
17783
|
+
}
|
|
17784
|
+
/** The removed-line base color the user chose, or `undefined` (theme color). */
|
|
17785
|
+
function diffDelColor() {
|
|
17786
|
+
const v = localStorage.getItem(DIFF_DEL_COLOR_KEY);
|
|
17787
|
+
return v !== null && v.length > 0 ? v : void 0;
|
|
17788
|
+
}
|
|
17789
|
+
/** Persist the removed-line base color (a hex like `#ef4444`). */
|
|
17790
|
+
function setDiffDelColor(value) {
|
|
17791
|
+
localStorage.setItem(DIFF_DEL_COLOR_KEY, value);
|
|
17792
|
+
}
|
|
17793
|
+
/** The theme's added-line base color, for the settings swatch default. */
|
|
17794
|
+
function currentDiffAddColor() {
|
|
17795
|
+
return themeColor("--dsw-alias-state-success-primary", "#22c55e");
|
|
17796
|
+
}
|
|
17797
|
+
/** The theme's removed-line base color, for the settings swatch default. */
|
|
17798
|
+
function currentDiffDelColor() {
|
|
17799
|
+
return themeColor("--dsw-alias-state-error-primary", "#ef4444");
|
|
17800
|
+
}
|
|
17801
|
+
/**
|
|
17802
|
+
* The highlight language chosen by hand for one file suffix, remembered so the
|
|
17803
|
+
* choice sticks for every file with that suffix. Not a Settings row: it is a
|
|
17804
|
+
* per-suffix consequence of using the language picker, so it lives here as
|
|
17805
|
+
* storage only.
|
|
17806
|
+
* @param suffix - the lowercase extension without the dot (see `suffixOfPath`).
|
|
17807
|
+
* @returns the remembered language id, or undefined for "auto".
|
|
17808
|
+
*/
|
|
17809
|
+
function languageForSuffix(suffix) {
|
|
17810
|
+
const language = rememberedLanguages()[suffix];
|
|
17811
|
+
return language === void 0 || language === "" ? void 0 : language;
|
|
17812
|
+
}
|
|
17813
|
+
/**
|
|
17814
|
+
* Remember (or clear) the language chosen for one suffix. A `null` language
|
|
17815
|
+
* forgets the suffix, which is what picking "auto" means.
|
|
17816
|
+
* @param suffix - the lowercase extension without the dot.
|
|
17817
|
+
* @param language - the language id, or null to forget the suffix.
|
|
17818
|
+
*/
|
|
17819
|
+
function setLanguageForSuffix(suffix, language) {
|
|
17820
|
+
const stored = rememberedLanguages();
|
|
17821
|
+
if (language === null) {
|
|
17822
|
+
if (stored[suffix] === void 0) return;
|
|
17823
|
+
delete stored[suffix];
|
|
17824
|
+
} else {
|
|
17825
|
+
if (stored[suffix] === language) return;
|
|
17826
|
+
stored[suffix] = language;
|
|
17827
|
+
}
|
|
17828
|
+
localStorage.setItem(LANG_BY_SUFFIX_KEY, JSON.stringify(stored));
|
|
17829
|
+
}
|
|
17830
|
+
/** The whole suffix → language map (empty when unset or unreadable). */
|
|
17831
|
+
function rememberedLanguages() {
|
|
17832
|
+
const raw = localStorage.getItem(LANG_BY_SUFFIX_KEY);
|
|
17833
|
+
if (raw === null) return {};
|
|
17834
|
+
try {
|
|
17835
|
+
const parsed = JSON.parse(raw);
|
|
17836
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return {};
|
|
17837
|
+
const cleaned = {};
|
|
17838
|
+
for (const [suffix, language] of Object.entries(parsed)) if (typeof language === "string" && language !== "") cleaned[suffix] = language;
|
|
17839
|
+
return cleaned;
|
|
17840
|
+
} catch {
|
|
17841
|
+
return {};
|
|
17842
|
+
}
|
|
17843
|
+
}
|
|
17844
|
+
/**
|
|
17845
|
+
* Whether the whole-file diff view uses the two-column (side-by-side) layout.
|
|
17846
|
+
* Default off (single column): the unified diff. Only an explicit `'1'` enables
|
|
17847
|
+
* split mode.
|
|
17848
|
+
* @returns whether the split (two-column) diff view is used.
|
|
17849
|
+
*/
|
|
17850
|
+
function splitMode() {
|
|
17851
|
+
return localStorage.getItem(SPLIT_MODE_KEY) === "1";
|
|
17852
|
+
}
|
|
17853
|
+
/** Persist the split-view preference. */
|
|
17854
|
+
function setSplitMode(value) {
|
|
17855
|
+
localStorage.setItem(SPLIT_MODE_KEY, value ? "1" : "0");
|
|
17856
|
+
}
|
|
17857
|
+
/**
|
|
17858
|
+
* Whether the rendered Markdown preview is shown by default (for Markdown files).
|
|
17859
|
+
* Default off: the source diff is shown unless the panel toggle is used. Only an
|
|
17860
|
+
* explicit `'1'` enables the preview default.
|
|
17861
|
+
* @returns whether the Markdown preview default is enabled.
|
|
17862
|
+
*/
|
|
17863
|
+
function mdPreviewEnabled() {
|
|
17864
|
+
return localStorage.getItem(MD_PREVIEW_KEY) === "1";
|
|
17865
|
+
}
|
|
17866
|
+
/** Persist the Markdown-preview default preference. */
|
|
17867
|
+
function setMdPreviewEnabled(value) {
|
|
17868
|
+
localStorage.setItem(MD_PREVIEW_KEY, value ? "1" : "0");
|
|
17869
|
+
}
|
|
17870
|
+
/**
|
|
17871
|
+
* The Markdown-preview content max width, in pixels (single column). Defaults to
|
|
17872
|
+
* 800; the double-column view totals 2x this. An out-of-range or non-integer
|
|
17873
|
+
* value falls back to the default.
|
|
17874
|
+
* @returns the max width in pixels.
|
|
17875
|
+
*/
|
|
17876
|
+
function mdMaxWidth() {
|
|
17877
|
+
const raw = Number.parseInt(localStorage.getItem(MD_MAX_WIDTH_KEY) ?? "", 10);
|
|
17878
|
+
if (!Number.isInteger(raw)) return 800;
|
|
17879
|
+
return Math.max(480, Math.min(MD_MAX_WIDTH_MAX, raw));
|
|
17880
|
+
}
|
|
17881
|
+
/** Persist the Markdown-preview content max width. */
|
|
17882
|
+
function setMdMaxWidth(value) {
|
|
17883
|
+
localStorage.setItem(MD_MAX_WIDTH_KEY, String(Math.max(480, Math.min(MD_MAX_WIDTH_MAX, value))));
|
|
17884
|
+
}
|
|
17885
|
+
/**
|
|
17886
|
+
* How many rows of lead the diff block jump leaves above the jumped-to block,
|
|
17887
|
+
* and how far the anchored navigation scans. Defaults to 2; an out-of-range or
|
|
17888
|
+
* non-integer value falls back to the default.
|
|
17889
|
+
* @returns the lead row count.
|
|
17890
|
+
*/
|
|
17891
|
+
function navLeadRows() {
|
|
17892
|
+
const raw = Number.parseInt(localStorage.getItem(NAV_LEAD_KEY) ?? "", 10);
|
|
17893
|
+
if (!Number.isInteger(raw)) return 2;
|
|
17894
|
+
return Math.max(0, Math.min(10, raw));
|
|
17895
|
+
}
|
|
17896
|
+
/** Persist the block-jump lead row count. */
|
|
17897
|
+
function setNavLeadRows(value) {
|
|
17898
|
+
localStorage.setItem(NAV_LEAD_KEY, String(Math.max(0, Math.min(10, value))));
|
|
17899
|
+
}
|
|
17900
|
+
const QUICK_SUMMON_KEY = "diff-approval:quick-summon-key";
|
|
17901
|
+
/**
|
|
17902
|
+
* The quick-summon chord. Stored as `Modifier+...+Key`; falls back to
|
|
17903
|
+
* {@link DEFAULT_QUICK_SUMMON}.
|
|
17904
|
+
* @returns the chord string.
|
|
17905
|
+
*/
|
|
17906
|
+
function quickSummonKey() {
|
|
17907
|
+
return localStorage.getItem(QUICK_SUMMON_KEY) ?? "Ctrl+D";
|
|
17908
|
+
}
|
|
17909
|
+
/** Persist the quick-summon chord. */
|
|
17910
|
+
function setQuickSummonKey(value) {
|
|
17911
|
+
localStorage.setItem(QUICK_SUMMON_KEY, value);
|
|
17912
|
+
}
|
|
17913
|
+
const KEY_PREFIX = "diff-approval:key:";
|
|
17914
|
+
/** Default chord for each configurable action (every supported key except the
|
|
17915
|
+
* panel's own ESC-to-close, which is intentionally not remapped). */
|
|
17916
|
+
const DEFAULT_KEYBINDINGS = {
|
|
17917
|
+
jumpUp: "Ctrl+ArrowUp",
|
|
17918
|
+
jumpDown: "Ctrl+ArrowDown",
|
|
17919
|
+
copyRef: "Ctrl+L",
|
|
17920
|
+
openSearch: "Ctrl+F",
|
|
17921
|
+
searchNext: "F3",
|
|
17922
|
+
searchPrev: "Shift+F3",
|
|
17923
|
+
matchCase: "Alt+C",
|
|
17924
|
+
matchWholeWord: "Alt+W",
|
|
17925
|
+
undo: "Ctrl+Z",
|
|
17926
|
+
redo: "Ctrl+Shift+Z",
|
|
17927
|
+
cycleNext: "Ctrl+Tab",
|
|
17928
|
+
cyclePrev: "Ctrl+Shift+Tab"
|
|
17105
17929
|
};
|
|
17930
|
+
/** The currently configured chord for one action; falls back to its default. */
|
|
17931
|
+
function keybindingOf(action) {
|
|
17932
|
+
return localStorage.getItem(`${KEY_PREFIX}${action}`) ?? DEFAULT_KEYBINDINGS[action] ?? "";
|
|
17933
|
+
}
|
|
17934
|
+
/** Persist one action's chord. */
|
|
17935
|
+
function setKeybinding(action, chord) {
|
|
17936
|
+
localStorage.setItem(`${KEY_PREFIX}${action}`, chord);
|
|
17937
|
+
}
|
|
17938
|
+
/**
|
|
17939
|
+
* Whether a keyboard event matches a chord string like `Ctrl+D`. Modifier
|
|
17940
|
+
* names are matched case-insensitively (`Ctrl`/`Control`, `Alt`/`Option`,
|
|
17941
|
+
* `Shift`, `Meta`/`Cmd`/`Command`/`Win`); the final part is the key. Exact
|
|
17942
|
+
* modifier set is required (extra modifiers do not match).
|
|
17943
|
+
* @param event - the keydown event.
|
|
17944
|
+
* @param shortcut - the chord string.
|
|
17945
|
+
* @returns whether the event matches.
|
|
17946
|
+
*/
|
|
17947
|
+
function matchesShortcut(event, shortcut) {
|
|
17948
|
+
const parts = shortcut.split("+").map((part) => part.trim().toLowerCase());
|
|
17949
|
+
const key = parts.pop();
|
|
17950
|
+
if (key === void 0 || key === "") return false;
|
|
17951
|
+
const mods = new Set(parts);
|
|
17952
|
+
const ctrl = mods.has("ctrl") || mods.has("control");
|
|
17953
|
+
const alt = mods.has("alt") || mods.has("option");
|
|
17954
|
+
const shift = mods.has("shift");
|
|
17955
|
+
const meta = mods.has("meta") || mods.has("cmd") || mods.has("command") || mods.has("win");
|
|
17956
|
+
return event.key.toLowerCase() === key && event.ctrlKey === ctrl && event.altKey === alt && event.shiftKey === shift && event.metaKey === meta;
|
|
17957
|
+
}
|
|
17106
17958
|
//#endregion
|
|
17107
17959
|
//#region lib/types/client/PendingPanel.js
|
|
17108
17960
|
/** Sidebar-foot pending-edit review action and the split review panel it opens. */
|
|
@@ -17126,8 +17978,11 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
17126
17978
|
const COMPOSER_HEIGHT_VAR = "--dsh-composer-height";
|
|
17127
17979
|
/** Interactive composer/approval cards; clicking these keeps the panel open.
|
|
17128
17980
|
Deliberately the cards themselves, not the seat: the approval frame's wide
|
|
17129
|
-
side gutters are blank space, so a click there must still close.
|
|
17130
|
-
|
|
17981
|
+
side gutters are blank space, so a click there must still close.
|
|
17982
|
+
`[role="menu"]` covers the portaled menus this panel opens (the highlight
|
|
17983
|
+
language picker): the list is rendered into `document.body`, so picking an
|
|
17984
|
+
item is a pointerdown outside the panel element and would otherwise close it. */
|
|
17985
|
+
const KEEP_OPEN_SELECTOR = "[data-composer-card],[data-question-key] > *,[data-plan-review-key] > *,[data-approval-key] > *,[role=\"menu\"]";
|
|
17131
17986
|
/** Seat counts as docked when its bottom is this close to the window bottom. */
|
|
17132
17987
|
const DOCKED_TOLERANCE_PX = 48;
|
|
17133
17988
|
/** File-list pane width bounds for the manual split drag, in px. */
|
|
@@ -17379,6 +18234,10 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
17379
18234
|
* 5px frame padding on each side + 1px border on each side, plus a little
|
|
17380
18235
|
* breathing room so the bottom padding never sits flush against it. */
|
|
17381
18236
|
const BLOCK_ACTIONS_FRAME_PX = 40;
|
|
18237
|
+
/** How far a floating block-action frame stays inside the surface it is anchored
|
|
18238
|
+
* to (the content's right edge in the Markdown preview, the pane's in the code
|
|
18239
|
+
* view). */
|
|
18240
|
+
const FRAME_INSET_PX = 8;
|
|
17382
18241
|
/** The trailing file-name segment of a path, used for row display. */
|
|
17383
18242
|
function basenameOf(path) {
|
|
17384
18243
|
const index = Math.max(path.lastIndexOf("/"), path.lastIndexOf("\\"));
|
|
@@ -17431,6 +18290,29 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
17431
18290
|
Array.from(document.querySelectorAll("button")).find((button) => button.textContent?.trim() === sectionLabel)?.click();
|
|
17432
18291
|
});
|
|
17433
18292
|
}
|
|
18293
|
+
/**
|
|
18294
|
+
* Search narrowing glyphs, taken from VS Code's Codicons so the two find-widget
|
|
18295
|
+
* toggles read exactly as they do in that editor: `case-sensitive` (an upper- and
|
|
18296
|
+
* lower-case letter pair) and `whole-word` (the same letter pair over a bar that
|
|
18297
|
+
* marks the word boundary). Inline SVG because the icon library ships neither.
|
|
18298
|
+
* @param props.kind - which glyph to draw.
|
|
18299
|
+
* @returns the 16px-grid glyph.
|
|
18300
|
+
*/
|
|
18301
|
+
function SearchOptionIcon({ kind }) {
|
|
18302
|
+
return (0, react_jsx_runtime.jsx)("svg", {
|
|
18303
|
+
width: 16,
|
|
18304
|
+
height: 16,
|
|
18305
|
+
viewBox: "0 0 16 16",
|
|
18306
|
+
fill: "none",
|
|
18307
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
18308
|
+
children: (0, react_jsx_runtime.jsx)("path", {
|
|
18309
|
+
fill: "currentColor",
|
|
18310
|
+
fillRule: "evenodd",
|
|
18311
|
+
clipRule: "evenodd",
|
|
18312
|
+
d: kind === "case" ? `M4.02602 3.34176C4.16218 2.93404 4.83818 2.93398 4.97426 3.34176L6.97426 9.34274C6.97526 9.34674 6.97817 9.35544 6.97817 9.35544L7.97426 12.3427C8.06126 12.6047 7.91984 12.8875 7.65786 12.9756C7.60486 12.9926 7.55165 13.0009 7.49965 13.0009C7.29082 13.0008 7.09602 12.868 7.02602 12.6591L6.14028 10.0009H2.86L1.97426 12.6591C1.88728 12.919 1.60634 13.0634 1.34243 12.9746C1.08043 12.8866 0.93902 12.6038 1.02602 12.3418L2.02211 9.35544C2.02311 9.35144 2.02602 9.34274 2.02602 9.34274L4.02602 3.34176ZM3.19399 8.99997H5.80629L4.49965 5.08102L3.19399 8.99997ZM11.8581 6.66794C13.165 6.73296 13.9427 7.48427 13.9967 8.69626L13.9997 8.83297V12.5078C13.9957 12.7568 13.809 12.9621 13.568 12.9951L13.4997 13C13.2469 12.9998 13.0376 12.8121 13.0045 12.5683L12.9997 12.5V12.4297C12.3407 12.8066 11.7316 13 11.1666 13C9.94081 12.9998 8.99965 12.1369 8.99965 10.833C8.99967 9.68299 9.79211 8.82889 11.1061 8.66989C11.7279 8.59493 12.3589 8.64164 12.9987 8.80954C12.9915 8.07194 12.6279 7.70704 11.8082 7.66598C11.1672 7.63398 10.7158 7.72415 10.4518 7.90915C10.2258 8.06799 9.91347 8.01301 9.75551 7.78708C9.59671 7.56115 9.65178 7.24878 9.87758 7.09079C10.3165 6.78283 10.9138 6.64715 11.6666 6.6611L11.8581 6.66794ZM12.7965 9.8154C12.2587 9.66749 11.7361 9.62551 11.2262 9.68747C10.4042 9.78747 9.99868 10.2244 9.99868 10.8574C9.99884 11.5881 10.474 12.0242 11.1657 12.0244C11.6196 12.0244 12.1777 11.8137 12.8336 11.3818L12.9987 11.2695V9.87594L12.7965 9.8154Z` : `M4.8584 5.6709C6.16516 5.73603 6.94308 6.48734 6.99707 7.69922L7 7.83594V11.5107C6.996 11.7596 6.80919 11.9649 6.56836 11.998L6.5 12.0029C6.24709 12.0029 6.038 11.8152 6.00488 11.5713L6 11.5029V11.4326C5.341 11.8096 4.73199 12.0029 4.16699 12.0029C2.941 12.0029 2 11.1399 2 9.83594C2.00003 8.68597 2.79247 7.83185 4.10645 7.67285C4.7283 7.59793 5.35918 7.64552 5.99902 7.81348C5.99202 7.07548 5.62762 6.70995 4.80762 6.66895C4.16686 6.637 3.7161 6.72717 3.45215 6.91211C3.22615 7.07111 2.91386 7.01604 2.75586 6.79004C2.5969 6.56404 2.65194 6.25174 2.87793 6.09375C3.31692 5.78579 3.91404 5.65006 4.66699 5.66406L4.8584 5.6709ZM5.79688 8.81836C5.25888 8.67037 4.73558 8.62843 4.22559 8.69043C3.40389 8.79054 2.99902 9.22747 2.99902 9.86035C2.99917 10.5911 3.47413 11.0273 4.16602 11.0273C4.62001 11.0273 5.17799 10.8168 5.83398 10.3848L5.99902 10.2725V8.87891L5.79688 8.81836ZM9.55078 2.00586C9.78578 2.02986 9.97307 2.21715 9.99707 2.45215C10 2.46907 10 2.48601 10 2.50293V6.60254C10.418 6.22566 10.9371 6.00293 11.5 6.00293C12.881 6.00293 14 7.34596 14 9.00293C14 10.6599 12.881 12.0029 11.5 12.0029C10.9371 12.0029 10.418 11.7802 10 11.4033V11.5029C10 11.7619 9.80278 11.974 9.55078 12C9.53385 12.003 9.51693 12.0029 9.5 12.0029C9.224 12.0029 9 11.7789 9 11.5029V2.50293C9 2.486 9.00095 2.46907 9.00293 2.45215C9.02793 2.20015 9.241 2.00293 9.5 2.00293C9.51692 2.00293 9.53386 2.00388 9.55078 2.00586ZM11.4355 7.00391C11.0307 7.03208 10.5769 7.31545 10.29 7.82227C10.1232 8.12611 10.018 8.49479 10.002 8.89453C9.99995 8.92952 10 8.96597 10 9.00195C10 9.03795 10.001 9.07438 10.002 9.10938C10.018 9.50814 10.1222 9.87582 10.2891 10.1797C10.576 10.6875 11.0307 10.9728 11.4355 11C11.4565 11.002 11.478 11.002 11.5 11.002C11.522 11.002 11.5435 11.001 11.5645 11C11.9693 10.9728 12.424 10.6875 12.7109 10.1797C12.8778 9.87582 12.982 9.50814 12.998 9.10938C13 9.07438 13 9.03795 13 9.00195C13 8.96597 12.999 8.92952 12.998 8.89453C12.982 8.49479 12.8768 8.12611 12.71 7.82227C12.4231 7.31545 11.9693 7.03109 11.5645 7.00391C11.5435 7.00191 11.522 7.00195 11.5 7.00195C11.478 7.00195 11.4565 7.00291 11.4355 7.00391ZM15.5 12.5C15.776 12.5 16 12.724 16 13V13.5C16 14.327 15.327 15 14.5 15H1.5C0.673 15 0 14.327 0 13.5V13C0 12.724 0.224 12.5 0.5 12.5C0.776 12.5 1 12.724 1 13V13.5C1 13.775 1.224 14 1.5 14H14.5C14.776 14 15 13.775 15 13.5V13C15 12.724 15.224 12.5 15.5 12.5Z`
|
|
18313
|
+
})
|
|
18314
|
+
});
|
|
18315
|
+
}
|
|
17434
18316
|
/** The diff body's row class per line kind. */
|
|
17435
18317
|
const ROW_CLASS = {
|
|
17436
18318
|
context: PendingPanel_module_css_default.context,
|
|
@@ -17509,27 +18391,13 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
17509
18391
|
}, i);
|
|
17510
18392
|
});
|
|
17511
18393
|
}
|
|
17512
|
-
/** Character ranges of each case-insensitive occurrence of `query` in `text`. */
|
|
17513
|
-
function matchRangesOf(text, query) {
|
|
17514
|
-
if (query === "") return [];
|
|
17515
|
-
const lower = text.toLowerCase();
|
|
17516
|
-
const q = query.toLowerCase();
|
|
17517
|
-
const out = [];
|
|
17518
|
-
let from = 0;
|
|
17519
|
-
for (;;) {
|
|
17520
|
-
const at = lower.indexOf(q, from);
|
|
17521
|
-
if (at === -1) return out;
|
|
17522
|
-
out.push([at, at + query.length]);
|
|
17523
|
-
from = at + query.length;
|
|
17524
|
-
}
|
|
17525
|
-
}
|
|
17526
18394
|
/** Render `text[segStart, segEnd)` with every `query` match wrapped in a search
|
|
17527
18395
|
* highlight, keeping the syntax highlight and intra-line chips on the non-match
|
|
17528
18396
|
* parts. When no match is present it renders exactly as before (syntax / intra /
|
|
17529
18397
|
* plain). `segStart`/`segEnd` let the caller render a wrapped sub-line range. */
|
|
17530
|
-
function textWithSearch(text, runs, intra, query, segStart = 0, segEnd = text.length, current = false) {
|
|
18398
|
+
function textWithSearch(text, runs, intra, options, query, segStart = 0, segEnd = text.length, current = false) {
|
|
17531
18399
|
const segText = text.slice(segStart, segEnd);
|
|
17532
|
-
const ranges = matchRangesOf(segText, query);
|
|
18400
|
+
const ranges = matchRangesOf(segText, query, options);
|
|
17533
18401
|
if (ranges.length === 0) return intra !== void 0 && intra.length > 0 ? renderIntra(runs, intra, segStart, segEnd) : runs !== void 0 && runs.length > 0 ? clipRuns(runs, segStart, segEnd) : segText === "" ? "\xA0" : segText;
|
|
17534
18402
|
const nodes = [];
|
|
17535
18403
|
const push = (absStart, absEnd, isMatch) => {
|
|
@@ -17556,6 +18424,96 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
17556
18424
|
if (cursor < segEnd) push(cursor, segEnd, false);
|
|
17557
18425
|
return nodes;
|
|
17558
18426
|
}
|
|
18427
|
+
/** Arrow keys render as arrows in a tooltip hint (`Ctrl+↑`, not `Ctrl+ArrowUp`). */
|
|
18428
|
+
const CHORD_KEY_GLYPHS = {
|
|
18429
|
+
ArrowUp: "↑",
|
|
18430
|
+
ArrowDown: "↓",
|
|
18431
|
+
ArrowLeft: "←",
|
|
18432
|
+
ArrowRight: "→"
|
|
18433
|
+
};
|
|
18434
|
+
/**
|
|
18435
|
+
* A tooltip label with the action's configured chord appended. The hint states
|
|
18436
|
+
* the binding the user actually has — including one rebound in Settings — rather
|
|
18437
|
+
* than a default baked into the label.
|
|
18438
|
+
* @param label - the translated action label.
|
|
18439
|
+
* @param action - the keybinding action id (see `DEFAULT_KEYBINDINGS`).
|
|
18440
|
+
* @returns the label, with ` (chord)` appended when one is configured.
|
|
18441
|
+
*/
|
|
18442
|
+
function withChord(label, action) {
|
|
18443
|
+
const chord = keybindingOf(action);
|
|
18444
|
+
if (chord === "") return label;
|
|
18445
|
+
return `${label} (${chord.split("+").map((part) => CHORD_KEY_GLYPHS[part] ?? part).join("+")})`;
|
|
18446
|
+
}
|
|
18447
|
+
/**
|
|
18448
|
+
* Whether a key event's target is one of the panel's search query boxes. The
|
|
18449
|
+
* bar's own chords (step, narrow) are scoped this way: the caret has to be in
|
|
18450
|
+
* the box, so an open bar is not enough — the same chord must stay free for
|
|
18451
|
+
* whatever else happens to have focus (the chat composer above all), and a mouse
|
|
18452
|
+
* click on a bar button hands the focus straight back to the box.
|
|
18453
|
+
* @param event - the keydown event.
|
|
18454
|
+
* @returns whether the event came from a search query box.
|
|
18455
|
+
*/
|
|
18456
|
+
function isSearchInputEvent(event) {
|
|
18457
|
+
const target = event.target;
|
|
18458
|
+
return target instanceof HTMLInputElement && target.hasAttribute("data-diff-search-input");
|
|
18459
|
+
}
|
|
18460
|
+
/**
|
|
18461
|
+
* Whether a key event came from inside the plugin's own panel. Esc is split along
|
|
18462
|
+
* that line: a press inside the panel is the panel's to dismiss (its search bar
|
|
18463
|
+
* first, otherwise the panel itself), while anything outside it — the chat
|
|
18464
|
+
* composer above all — dismisses the panel and keeps its own Esc besides.
|
|
18465
|
+
* @param event - the keydown event.
|
|
18466
|
+
* @returns whether the event came from inside the panel.
|
|
18467
|
+
*/
|
|
18468
|
+
function isInPanelEvent(event) {
|
|
18469
|
+
const target = event.target;
|
|
18470
|
+
return target instanceof Element && target.closest("[data-diff-approval-panel]") !== null;
|
|
18471
|
+
}
|
|
18472
|
+
/**
|
|
18473
|
+
* Whether a key event came from a text field that owns its own keys (`Esc`,
|
|
18474
|
+
* cursor moves). The chat composer is the one that matters: the panel leaves it
|
|
18475
|
+
* alone even while its own search bar is open.
|
|
18476
|
+
* @param event - the keydown event.
|
|
18477
|
+
* @returns whether the event came from a text field.
|
|
18478
|
+
*/
|
|
18479
|
+
function isTextFieldEvent(event) {
|
|
18480
|
+
const target = event.target;
|
|
18481
|
+
return target instanceof Element && target.closest("input, textarea, [contenteditable=\"true\"]") !== null;
|
|
18482
|
+
}
|
|
18483
|
+
/**
|
|
18484
|
+
* The in-file search's narrowing toggles. The persisted preference is the
|
|
18485
|
+
* source of truth, so the choice survives a reopen and the two views cannot
|
|
18486
|
+
* disagree: each bar owns one of these, and it re-reads the stored flags with
|
|
18487
|
+
* {@link sync} whenever its bar opens. The options object is memoized so the
|
|
18488
|
+
* memoized rows only re-render when a toggle changes.
|
|
18489
|
+
* @returns the options, their current values, the two toggles, and the re-read.
|
|
18490
|
+
*/
|
|
18491
|
+
function useSearchOptions() {
|
|
18492
|
+
const [caseSensitive, setCase] = (0, react.useState)(searchCaseSensitive);
|
|
18493
|
+
const [wholeWord, setWord] = (0, react.useState)(searchWholeWord);
|
|
18494
|
+
return {
|
|
18495
|
+
options: (0, react.useMemo)(() => ({
|
|
18496
|
+
caseSensitive,
|
|
18497
|
+
wholeWord
|
|
18498
|
+
}), [caseSensitive, wholeWord]),
|
|
18499
|
+
caseSensitive,
|
|
18500
|
+
wholeWord,
|
|
18501
|
+
toggleCase: (0, react.useCallback)(() => {
|
|
18502
|
+
const next = !searchCaseSensitive();
|
|
18503
|
+
setSearchCaseSensitive(next);
|
|
18504
|
+
setCase(next);
|
|
18505
|
+
}, []),
|
|
18506
|
+
toggleWord: (0, react.useCallback)(() => {
|
|
18507
|
+
const next = !searchWholeWord();
|
|
18508
|
+
setSearchWholeWord(next);
|
|
18509
|
+
setWord(next);
|
|
18510
|
+
}, []),
|
|
18511
|
+
sync: (0, react.useCallback)(() => {
|
|
18512
|
+
setCase(searchCaseSensitive());
|
|
18513
|
+
setWord(searchWholeWord());
|
|
18514
|
+
}, [])
|
|
18515
|
+
};
|
|
18516
|
+
}
|
|
17559
18517
|
/**
|
|
17560
18518
|
* One rendered diff row, memoized so a poll or an unrelated state change
|
|
17561
18519
|
* does not re-render rows whose content, highlight, and focus are unchanged.
|
|
@@ -17564,18 +18522,18 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
17564
18522
|
* is `wrappedLines.length * 22` by construction.
|
|
17565
18523
|
*/
|
|
17566
18524
|
const DiffRow = (0, react.memo)(function DiffRow(props) {
|
|
17567
|
-
const { index, row, runs, focused, searchHit, searchCurrent, searchQuery, onRowHover, wrappedLines } = props;
|
|
18525
|
+
const { index, row, runs, focused, searchHit, searchCurrent, searchQuery, searchOptions, onRowHover, wrappedLines } = props;
|
|
17568
18526
|
const lineNumber = row.kind === "del" ? row.oldLine : row.newLine;
|
|
17569
18527
|
const sideRuns = row.kind === "del" ? runs?.oldRuns : runs?.newRuns;
|
|
17570
18528
|
const lineRuns = lineNumber === void 0 ? void 0 : sideRuns?.[lineNumber - 1];
|
|
17571
18529
|
let code;
|
|
17572
|
-
if (wrappedLines === void 0) code = textWithSearch(row.text, lineRuns, void 0, searchQuery, 0, row.text.length, searchCurrent);
|
|
18530
|
+
if (wrappedLines === void 0) code = textWithSearch(row.text, lineRuns, void 0, searchOptions, searchQuery, 0, row.text.length, searchCurrent);
|
|
17573
18531
|
else {
|
|
17574
18532
|
let offset = 0;
|
|
17575
18533
|
code = wrappedLines.map((line, lineIndex) => {
|
|
17576
18534
|
const start = offset;
|
|
17577
18535
|
offset += line.length;
|
|
17578
|
-
const content = textWithSearch(row.text, lineRuns, void 0, searchQuery, start, offset, searchCurrent);
|
|
18536
|
+
const content = textWithSearch(row.text, lineRuns, void 0, searchOptions, searchQuery, start, offset, searchCurrent);
|
|
17579
18537
|
return (0, react_jsx_runtime.jsx)("div", {
|
|
17580
18538
|
className: PendingPanel_module_css_default.subline,
|
|
17581
18539
|
children: content
|
|
@@ -17611,69 +18569,6 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
17611
18569
|
]
|
|
17612
18570
|
});
|
|
17613
18571
|
});
|
|
17614
|
-
/**
|
|
17615
|
-
* One diff block's old/new line ranges, 1-based inclusive, for block-level
|
|
17616
|
-
* keep/revert. A side with no lines (a pure addition or deletion) is empty;
|
|
17617
|
-
* its start is that side's insertion point — the line after the surrounding
|
|
17618
|
-
* context — so the host can insert there.
|
|
17619
|
-
* @param rows - the whole-file diff rows.
|
|
17620
|
-
* @param block - the block's row range.
|
|
17621
|
-
* @returns the old and new line ranges.
|
|
17622
|
-
*/
|
|
17623
|
-
function blockRangesOf(rows, block) {
|
|
17624
|
-
let oldStart = Infinity;
|
|
17625
|
-
let oldEnd = -Infinity;
|
|
17626
|
-
let newStart = Infinity;
|
|
17627
|
-
let newEnd = -Infinity;
|
|
17628
|
-
for (let index = block.start; index <= block.end; index++) {
|
|
17629
|
-
const row = rows[index];
|
|
17630
|
-
if (row === void 0) continue;
|
|
17631
|
-
if (row.oldLine !== void 0) {
|
|
17632
|
-
oldStart = Math.min(oldStart, row.oldLine);
|
|
17633
|
-
oldEnd = Math.max(oldEnd, row.oldLine);
|
|
17634
|
-
}
|
|
17635
|
-
if (row.newLine !== void 0) {
|
|
17636
|
-
newStart = Math.min(newStart, row.newLine);
|
|
17637
|
-
newEnd = Math.max(newEnd, row.newLine);
|
|
17638
|
-
}
|
|
17639
|
-
}
|
|
17640
|
-
const before = rows[block.start - 1];
|
|
17641
|
-
if (oldStart === Infinity) {
|
|
17642
|
-
oldStart = (before?.oldLine ?? 0) + 1;
|
|
17643
|
-
oldEnd = oldStart - 1;
|
|
17644
|
-
}
|
|
17645
|
-
if (newStart === Infinity) {
|
|
17646
|
-
newStart = (before?.newLine ?? 0) + 1;
|
|
17647
|
-
newEnd = newStart - 1;
|
|
17648
|
-
}
|
|
17649
|
-
return {
|
|
17650
|
-
oldStart,
|
|
17651
|
-
oldEnd,
|
|
17652
|
-
newStart,
|
|
17653
|
-
newEnd
|
|
17654
|
-
};
|
|
17655
|
-
}
|
|
17656
|
-
/** Split a row list into maximal runs of non-context rows. */
|
|
17657
|
-
function changeBlocksOf(diff) {
|
|
17658
|
-
const blocks = [];
|
|
17659
|
-
let start = -1;
|
|
17660
|
-
diff.rows.forEach((row, index) => {
|
|
17661
|
-
if (row.kind !== "context") {
|
|
17662
|
-
if (start === -1) start = index;
|
|
17663
|
-
} else if (start !== -1) {
|
|
17664
|
-
blocks.push({
|
|
17665
|
-
start,
|
|
17666
|
-
end: index - 1
|
|
17667
|
-
});
|
|
17668
|
-
start = -1;
|
|
17669
|
-
}
|
|
17670
|
-
});
|
|
17671
|
-
if (start !== -1) blocks.push({
|
|
17672
|
-
start,
|
|
17673
|
-
end: diff.rows.length - 1
|
|
17674
|
-
});
|
|
17675
|
-
return blocks;
|
|
17676
|
-
}
|
|
17677
18572
|
/** Whether a block keep/revert range covers the file's entire change region, so
|
|
17678
18573
|
* applying it leaves the file with no pending diff — the "remove or keep in
|
|
17679
18574
|
* list" prompt applies. This generalises the single-block case to a selection
|
|
@@ -17695,23 +18590,60 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
17695
18590
|
}
|
|
17696
18591
|
return block.oldStart <= oldMin && block.oldEnd >= oldMax && block.newStart <= newMin && block.newEnd >= newMax;
|
|
17697
18592
|
}
|
|
17698
|
-
/** Row indices whose text
|
|
17699
|
-
|
|
18593
|
+
/** Row indices whose text matches `query` under the options; empty for ''. Counted
|
|
18594
|
+
* through the same matcher the highlights use, so the tally can never disagree
|
|
18595
|
+
* with what is drawn. */
|
|
18596
|
+
function matchingRows(rows, query, options) {
|
|
17700
18597
|
if (query === "") return [];
|
|
17701
|
-
const lower = query.toLowerCase();
|
|
17702
18598
|
const out = [];
|
|
17703
|
-
for (let i = 0; i < rows.length; i++) if (rows[i].text
|
|
18599
|
+
for (let i = 0; i < rows.length; i++) if (matchRangesOf(rows[i].text, query, options).length > 0) out.push(i);
|
|
17704
18600
|
return out;
|
|
17705
18601
|
}
|
|
18602
|
+
/** The source lines one row window covers on a side (1-based, inclusive), or
|
|
18603
|
+
* undefined when the window shows none of that side — an all-added or
|
|
18604
|
+
* all-deleted stretch, where the other side has nothing to highlight. */
|
|
18605
|
+
function windowLineRange(rows, from, to, side) {
|
|
18606
|
+
let first = Infinity;
|
|
18607
|
+
let last = -Infinity;
|
|
18608
|
+
for (let index = from; index < to; index++) {
|
|
18609
|
+
const row = rows[index];
|
|
18610
|
+
if (row === void 0) continue;
|
|
18611
|
+
const line = side === "old" ? row.oldLine : row.newLine;
|
|
18612
|
+
if (line === void 0) continue;
|
|
18613
|
+
if (line < first) first = line;
|
|
18614
|
+
if (line > last) last = line;
|
|
18615
|
+
}
|
|
18616
|
+
return Number.isFinite(first) ? {
|
|
18617
|
+
from: first,
|
|
18618
|
+
to: last
|
|
18619
|
+
} : void 0;
|
|
18620
|
+
}
|
|
18621
|
+
/** The source lines one split-view pair window covers on a side (1-based,
|
|
18622
|
+
* inclusive), or undefined when the window shows none of that side. */
|
|
18623
|
+
function pairLineRange(pairs, from, to, side) {
|
|
18624
|
+
let first = Infinity;
|
|
18625
|
+
let last = -Infinity;
|
|
18626
|
+
for (let index = from; index < to; index++) {
|
|
18627
|
+
const pair = pairs[index];
|
|
18628
|
+
const line = side === "left" ? pair?.left?.line : pair?.right?.line;
|
|
18629
|
+
if (line === void 0) continue;
|
|
18630
|
+
if (line < first) first = line;
|
|
18631
|
+
if (line > last) last = line;
|
|
18632
|
+
}
|
|
18633
|
+
return Number.isFinite(first) ? {
|
|
18634
|
+
from: first,
|
|
18635
|
+
to: last
|
|
18636
|
+
} : void 0;
|
|
18637
|
+
}
|
|
17706
18638
|
/** One side's line-content for the split view: the highlighted runs or plain text. */
|
|
17707
|
-
function splitSideContent(side, wrapped, runs, intra, query, current) {
|
|
18639
|
+
function splitSideContent(side, wrapped, runs, intra, options, query, current) {
|
|
17708
18640
|
if (side === void 0) return "";
|
|
17709
|
-
if (wrapped === void 0) return textWithSearch(side.text, runs, intra, query, 0, side.text.length, current);
|
|
18641
|
+
if (wrapped === void 0) return textWithSearch(side.text, runs, intra, options, query, 0, side.text.length, current);
|
|
17710
18642
|
let offset = 0;
|
|
17711
18643
|
return wrapped.map((line, i) => {
|
|
17712
18644
|
const start = offset;
|
|
17713
18645
|
offset += line.length;
|
|
17714
|
-
const content = textWithSearch(side.text, runs, intra, query, start, offset, current);
|
|
18646
|
+
const content = textWithSearch(side.text, runs, intra, options, query, start, offset, current);
|
|
17715
18647
|
return (0, react_jsx_runtime.jsx)("div", {
|
|
17716
18648
|
className: PendingPanel_module_css_default.subline,
|
|
17717
18649
|
children: content
|
|
@@ -17727,7 +18659,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
17727
18659
|
* one side is longer. The gutter and code are top-aligned so sub-lines line up
|
|
17728
18660
|
* across the divider.
|
|
17729
18661
|
*/
|
|
17730
|
-
function SplitSideRow({ index, side, wrapped, runs, kind, isLeft, height, focused, searchHit, searchCurrent, searchQuery, onHover, intra }) {
|
|
18662
|
+
function SplitSideRow({ index, side, wrapped, runs, kind, isLeft, height, focused, searchHit, searchCurrent, searchQuery, searchOptions, onHover, intra }) {
|
|
17731
18663
|
const tint = isLeft ? kind === "del" || kind === "replace" ? PendingPanel_module_css_default.splitLdel : "" : kind === "add" || kind === "replace" ? PendingPanel_module_css_default.splitRadd : "";
|
|
17732
18664
|
return (0, react_jsx_runtime.jsxs)("div", {
|
|
17733
18665
|
className: PendingPanel_module_css_default.line,
|
|
@@ -17746,12 +18678,12 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
17746
18678
|
className: `${PendingPanel_module_css_default.code} ${tint}`,
|
|
17747
18679
|
"data-diff-code": true,
|
|
17748
18680
|
"data-diff-code-side": isLeft ? "left" : "right",
|
|
17749
|
-
children: splitSideContent(side, wrapped, runs, intra, searchQuery, searchCurrent)
|
|
18681
|
+
children: splitSideContent(side, wrapped, runs, intra, searchOptions, searchQuery, searchCurrent)
|
|
17750
18682
|
})]
|
|
17751
18683
|
});
|
|
17752
18684
|
}
|
|
17753
18685
|
/** The two-column (side-by-side) whole-file diff view. */
|
|
17754
|
-
const SplitDiff = (0, react.forwardRef)(function SplitDiff({ file, model, runs, langWrap, tabWidthSpaces, busy, t, selection, leadRows, onBlockKeep, onBlockRevert, onWrapToast }, ref) {
|
|
18686
|
+
const SplitDiff = (0, react.forwardRef)(function SplitDiff({ file, model, runs, langWrap, tabWidthSpaces, busy, t, selection, leadRows, onBlockKeep, onBlockRevert, onWrapToast, onVisibleLines }, ref) {
|
|
17755
18687
|
const ROW_HEIGHT_PX = diffLineHeight();
|
|
17756
18688
|
const NAV_ANCHOR_TOLERANCE_PX = ROW_HEIGHT_PX / 4;
|
|
17757
18689
|
const { pairs, pairOfRow } = (0, react.useMemo)(() => computeSideBySideDiff(model.diff.rows, true), [model]);
|
|
@@ -17791,6 +18723,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
17791
18723
|
});
|
|
17792
18724
|
const [searchOpen, setSearchOpen] = (0, react.useState)(false);
|
|
17793
18725
|
const [searchQuery, setSearchQuery] = (0, react.useState)("");
|
|
18726
|
+
const search = useSearchOptions();
|
|
17794
18727
|
const [searchIndex, setSearchIndex] = (0, react.useState)(0);
|
|
17795
18728
|
const searchInputRef = (0, react.useRef)(null);
|
|
17796
18729
|
(0, react.useEffect)(() => {
|
|
@@ -17916,14 +18849,43 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
17916
18849
|
if (strip === null || col === null) return;
|
|
17917
18850
|
col.scrollLeft = strip.scrollLeft;
|
|
17918
18851
|
}, []);
|
|
17919
|
-
const searchMatches = (0, react.useMemo)(() => searchPairs(pairs, searchQuery), [
|
|
18852
|
+
const searchMatches = (0, react.useMemo)(() => searchPairs(pairs, searchQuery, search.options), [
|
|
18853
|
+
pairs,
|
|
18854
|
+
searchQuery,
|
|
18855
|
+
search.options
|
|
18856
|
+
]);
|
|
17920
18857
|
const searchHitSet = (0, react.useMemo)(() => new Set(searchMatches), [searchMatches]);
|
|
17921
18858
|
const currentSearchPair = searchMatches.length === 0 ? void 0 : searchMatches[searchIndex % searchMatches.length];
|
|
17922
18859
|
const closeSearch = () => {
|
|
18860
|
+
if (!searchOpen) return false;
|
|
17923
18861
|
setSearchOpen(false);
|
|
17924
18862
|
setSearchQuery("");
|
|
17925
18863
|
setSearchIndex(0);
|
|
17926
18864
|
bodyRef.current?.focus();
|
|
18865
|
+
return true;
|
|
18866
|
+
};
|
|
18867
|
+
/** Run one bar control's action, then hand the focus back to the query box:
|
|
18868
|
+
* the bar's chords are scoped to the box (its Esc to the bar), so a mouse
|
|
18869
|
+
* click on a bar button must not leave them dead on the button it landed on. */
|
|
18870
|
+
const andRefocus = (action) => {
|
|
18871
|
+
action();
|
|
18872
|
+
searchInputRef.current?.focus();
|
|
18873
|
+
};
|
|
18874
|
+
/** The shared toolbar's search button: the split view owns its own bar, so the
|
|
18875
|
+
* button has to toggle this one rather than the single-column state. */
|
|
18876
|
+
const toggleSearch = () => {
|
|
18877
|
+
if (searchOpen) closeSearch();
|
|
18878
|
+
else openSearch();
|
|
18879
|
+
};
|
|
18880
|
+
const toggleMatchCase = () => {
|
|
18881
|
+
if (!searchOpen) return false;
|
|
18882
|
+
search.toggleCase();
|
|
18883
|
+
return true;
|
|
18884
|
+
};
|
|
18885
|
+
const toggleMatchWholeWord = () => {
|
|
18886
|
+
if (!searchOpen) return false;
|
|
18887
|
+
search.toggleWord();
|
|
18888
|
+
return true;
|
|
17927
18889
|
};
|
|
17928
18890
|
const openSearch = () => {
|
|
17929
18891
|
if (searchOpen) {
|
|
@@ -17931,13 +18893,14 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
17931
18893
|
searchInputRef.current?.select();
|
|
17932
18894
|
return;
|
|
17933
18895
|
}
|
|
18896
|
+
search.sync();
|
|
17934
18897
|
const live = window.getSelection();
|
|
17935
18898
|
const liveRange = splitRowRangeOf(live);
|
|
17936
18899
|
const range = liveRange !== void 0 ? liveRange : selection;
|
|
17937
18900
|
const liveText = (live?.toString() ?? "").trim();
|
|
17938
18901
|
const value = liveText !== "" && !liveText.includes("\n") ? liveText : "";
|
|
17939
18902
|
setSearchQuery(value);
|
|
17940
|
-
const matches = value === "" ? [] : searchPairs(pairs, value);
|
|
18903
|
+
const matches = value === "" ? [] : searchPairs(pairs, value, search.options);
|
|
17941
18904
|
let index = 0;
|
|
17942
18905
|
if (matches.length > 0) {
|
|
17943
18906
|
const inSel = range === void 0 ? -1 : matches.findIndex((i) => i >= range.start && i <= range.end);
|
|
@@ -18010,6 +18973,19 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
18010
18973
|
end = Math.max(end, selection.end + 1);
|
|
18011
18974
|
}
|
|
18012
18975
|
const visiblePairs = pairs.slice(start, end);
|
|
18976
|
+
(0, react.useEffect)(() => {
|
|
18977
|
+
onVisibleLines({
|
|
18978
|
+
oldRange: pairLineRange(pairs, start, end, "left"),
|
|
18979
|
+
newRange: pairLineRange(pairs, start, end, "right"),
|
|
18980
|
+
live: viewportH > 0
|
|
18981
|
+
});
|
|
18982
|
+
}, [
|
|
18983
|
+
onVisibleLines,
|
|
18984
|
+
pairs,
|
|
18985
|
+
start,
|
|
18986
|
+
end,
|
|
18987
|
+
viewportH
|
|
18988
|
+
]);
|
|
18013
18989
|
const wrapArmedRef = (0, react.useRef)(0);
|
|
18014
18990
|
const jump = (direction, wrapGuard = false, singleToast = wrapGuard) => {
|
|
18015
18991
|
const count = blockOfPair.length;
|
|
@@ -18051,11 +19027,19 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
18051
19027
|
(0, react.useImperativeHandle)(ref, () => ({
|
|
18052
19028
|
jump,
|
|
18053
19029
|
openSearch,
|
|
18054
|
-
|
|
19030
|
+
toggleSearch,
|
|
19031
|
+
closeSearch,
|
|
19032
|
+
searchNext,
|
|
19033
|
+
toggleMatchCase,
|
|
19034
|
+
toggleMatchWholeWord
|
|
18055
19035
|
}), [
|
|
18056
19036
|
jump,
|
|
18057
19037
|
openSearch,
|
|
18058
|
-
|
|
19038
|
+
toggleSearch,
|
|
19039
|
+
closeSearch,
|
|
19040
|
+
searchNext,
|
|
19041
|
+
toggleMatchCase,
|
|
19042
|
+
toggleMatchWholeWord
|
|
18059
19043
|
]);
|
|
18060
19044
|
(0, react.useLayoutEffect)(() => {
|
|
18061
19045
|
if (pairCount === 0) return;
|
|
@@ -18139,6 +19123,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
18139
19123
|
searchHit: searchHitSet.has(index),
|
|
18140
19124
|
searchCurrent: index === currentSearchPair,
|
|
18141
19125
|
searchQuery,
|
|
19126
|
+
searchOptions: search.options,
|
|
18142
19127
|
onHover: () => onPairHover(index),
|
|
18143
19128
|
intra: sideIndex?.left === void 0 ? void 0 : model.intra.get(sideIndex.left)
|
|
18144
19129
|
}, index);
|
|
@@ -18181,6 +19166,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
18181
19166
|
searchHit: searchHitSet.has(index),
|
|
18182
19167
|
searchCurrent: index === currentSearchPair,
|
|
18183
19168
|
searchQuery,
|
|
19169
|
+
searchOptions: search.options,
|
|
18184
19170
|
onHover: () => onPairHover(index),
|
|
18185
19171
|
intra: sideIndex?.right === void 0 ? void 0 : model.intra.get(sideIndex.right)
|
|
18186
19172
|
}, index);
|
|
@@ -18246,7 +19232,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
18246
19232
|
setSearchQuery(value);
|
|
18247
19233
|
if (value !== "") {
|
|
18248
19234
|
const body = bodyRef.current;
|
|
18249
|
-
const matches = searchPairs(pairs, value);
|
|
19235
|
+
const matches = searchPairs(pairs, value, search.options);
|
|
18250
19236
|
const anchor = currentSearchPair ?? (body === null ? 0 : pairAtY(body.scrollTop));
|
|
18251
19237
|
const at = matches.findIndex((index) => index >= anchor);
|
|
18252
19238
|
setSearchIndex(at === -1 ? 0 : at);
|
|
@@ -18256,7 +19242,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
18256
19242
|
if (event.key === "Enter") {
|
|
18257
19243
|
event.preventDefault();
|
|
18258
19244
|
goSearch(event.shiftKey ? -1 : 1);
|
|
18259
|
-
}
|
|
19245
|
+
}
|
|
18260
19246
|
}
|
|
18261
19247
|
}),
|
|
18262
19248
|
(0, react_jsx_runtime.jsx)("span", {
|
|
@@ -18265,7 +19251,45 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
18265
19251
|
children: searchMatches.length === 0 ? "0/0" : `${searchIndex % searchMatches.length + 1}/${searchMatches.length}`
|
|
18266
19252
|
}),
|
|
18267
19253
|
(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
18268
|
-
label:
|
|
19254
|
+
label: withChord(t("action.matchCase"), "matchCase"),
|
|
19255
|
+
side: "bottom",
|
|
19256
|
+
delayMs: 500,
|
|
19257
|
+
children: (0, react_jsx_runtime.jsx)("button", {
|
|
19258
|
+
type: "button",
|
|
19259
|
+
className: search.caseSensitive ? `${PendingPanel_module_css_default.searchToggle} ${PendingPanel_module_css_default.searchToggleOn}` : PendingPanel_module_css_default.searchToggle,
|
|
19260
|
+
"data-diff-search-case": true,
|
|
19261
|
+
"data-on": search.caseSensitive ? "" : void 0,
|
|
19262
|
+
"aria-label": t("action.matchCase"),
|
|
19263
|
+
"aria-pressed": search.caseSensitive,
|
|
19264
|
+
onClick: () => {
|
|
19265
|
+
andRefocus(() => {
|
|
19266
|
+
search.toggleCase();
|
|
19267
|
+
});
|
|
19268
|
+
},
|
|
19269
|
+
children: (0, react_jsx_runtime.jsx)(SearchOptionIcon, { kind: "case" })
|
|
19270
|
+
})
|
|
19271
|
+
}),
|
|
19272
|
+
(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
19273
|
+
label: withChord(t("action.matchWholeWord"), "matchWholeWord"),
|
|
19274
|
+
side: "bottom",
|
|
19275
|
+
delayMs: 500,
|
|
19276
|
+
children: (0, react_jsx_runtime.jsx)("button", {
|
|
19277
|
+
type: "button",
|
|
19278
|
+
className: search.wholeWord ? `${PendingPanel_module_css_default.searchToggle} ${PendingPanel_module_css_default.searchToggleOn}` : PendingPanel_module_css_default.searchToggle,
|
|
19279
|
+
"data-diff-search-word": true,
|
|
19280
|
+
"data-on": search.wholeWord ? "" : void 0,
|
|
19281
|
+
"aria-label": t("action.matchWholeWord"),
|
|
19282
|
+
"aria-pressed": search.wholeWord,
|
|
19283
|
+
onClick: () => {
|
|
19284
|
+
andRefocus(() => {
|
|
19285
|
+
search.toggleWord();
|
|
19286
|
+
});
|
|
19287
|
+
},
|
|
19288
|
+
children: (0, react_jsx_runtime.jsx)(SearchOptionIcon, { kind: "word" })
|
|
19289
|
+
})
|
|
19290
|
+
}),
|
|
19291
|
+
(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
19292
|
+
label: withChord(t("action.prevDiff"), "searchPrev"),
|
|
18269
19293
|
side: "bottom",
|
|
18270
19294
|
delayMs: 500,
|
|
18271
19295
|
children: (0, react_jsx_runtime.jsx)("button", {
|
|
@@ -18275,13 +19299,15 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
18275
19299
|
"aria-label": t("action.prevDiff"),
|
|
18276
19300
|
disabled: searchMatches.length === 0,
|
|
18277
19301
|
onClick: () => {
|
|
18278
|
-
|
|
19302
|
+
andRefocus(() => {
|
|
19303
|
+
goSearch(-1);
|
|
19304
|
+
});
|
|
18279
19305
|
},
|
|
18280
19306
|
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronUpOutline14, { size: 14 })
|
|
18281
19307
|
})
|
|
18282
19308
|
}),
|
|
18283
19309
|
(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
18284
|
-
label:
|
|
19310
|
+
label: withChord(t("action.nextDiff"), "searchNext"),
|
|
18285
19311
|
side: "bottom",
|
|
18286
19312
|
delayMs: 500,
|
|
18287
19313
|
children: (0, react_jsx_runtime.jsx)("button", {
|
|
@@ -18291,7 +19317,9 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
18291
19317
|
"aria-label": t("action.nextDiff"),
|
|
18292
19318
|
disabled: searchMatches.length === 0,
|
|
18293
19319
|
onClick: () => {
|
|
18294
|
-
|
|
19320
|
+
andRefocus(() => {
|
|
19321
|
+
goSearch(1);
|
|
19322
|
+
});
|
|
18295
19323
|
},
|
|
18296
19324
|
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, { size: 14 })
|
|
18297
19325
|
})
|
|
@@ -18422,6 +19450,21 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
18422
19450
|
function codeCellAt(node) {
|
|
18423
19451
|
return ((node instanceof Element ? node : node.parentElement)?.closest("[data-diff-row], [data-diff-split-row]"))?.querySelector("[data-diff-code]") ?? null;
|
|
18424
19452
|
}
|
|
19453
|
+
/**
|
|
19454
|
+
* The change block a rendered Markdown-preview node belongs to, or undefined (a
|
|
19455
|
+
* context block, or a node outside any tagged block). The preview tags every
|
|
19456
|
+
* changed run's element with `data-md-block`, carrying the same block index the
|
|
19457
|
+
* source view's `changeBlocksOf` derives over the same contents — so a preview
|
|
19458
|
+
* element and a source block name the same keep/revert target.
|
|
19459
|
+
* @param node - the node under the pointer.
|
|
19460
|
+
* @returns the block index, or undefined.
|
|
19461
|
+
*/
|
|
19462
|
+
function previewBlockAt(node) {
|
|
19463
|
+
const raw = (node instanceof Element ? node : node?.parentElement ?? null)?.closest("[data-md-block]")?.getAttribute("data-md-block");
|
|
19464
|
+
if (raw === null || raw === void 0) return void 0;
|
|
19465
|
+
const index = Number(raw);
|
|
19466
|
+
return Number.isInteger(index) && index >= 0 ? index : void 0;
|
|
19467
|
+
}
|
|
18425
19468
|
/** Character offset of a selection boundary within its line's code text. A
|
|
18426
19469
|
* boundary outside the code cell (the line-number gutter) sits at the line's
|
|
18427
19470
|
* start — offset 0, never the line's end — so it never skips the line. */
|
|
@@ -18590,7 +19633,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
18590
19633
|
});
|
|
18591
19634
|
}
|
|
18592
19635
|
/** The selected file's diff, actions, jump controls, and copy toolbar. */
|
|
18593
|
-
function PendingDiff({ file, busy, workspacePath, jumpSignal, undoFlash, failedMessage, onPasteReference, onToast, t, onKeep, onRevert, onBlockKeep, onBlockRevert, onOpen, onPreviewImage, floatMode, floatOpen, onToggleFileList }) {
|
|
19636
|
+
function PendingDiff({ file, busy, workspacePath, jumpSignal, undoFlash, failedMessage, onPasteReference, onToast, t, onKeep, onRevert, onRefreshVcs, onBlockKeep, onBlockRevert, onOpen, onPreviewImage, floatMode, floatOpen, onToggleFileList }) {
|
|
18594
19637
|
const [langOverride, setLangOverride] = (0, react.useState)(void 0);
|
|
18595
19638
|
const [langMenuOpen, setLangMenuOpen] = (0, react.useState)(false);
|
|
18596
19639
|
const langMenuItems = (0, react.useMemo)(() => [{
|
|
@@ -18601,8 +19644,11 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
18601
19644
|
label: languageDisplayName(language)
|
|
18602
19645
|
}))], [t]);
|
|
18603
19646
|
const detectedLang = (0, react.useMemo)(() => langFromPath(file.path), [file.path]);
|
|
18604
|
-
const
|
|
18605
|
-
const
|
|
19647
|
+
const langSuffix = (0, react.useMemo)(() => suffixOfPath(file.path), [file.path]);
|
|
19648
|
+
const rememberedLang = (0, react.useMemo)(() => langSuffix === void 0 ? void 0 : languageForSuffix(langSuffix), [langSuffix]);
|
|
19649
|
+
const effectiveLang = langOverride ?? rememberedLang;
|
|
19650
|
+
const lang = (0, react.useMemo)(() => effectiveLang ?? detectedLang, [detectedLang, effectiveLang]);
|
|
19651
|
+
const langLabel = effectiveLang === void 0 ? detectedLang === void 0 ? t("action.langAuto") : t("action.langAutoDetected", { lang: languageDisplayName(detectedLang) }) : languageDisplayName(effectiveLang);
|
|
18606
19652
|
const wrapKey = lang ?? "";
|
|
18607
19653
|
const [langWrap, setLangWrap] = (0, react.useState)(() => wrapEnabled(wrapKey));
|
|
18608
19654
|
(0, react.useEffect)(() => {
|
|
@@ -18616,6 +19662,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
18616
19662
|
const [tabWidthSpaces] = (0, react.useState)(() => tabWidth());
|
|
18617
19663
|
const [splitView, setSplitView] = (0, react.useState)(() => splitMode());
|
|
18618
19664
|
const [mdPreview, setMdPreview] = (0, react.useState)(() => mdPreviewEnabled());
|
|
19665
|
+
const previewActive = mdPreview && lang === "markdown";
|
|
18619
19666
|
const mdPreviewBodyRef = (0, react.useRef)(null);
|
|
18620
19667
|
const [mdRulerMarkers, setMdRulerMarkers] = (0, react.useState)([]);
|
|
18621
19668
|
const [mdImageTick, setMdImageTick] = (0, react.useState)(0);
|
|
@@ -18657,12 +19704,12 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
18657
19704
|
]);
|
|
18658
19705
|
const splitPairs = (0, react.useMemo)(() => splitView ? computeSideBySideDiff(model.diff.rows, true).pairs : null, [splitView, model]);
|
|
18659
19706
|
(0, react.useEffect)(() => {
|
|
18660
|
-
if (!
|
|
19707
|
+
if (!previewActive) return;
|
|
18661
19708
|
const body = mdPreviewBodyRef.current;
|
|
18662
19709
|
if (body === null) return;
|
|
18663
19710
|
resolvePreviewImages(body, file.path, workspacePath, (path) => onPreviewImage(file.sessionId, path)).then(() => setMdImageTick((tick) => tick + 1));
|
|
18664
19711
|
}, [
|
|
18665
|
-
|
|
19712
|
+
previewActive,
|
|
18666
19713
|
splitView,
|
|
18667
19714
|
file.path,
|
|
18668
19715
|
file.sessionId,
|
|
@@ -18705,37 +19752,31 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
18705
19752
|
return markers;
|
|
18706
19753
|
}, [model]);
|
|
18707
19754
|
(0, react.useLayoutEffect)(() => {
|
|
18708
|
-
if (!
|
|
19755
|
+
if (!previewActive) return;
|
|
18709
19756
|
const body = mdPreviewBodyRef.current;
|
|
18710
19757
|
if (body === null) return;
|
|
18711
19758
|
const measured = markdownPreviewMarkers(body);
|
|
18712
19759
|
setMdRulerMarkers(measured.length > 0 ? measured : rulerMarkers);
|
|
18713
19760
|
}, [
|
|
18714
|
-
|
|
19761
|
+
previewActive,
|
|
18715
19762
|
splitView,
|
|
18716
19763
|
file.oldText,
|
|
18717
19764
|
file.newText,
|
|
18718
19765
|
mdImageTick,
|
|
18719
19766
|
rulerMarkers
|
|
18720
19767
|
]);
|
|
18721
|
-
const
|
|
18722
|
-
(0, react.
|
|
18723
|
-
|
|
18724
|
-
|
|
18725
|
-
|
|
18726
|
-
|
|
18727
|
-
newRuns: highlightLines(file.newText, lang)
|
|
18728
|
-
});
|
|
18729
|
-
}, 0);
|
|
18730
|
-
return () => {
|
|
18731
|
-
window.clearTimeout(timer);
|
|
18732
|
-
};
|
|
18733
|
-
}, [
|
|
19768
|
+
const oldLines = (0, react.useMemo)(() => file.oldText.split("\n"), [file.oldText]);
|
|
19769
|
+
const newLines = (0, react.useMemo)(() => file.newText.split("\n"), [file.newText]);
|
|
19770
|
+
const highlightKey = (0, react.useMemo)(() => ({
|
|
19771
|
+
file: file.id,
|
|
19772
|
+
lang
|
|
19773
|
+
}), [
|
|
18734
19774
|
file.id,
|
|
19775
|
+
lang,
|
|
18735
19776
|
file.oldText,
|
|
18736
|
-
file.newText
|
|
18737
|
-
lang
|
|
19777
|
+
file.newText
|
|
18738
19778
|
]);
|
|
19779
|
+
const [splitVisible, setSplitVisible] = (0, react.useState)(void 0);
|
|
18739
19780
|
const bodyRef = (0, react.useRef)(null);
|
|
18740
19781
|
const [focus, setFocus] = (0, react.useState)(0);
|
|
18741
19782
|
const [scrollTick, setScrollTick] = (0, react.useState)(0);
|
|
@@ -18745,10 +19786,23 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
18745
19786
|
const [hScrollbarPx, setHScrollbarPx] = (0, react.useState)(0);
|
|
18746
19787
|
const [hoveredBlock, setHoveredBlock] = (0, react.useState)(void 0);
|
|
18747
19788
|
const [selection, setSelection] = (0, react.useState)(void 0);
|
|
19789
|
+
const [previewFrame, setPreviewFrame] = (0, react.useState)(void 0);
|
|
19790
|
+
const [previewCovered, setPreviewCovered] = (0, react.useState)([]);
|
|
19791
|
+
const [previewSelectionFrame, setPreviewSelectionFrame] = (0, react.useState)(void 0);
|
|
19792
|
+
const previewScrollTopRef = (0, react.useRef)(0);
|
|
19793
|
+
const previewHoverFrameRef = (0, react.useRef)(null);
|
|
19794
|
+
const previewSelectionFrameRef = (0, react.useRef)(null);
|
|
19795
|
+
const previewSearchHitsRef = (0, react.useRef)([]);
|
|
19796
|
+
const [previewHitCount, setPreviewHitCount] = (0, react.useState)(0);
|
|
19797
|
+
const previewSearchAnchorRef = (0, react.useRef)(false);
|
|
19798
|
+
const [previewFlash, setPreviewFlash] = (0, react.useState)(void 0);
|
|
19799
|
+
const previewFlashRef = (0, react.useRef)(null);
|
|
19800
|
+
const previewFlashPendingRef = (0, react.useRef)(false);
|
|
18748
19801
|
const selectionTextRef = (0, react.useRef)("");
|
|
18749
19802
|
const [copied, setCopied] = (0, react.useState)(false);
|
|
18750
19803
|
const [searchOpen, setSearchOpen] = (0, react.useState)(false);
|
|
18751
19804
|
const [searchQuery, setSearchQuery] = (0, react.useState)("");
|
|
19805
|
+
const search = useSearchOptions();
|
|
18752
19806
|
const [searchIndex, setSearchIndex] = (0, react.useState)(0);
|
|
18753
19807
|
const searchInputRef = (0, react.useRef)(null);
|
|
18754
19808
|
const [flashKey, setFlashKey] = (0, react.useState)(0);
|
|
@@ -18756,6 +19810,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
18756
19810
|
const bumpFlash = (shake) => {
|
|
18757
19811
|
pinShakeRef.current = shake;
|
|
18758
19812
|
setFlashKey((prev) => prev + 1);
|
|
19813
|
+
previewFlashPendingRef.current = true;
|
|
18759
19814
|
};
|
|
18760
19815
|
(0, react.useEffect)(() => {
|
|
18761
19816
|
setFocus(0);
|
|
@@ -18781,6 +19836,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
18781
19836
|
return model.blocks.map((block) => blockRangesOf(model.diff.rows, block));
|
|
18782
19837
|
}, [model]);
|
|
18783
19838
|
const coveredBlockIndices = (0, react.useMemo)(() => {
|
|
19839
|
+
if (previewActive) return previewCovered;
|
|
18784
19840
|
if (selection === void 0 || splitView) return [];
|
|
18785
19841
|
const covered = [];
|
|
18786
19842
|
for (let index = 0; index < model.blocks.length; index++) {
|
|
@@ -18791,7 +19847,10 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
18791
19847
|
}, [
|
|
18792
19848
|
selection,
|
|
18793
19849
|
splitView,
|
|
18794
|
-
model
|
|
19850
|
+
model,
|
|
19851
|
+
mdPreview,
|
|
19852
|
+
lang,
|
|
19853
|
+
previewCovered
|
|
18795
19854
|
]);
|
|
18796
19855
|
const selectionRange = (0, react.useMemo)(() => {
|
|
18797
19856
|
if (coveredBlockIndices.length === 0) return void 0;
|
|
@@ -18806,10 +19865,324 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
18806
19865
|
end: last.end
|
|
18807
19866
|
});
|
|
18808
19867
|
}, [coveredBlockIndices, model]);
|
|
18809
|
-
const searchMatches = (0, react.useMemo)(() => matchingRows(model.diff.rows, searchQuery), [
|
|
19868
|
+
const searchMatches = (0, react.useMemo)(() => matchingRows(model.diff.rows, searchQuery, search.options), [
|
|
19869
|
+
model,
|
|
19870
|
+
searchQuery,
|
|
19871
|
+
search.options
|
|
19872
|
+
]);
|
|
18810
19873
|
const searchHitSet = (0, react.useMemo)(() => new Set(searchMatches), [searchMatches]);
|
|
18811
19874
|
const currentSearchRow = searchMatches.length === 0 ? void 0 : searchMatches[searchIndex % searchMatches.length];
|
|
19875
|
+
const searchMatchCount = previewActive ? previewHitCount : searchMatches.length;
|
|
19876
|
+
/**
|
|
19877
|
+
* Measure one frame's placement for a set of change blocks in the Markdown
|
|
19878
|
+
* preview. The block's bottom is stored in *content* coordinates (scroll
|
|
19879
|
+
* independent), so the scroll path can move the frame without re-measuring or
|
|
19880
|
+
* re-rendering — the preview's markdown re-render is far too heavy to run per
|
|
19881
|
+
* scroll event.
|
|
19882
|
+
*
|
|
19883
|
+
* - vertically at the block group's bottom edge, clamped into the pane exactly
|
|
19884
|
+
* like the code view (`pane height - frame height`), so a block near or past
|
|
19885
|
+
* the bottom keeps its actions visible at the pane's bottom edge;
|
|
19886
|
+
* - horizontally at the *content's* right edge, because the preview content is
|
|
19887
|
+
* centred under a max width — anchoring to the pane would leave the frame
|
|
19888
|
+
* stranded in the empty margin on a wide panel.
|
|
19889
|
+
* @param indices - the block indices the frame acts on.
|
|
19890
|
+
* @returns the placement, or undefined when nothing is rendered.
|
|
19891
|
+
*/
|
|
19892
|
+
const previewFrameFor = (0, react.useCallback)((indices) => {
|
|
19893
|
+
const body = mdPreviewBodyRef.current;
|
|
19894
|
+
if (body === null || indices.length === 0) return void 0;
|
|
19895
|
+
const bodyRect = body.getBoundingClientRect();
|
|
19896
|
+
let bottom = -Infinity;
|
|
19897
|
+
for (const index of indices) for (const element of body.querySelectorAll(`[data-md-block="${index}"]`)) bottom = Math.max(bottom, element.getBoundingClientRect().bottom);
|
|
19898
|
+
if (!Number.isFinite(bottom)) return void 0;
|
|
19899
|
+
const content = body.querySelector("[data-diff-md-preview-content]");
|
|
19900
|
+
const contentRight = content === null ? bodyRect.right : content.getBoundingClientRect().right;
|
|
19901
|
+
previewScrollTopRef.current = body.scrollTop;
|
|
19902
|
+
return {
|
|
19903
|
+
contentBottom: bottom - bodyRect.top + body.scrollTop,
|
|
19904
|
+
maxTop: Math.max(0, body.clientHeight - BLOCK_ACTIONS_FRAME_PX),
|
|
19905
|
+
right: Math.max(0, bodyRect.right - contentRight) + FRAME_INSET_PX
|
|
19906
|
+
};
|
|
19907
|
+
}, []);
|
|
19908
|
+
/** One placement's viewport top at a given scroll offset, clamped into the pane. */
|
|
19909
|
+
const previewFrameTop = (frame, scrollTop) => frame === void 0 ? void 0 : Math.min(Math.max(0, frame.contentBottom - scrollTop - 2), frame.maxTop);
|
|
19910
|
+
/** Write one placement onto its element right away, so a scroll tracks the pane
|
|
19911
|
+
* in the same frame as the content instead of waiting for a React render. */
|
|
19912
|
+
const applyPreviewFrame = (element, frame) => {
|
|
19913
|
+
if (element === null || frame === void 0) return;
|
|
19914
|
+
const top = previewFrameTop(frame, previewScrollTopRef.current);
|
|
19915
|
+
if (top === void 0) return;
|
|
19916
|
+
element.style.top = `${top}px`;
|
|
19917
|
+
element.style.right = `${frame.right}px`;
|
|
19918
|
+
};
|
|
19919
|
+
/**
|
|
19920
|
+
* Measure the flash box for one preview change block: the union of the block's
|
|
19921
|
+
* rendered elements, in content coordinates. The box is the background-diff
|
|
19922
|
+
* area itself — the code view's outline spans its pane the same way — so
|
|
19923
|
+
* vertically it is the tinted block, and horizontally the widest thing that
|
|
19924
|
+
* tint belongs to: its own edge in the single column, and the *aligned row* in
|
|
19925
|
+
* the double column, which spans both columns even when the change exists on
|
|
19926
|
+
* one side only. The code view derives that from row offsets; rendered
|
|
19927
|
+
* Markdown has no rows, so the elements are measured.
|
|
19928
|
+
* @param index - the change block to outline.
|
|
19929
|
+
* @returns the placement, or undefined when the block is not rendered.
|
|
19930
|
+
*/
|
|
19931
|
+
const previewFlashFor = (index) => {
|
|
19932
|
+
const body = mdPreviewBodyRef.current;
|
|
19933
|
+
if (body === null) return void 0;
|
|
19934
|
+
const bodyRect = body.getBoundingClientRect();
|
|
19935
|
+
let top = Infinity;
|
|
19936
|
+
let bottom = -Infinity;
|
|
19937
|
+
let left = Infinity;
|
|
19938
|
+
let right = -Infinity;
|
|
19939
|
+
for (const element of body.querySelectorAll(`[data-md-block="${index}"]`)) {
|
|
19940
|
+
const rect = element.getBoundingClientRect();
|
|
19941
|
+
top = Math.min(top, rect.top);
|
|
19942
|
+
bottom = Math.max(bottom, rect.bottom);
|
|
19943
|
+
const row = element.closest(".mdDoubleRow");
|
|
19944
|
+
const span = row === null ? rect : row.getBoundingClientRect();
|
|
19945
|
+
left = Math.min(left, span.left);
|
|
19946
|
+
right = Math.max(right, span.right);
|
|
19947
|
+
}
|
|
19948
|
+
if (!Number.isFinite(top) || !Number.isFinite(bottom)) return void 0;
|
|
19949
|
+
return {
|
|
19950
|
+
contentTop: top - bodyRect.top + body.scrollTop,
|
|
19951
|
+
contentBottom: bottom - bodyRect.top + body.scrollTop,
|
|
19952
|
+
left: Math.max(0, left - bodyRect.left),
|
|
19953
|
+
right: Math.max(0, bodyRect.right - right)
|
|
19954
|
+
};
|
|
19955
|
+
};
|
|
19956
|
+
/** One flash's viewport box at a given scroll offset. The block is clamped into
|
|
19957
|
+
* the pane exactly like the code view clamps its rows, so a block taller than
|
|
19958
|
+
* the pane outlines the visible part instead of running past the edge. */
|
|
19959
|
+
const previewFlashBox = (flash, scrollTop) => {
|
|
19960
|
+
if (flash === void 0) return void 0;
|
|
19961
|
+
const paneHeight = mdPreviewBodyRef.current?.clientHeight ?? 0;
|
|
19962
|
+
const top = Math.max(0, flash.contentTop - scrollTop);
|
|
19963
|
+
const bottom = Math.min(paneHeight > 0 ? paneHeight : Number.POSITIVE_INFINITY, flash.contentBottom - scrollTop);
|
|
19964
|
+
return {
|
|
19965
|
+
top,
|
|
19966
|
+
height: Math.max(0, bottom - top)
|
|
19967
|
+
};
|
|
19968
|
+
};
|
|
19969
|
+
/** Write the flash box onto its element right away (the scroll path, so the box
|
|
19970
|
+
* tracks its block for the second it is visible). */
|
|
19971
|
+
const applyPreviewFlash = (element, flash) => {
|
|
19972
|
+
if (element === null || flash === void 0) return;
|
|
19973
|
+
const box = previewFlashBox(flash, previewScrollTopRef.current);
|
|
19974
|
+
if (box === void 0) return;
|
|
19975
|
+
element.style.top = `${box.top}px`;
|
|
19976
|
+
element.style.height = `${box.height}px`;
|
|
19977
|
+
element.style.left = `${flash.left}px`;
|
|
19978
|
+
element.style.right = `${flash.right}px`;
|
|
19979
|
+
};
|
|
19980
|
+
/** Re-place the two action frames at the mirrored scroll offset. */
|
|
19981
|
+
const applyPreviewFrames = () => {
|
|
19982
|
+
applyPreviewFrame(previewHoverFrameRef.current, previewFrame);
|
|
19983
|
+
applyPreviewFrame(previewSelectionFrameRef.current, previewSelectionFrame);
|
|
19984
|
+
};
|
|
19985
|
+
/** Re-place every preview overlay: the frames follow the pointer and the
|
|
19986
|
+
* selection, the flash box the last jump. Only a user scroll uses this — the
|
|
19987
|
+
* landing path re-measures the flash instead, and writing the previous
|
|
19988
|
+
* placement there would stomp the element that React then declines to
|
|
19989
|
+
* rewrite (its style values come out equal, so the write is skipped). */
|
|
19990
|
+
const applyPreviewOverlays = () => {
|
|
19991
|
+
applyPreviewFrames();
|
|
19992
|
+
applyPreviewFlash(previewFlashRef.current, previewFlash);
|
|
19993
|
+
};
|
|
19994
|
+
(0, react.useLayoutEffect)(() => {
|
|
19995
|
+
if (!previewActive) return;
|
|
19996
|
+
setPreviewFrame(hoveredBlock === void 0 ? void 0 : previewFrameFor([hoveredBlock]));
|
|
19997
|
+
}, [
|
|
19998
|
+
mdPreview,
|
|
19999
|
+
lang,
|
|
20000
|
+
hoveredBlock,
|
|
20001
|
+
file.oldText,
|
|
20002
|
+
file.newText,
|
|
20003
|
+
splitView,
|
|
20004
|
+
mdImageTick,
|
|
20005
|
+
previewFrameFor
|
|
20006
|
+
]);
|
|
20007
|
+
(0, react.useLayoutEffect)(() => {
|
|
20008
|
+
if (!previewActive) return;
|
|
20009
|
+
setPreviewSelectionFrame(previewFrameFor(previewCovered));
|
|
20010
|
+
}, [
|
|
20011
|
+
mdPreview,
|
|
20012
|
+
lang,
|
|
20013
|
+
previewCovered,
|
|
20014
|
+
file.oldText,
|
|
20015
|
+
file.newText,
|
|
20016
|
+
splitView,
|
|
20017
|
+
mdImageTick,
|
|
20018
|
+
previewFrameFor
|
|
20019
|
+
]);
|
|
20020
|
+
(0, react.useEffect)(() => {
|
|
20021
|
+
setHoveredBlock(void 0);
|
|
20022
|
+
setPreviewCovered([]);
|
|
20023
|
+
}, [mdPreview, lang]);
|
|
20024
|
+
/** The change blocks a live native selection inside the preview fully covers,
|
|
20025
|
+
* in order — the preview's counterpart of the code view's row-range rule. */
|
|
20026
|
+
const coveredPreviewBlocks = () => {
|
|
20027
|
+
const body = mdPreviewBodyRef.current;
|
|
20028
|
+
const live = window.getSelection();
|
|
20029
|
+
if (body === null || live === null || live.isCollapsed || live.rangeCount === 0) return [];
|
|
20030
|
+
const range = live.getRangeAt(0);
|
|
20031
|
+
if (!body.contains(range.commonAncestorContainer)) return [];
|
|
20032
|
+
const covered = [];
|
|
20033
|
+
for (let index = 0; index < model.blocks.length; index++) {
|
|
20034
|
+
const elements = body.querySelectorAll(`[data-md-block="${index}"]`);
|
|
20035
|
+
if (elements.length === 0) continue;
|
|
20036
|
+
let inside = true;
|
|
20037
|
+
for (const element of elements) {
|
|
20038
|
+
const blockRange = document.createRange();
|
|
20039
|
+
blockRange.selectNode(element);
|
|
20040
|
+
if (range.compareBoundaryPoints(Range.START_TO_START, blockRange) > 0 || range.compareBoundaryPoints(Range.END_TO_END, blockRange) < 0) {
|
|
20041
|
+
inside = false;
|
|
20042
|
+
break;
|
|
20043
|
+
}
|
|
20044
|
+
}
|
|
20045
|
+
if (inside) covered.push(index);
|
|
20046
|
+
}
|
|
20047
|
+
return covered;
|
|
20048
|
+
};
|
|
20049
|
+
/**
|
|
20050
|
+
* Bring one preview block into view. The code view scrolls arithmetically on
|
|
20051
|
+
* row offsets; rendered Markdown has no rows to count, so the block's own
|
|
20052
|
+
* element is measured instead. Its top edge lands `leadRows` code rows below
|
|
20053
|
+
* the pane's top — the same lead the code view leaves — clamped to the scroll
|
|
20054
|
+
* range, so a jump always lands the block in the same place (a bare
|
|
20055
|
+
* `scrollIntoView({ block: 'nearest' })` left it flush against whichever edge
|
|
20056
|
+
* it came from). The settled offset is mirrored into the frames right away: a
|
|
20057
|
+
* programmatic `scrollTop` fires `scroll` no earlier than the next task, and
|
|
20058
|
+
* the frames must not lag the content.
|
|
20059
|
+
* @param index - the change block to bring into view.
|
|
20060
|
+
*/
|
|
20061
|
+
const scrollPreviewBlockIntoView = (index) => {
|
|
20062
|
+
const body = mdPreviewBodyRef.current;
|
|
20063
|
+
if (body === null) return;
|
|
20064
|
+
const element = body.querySelector(`[data-md-block="${index}"]`);
|
|
20065
|
+
if (element === null) return;
|
|
20066
|
+
const elementTop = element.getBoundingClientRect().top - body.getBoundingClientRect().top + body.scrollTop;
|
|
20067
|
+
const maxTop = Math.max(0, body.scrollHeight - body.clientHeight);
|
|
20068
|
+
const target = Math.max(0, Math.min(elementTop - leadRows * ROW_HEIGHT_PX, maxTop));
|
|
20069
|
+
if (body.scrollTop !== target) body.scrollTop = target;
|
|
20070
|
+
previewScrollTopRef.current = body.scrollTop;
|
|
20071
|
+
applyPreviewFrames();
|
|
20072
|
+
};
|
|
20073
|
+
/**
|
|
20074
|
+
* Bring one preview search occurrence into view. The code view's search rule is
|
|
20075
|
+
* reused: a hit that is already fully visible is left alone (a search must not
|
|
20076
|
+
* yank the pane off what the user can see), one above the viewport lands with
|
|
20077
|
+
* the lead rows above it — the block-jump rule — and one below lands at the
|
|
20078
|
+
* bottom edge. The settled offset is mirrored into the frames, as for a jump.
|
|
20079
|
+
* @param hit - the highlighted occurrence to reveal.
|
|
20080
|
+
*/
|
|
20081
|
+
const scrollPreviewHitIntoView = (hit) => {
|
|
20082
|
+
const body = mdPreviewBodyRef.current;
|
|
20083
|
+
if (body === null) return;
|
|
20084
|
+
const bodyRect = body.getBoundingClientRect();
|
|
20085
|
+
const hitTop = hit.getBoundingClientRect().top - bodyRect.top + body.scrollTop;
|
|
20086
|
+
const hitBottom = hit.getBoundingClientRect().bottom - bodyRect.top + body.scrollTop;
|
|
20087
|
+
const viewTop = body.scrollTop;
|
|
20088
|
+
const viewBottom = viewTop + body.clientHeight;
|
|
20089
|
+
let target;
|
|
20090
|
+
if (hitTop - leadRows * ROW_HEIGHT_PX < viewTop) target = hitTop - leadRows * ROW_HEIGHT_PX;
|
|
20091
|
+
else if (hitBottom > viewBottom) target = hitBottom - body.clientHeight;
|
|
20092
|
+
if (target === void 0) return;
|
|
20093
|
+
const maxTop = Math.max(0, body.scrollHeight - body.clientHeight);
|
|
20094
|
+
const clamped = Math.max(0, Math.min(target, maxTop));
|
|
20095
|
+
if (body.scrollTop !== clamped) body.scrollTop = clamped;
|
|
20096
|
+
previewScrollTopRef.current = body.scrollTop;
|
|
20097
|
+
applyPreviewOverlays();
|
|
20098
|
+
};
|
|
20099
|
+
/**
|
|
20100
|
+
* Highlight the query inside the rendered preview. The preview's HTML is one
|
|
20101
|
+
* sanitized string (a flat blob to React) and re-rendering the Markdown per
|
|
20102
|
+
* keystroke is far too heavy, so the matches are wrapped onto the text nodes
|
|
20103
|
+
* that are already there — same matcher, same colors as the code view. The
|
|
20104
|
+
* wrapped elements become the preview's match list, since rendered Markdown
|
|
20105
|
+
* has no rows to map hits onto. A match Markdown split across elements (say
|
|
20106
|
+
* `**bo**ld` for `bold`) is not found: matching what is rendered cannot see
|
|
20107
|
+
* through the markup, which is the honest trade for highlighting the text the
|
|
20108
|
+
* user is actually looking at.
|
|
20109
|
+
*/
|
|
20110
|
+
(0, react.useLayoutEffect)(() => {
|
|
20111
|
+
for (const hit of previewSearchHitsRef.current) {
|
|
20112
|
+
hit.replaceWith(document.createTextNode(hit.textContent ?? ""));
|
|
20113
|
+
hit.parentNode?.normalize();
|
|
20114
|
+
}
|
|
20115
|
+
previewSearchHitsRef.current = [];
|
|
20116
|
+
const body = mdPreviewBodyRef.current;
|
|
20117
|
+
const content = body?.querySelector("[data-diff-md-preview-content]") ?? null;
|
|
20118
|
+
if (!previewActive || content === null || searchQuery === "") {
|
|
20119
|
+
setPreviewHitCount(0);
|
|
20120
|
+
return;
|
|
20121
|
+
}
|
|
20122
|
+
const walker = document.createTreeWalker(content, NodeFilter.SHOW_TEXT);
|
|
20123
|
+
const texts = [];
|
|
20124
|
+
for (let node = walker.nextNode(); node !== null; node = walker.nextNode()) texts.push(node);
|
|
20125
|
+
const hits = [];
|
|
20126
|
+
for (const node of texts) {
|
|
20127
|
+
const parent = node.parentElement;
|
|
20128
|
+
if (parent === null || parent.closest("script,style") !== null) continue;
|
|
20129
|
+
const ranges = matchRangesOf(node.data, searchQuery, search.options);
|
|
20130
|
+
if (ranges.length === 0) continue;
|
|
20131
|
+
const marks = [];
|
|
20132
|
+
for (let index = ranges.length - 1; index >= 0; index--) {
|
|
20133
|
+
const [start, end] = ranges[index];
|
|
20134
|
+
const range = document.createRange();
|
|
20135
|
+
range.setStart(node, start);
|
|
20136
|
+
range.setEnd(node, end);
|
|
20137
|
+
const mark = document.createElement("mark");
|
|
20138
|
+
mark.className = PendingPanel_module_css_default.searchMatch ?? "";
|
|
20139
|
+
mark.setAttribute("data-diff-search-match", "hit");
|
|
20140
|
+
range.surroundContents(mark);
|
|
20141
|
+
marks.push(mark);
|
|
20142
|
+
}
|
|
20143
|
+
hits.push(...marks.reverse());
|
|
20144
|
+
}
|
|
20145
|
+
previewSearchHitsRef.current = hits;
|
|
20146
|
+
setPreviewHitCount(hits.length);
|
|
20147
|
+
if (previewSearchAnchorRef.current) {
|
|
20148
|
+
previewSearchAnchorRef.current = false;
|
|
20149
|
+
const bodyTop = body === null ? 0 : body.getBoundingClientRect().top;
|
|
20150
|
+
const scrollTop = body?.scrollTop ?? 0;
|
|
20151
|
+
const at = hits.findIndex((hit) => hit.getBoundingClientRect().top - bodyTop + scrollTop >= scrollTop - 1);
|
|
20152
|
+
setSearchIndex(at === -1 ? 0 : at);
|
|
20153
|
+
}
|
|
20154
|
+
}, [
|
|
20155
|
+
previewActive,
|
|
20156
|
+
searchQuery,
|
|
20157
|
+
search.options,
|
|
20158
|
+
file.oldText,
|
|
20159
|
+
file.newText,
|
|
20160
|
+
splitView
|
|
20161
|
+
]);
|
|
20162
|
+
(0, react.useEffect)(() => {
|
|
20163
|
+
const hits = previewSearchHitsRef.current;
|
|
20164
|
+
if (!previewActive || hits.length === 0) return;
|
|
20165
|
+
const current = searchIndex % hits.length;
|
|
20166
|
+
hits.forEach((hit, index) => {
|
|
20167
|
+
const isCurrent = index === current;
|
|
20168
|
+
hit.className = (isCurrent ? PendingPanel_module_css_default.searchMatchCurrent : PendingPanel_module_css_default.searchMatch) ?? "";
|
|
20169
|
+
hit.setAttribute("data-diff-search-match", isCurrent ? "current" : "hit");
|
|
20170
|
+
});
|
|
20171
|
+
}, [
|
|
20172
|
+
previewActive,
|
|
20173
|
+
previewHitCount,
|
|
20174
|
+
searchIndex
|
|
20175
|
+
]);
|
|
18812
20176
|
const goSearch = (direction) => {
|
|
20177
|
+
if (previewActive) {
|
|
20178
|
+
cursorPosRef.current = void 0;
|
|
20179
|
+
const hits = previewSearchHitsRef.current;
|
|
20180
|
+
if (hits.length === 0) return;
|
|
20181
|
+
const next = (searchIndex % hits.length + direction + hits.length) % hits.length;
|
|
20182
|
+
setSearchIndex(next);
|
|
20183
|
+
scrollPreviewHitIntoView(hits[next]);
|
|
20184
|
+
return;
|
|
20185
|
+
}
|
|
18813
20186
|
if (searchMatches.length === 0) return;
|
|
18814
20187
|
if (cursorPosRef.current !== void 0) {
|
|
18815
20188
|
setSearchIndex(startIndexFor(searchQuery));
|
|
@@ -18824,7 +20197,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
18824
20197
|
* start (the "no cursor" fallback).
|
|
18825
20198
|
*/
|
|
18826
20199
|
const startIndexFor = (value) => {
|
|
18827
|
-
const matches = value === "" ? [] : matchingRows(model.diff.rows, value);
|
|
20200
|
+
const matches = value === "" ? [] : matchingRows(model.diff.rows, value, search.options);
|
|
18828
20201
|
const pos = cursorPosRef.current;
|
|
18829
20202
|
cursorPosRef.current = void 0;
|
|
18830
20203
|
if (matches.length === 0) return 0;
|
|
@@ -18843,15 +20216,19 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
18843
20216
|
searchInputRef.current?.select();
|
|
18844
20217
|
return;
|
|
18845
20218
|
}
|
|
20219
|
+
search.sync();
|
|
18846
20220
|
const live = window.getSelection();
|
|
18847
|
-
const liveRange = splitView ? splitRowRangeOf(live) : rowRangeOf(live);
|
|
20221
|
+
const liveRange = splitView && !previewActive ? splitRowRangeOf(live) : rowRangeOf(live);
|
|
18848
20222
|
const pos = liveRange !== void 0 ? liveRange : selection;
|
|
18849
20223
|
cursorPosRef.current = pos;
|
|
18850
20224
|
lastRecordedCursorRef.current = pos;
|
|
18851
20225
|
const liveText = (live?.toString() ?? "").trim();
|
|
18852
20226
|
const value = liveText !== "" && !liveText.includes("\n") ? liveText : selectionTextRef.current;
|
|
18853
20227
|
setSearchQuery(value);
|
|
18854
|
-
|
|
20228
|
+
if (previewActive) {
|
|
20229
|
+
previewSearchAnchorRef.current = true;
|
|
20230
|
+
setSearchIndex(0);
|
|
20231
|
+
} else setSearchIndex(startIndexFor(value));
|
|
18855
20232
|
setSearchOpen(true);
|
|
18856
20233
|
requestAnimationFrame(() => {
|
|
18857
20234
|
searchInputRef.current?.focus();
|
|
@@ -18862,7 +20239,18 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
18862
20239
|
openSearchRef.current = openSearchWithSelection;
|
|
18863
20240
|
const searchOpenRef = (0, react.useRef)(searchOpen);
|
|
18864
20241
|
searchOpenRef.current = searchOpen;
|
|
20242
|
+
/** Run one bar control's action, then hand the focus back to the query box:
|
|
20243
|
+
* the bar's chords are scoped to the box (its Esc to the bar), so a mouse
|
|
20244
|
+
* click on a bar button must not leave them dead on the button it landed on. */
|
|
20245
|
+
const andRefocus = (action) => {
|
|
20246
|
+
action();
|
|
20247
|
+
searchInputRef.current?.focus();
|
|
20248
|
+
};
|
|
18865
20249
|
const toggleSearch = () => {
|
|
20250
|
+
if (splitView && !previewActive) {
|
|
20251
|
+
splitDiffRef.current?.toggleSearch();
|
|
20252
|
+
return;
|
|
20253
|
+
}
|
|
18866
20254
|
if (searchOpen) {
|
|
18867
20255
|
cursorPosRef.current = void 0;
|
|
18868
20256
|
lastRecordedCursorRef.current = void 0;
|
|
@@ -18965,6 +20353,28 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
18965
20353
|
end = Math.max(end, selection.end + 1);
|
|
18966
20354
|
}
|
|
18967
20355
|
const visibleRows = rows.slice(start, end);
|
|
20356
|
+
const oldWindow = (0, react.useMemo)(() => windowLineRange(rows, start, end, "old"), [
|
|
20357
|
+
rows,
|
|
20358
|
+
start,
|
|
20359
|
+
end
|
|
20360
|
+
]);
|
|
20361
|
+
const newWindow = (0, react.useMemo)(() => windowLineRange(rows, start, end, "new"), [
|
|
20362
|
+
rows,
|
|
20363
|
+
start,
|
|
20364
|
+
end
|
|
20365
|
+
]);
|
|
20366
|
+
const onSplitVisibleLines = (0, react.useCallback)((visible) => {
|
|
20367
|
+
setSplitVisible(visible);
|
|
20368
|
+
}, []);
|
|
20369
|
+
const runs = useWindowedHighlight({
|
|
20370
|
+
key: highlightKey,
|
|
20371
|
+
oldLines,
|
|
20372
|
+
newLines,
|
|
20373
|
+
lang,
|
|
20374
|
+
oldRange: splitView ? splitVisible?.oldRange : oldWindow,
|
|
20375
|
+
newRange: splitView ? splitVisible?.newRange : newWindow,
|
|
20376
|
+
live: splitView ? splitVisible?.live ?? false : viewportHeight > 0
|
|
20377
|
+
});
|
|
18968
20378
|
const blockEnd = hoveredBlock === void 0 ? void 0 : model.blocks[hoveredBlock]?.end;
|
|
18969
20379
|
const blockActionsTop = blockEnd === void 0 ? 0 : Math.max(0, Math.min(offsetOf(blockEnd + 1) - scrollTop - 2, Math.max(0, viewportHeight - BLOCK_ACTIONS_FRAME_PX)));
|
|
18970
20380
|
const selectionBlockEnd = (() => {
|
|
@@ -18994,7 +20404,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
18994
20404
|
}, [
|
|
18995
20405
|
file.id,
|
|
18996
20406
|
splitView,
|
|
18997
|
-
|
|
20407
|
+
previewActive
|
|
18998
20408
|
]);
|
|
18999
20409
|
(0, react.useEffect)(() => {
|
|
19000
20410
|
const body = bodyRef.current;
|
|
@@ -19012,12 +20422,20 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
19012
20422
|
}, [
|
|
19013
20423
|
file.id,
|
|
19014
20424
|
langWrap,
|
|
19015
|
-
|
|
20425
|
+
previewActive
|
|
19016
20426
|
]);
|
|
19017
20427
|
(0, react.useLayoutEffect)(() => {
|
|
19018
20428
|
if (rowCount === 0) return;
|
|
19019
20429
|
const block = model.blocks[focus];
|
|
19020
20430
|
if (block === void 0) return;
|
|
20431
|
+
if (previewActive) {
|
|
20432
|
+
scrollPreviewBlockIntoView(focus);
|
|
20433
|
+
if (previewFlashPendingRef.current) {
|
|
20434
|
+
previewFlashPendingRef.current = false;
|
|
20435
|
+
setPreviewFlash(previewFlashFor(focus));
|
|
20436
|
+
}
|
|
20437
|
+
return;
|
|
20438
|
+
}
|
|
19021
20439
|
const body = bodyRef.current;
|
|
19022
20440
|
if (body === null) return;
|
|
19023
20441
|
const target = offsetOf(block.start) - leadRows * ROW_HEIGHT_PX;
|
|
@@ -19027,7 +20445,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
19027
20445
|
}, [
|
|
19028
20446
|
scrollTick,
|
|
19029
20447
|
rowOffsets === null,
|
|
19030
|
-
|
|
20448
|
+
previewActive
|
|
19031
20449
|
]);
|
|
19032
20450
|
const wrapArmedRef = (0, react.useRef)(0);
|
|
19033
20451
|
const jump = (direction, wrapGuard = false, singleToast = wrapGuard) => {
|
|
@@ -19047,7 +20465,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
19047
20465
|
wrapArmedRef.current = 0;
|
|
19048
20466
|
} else wrapArmedRef.current = 0;
|
|
19049
20467
|
}
|
|
19050
|
-
|
|
20468
|
+
const targetOf = (current) => {
|
|
19051
20469
|
if (direction === -1) return (current - 1 + count) % count;
|
|
19052
20470
|
const top = bodyRef.current?.scrollTop ?? 0;
|
|
19053
20471
|
for (let index = current + 1; index < count; index++) {
|
|
@@ -19056,12 +20474,14 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
19056
20474
|
if (offsetOf(block.start) >= top) return index;
|
|
19057
20475
|
}
|
|
19058
20476
|
return 0;
|
|
19059
|
-
}
|
|
20477
|
+
};
|
|
20478
|
+
const landed = targetOf(focus);
|
|
20479
|
+
setFocus(landed);
|
|
19060
20480
|
setScrollTick((tick) => tick + 1);
|
|
19061
20481
|
bumpFlash(false);
|
|
19062
20482
|
};
|
|
19063
20483
|
const jumpBlock = (direction, wrapGuard = false, singleToast = wrapGuard) => {
|
|
19064
|
-
if (splitView) {
|
|
20484
|
+
if (splitView && !previewActive) {
|
|
19065
20485
|
splitDiffRef.current?.jump(direction, wrapGuard, singleToast);
|
|
19066
20486
|
return;
|
|
19067
20487
|
}
|
|
@@ -19100,6 +20520,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
19100
20520
|
if (firstCovered === void 0) return;
|
|
19101
20521
|
await runBlockAction(action, selectionRange, firstCovered);
|
|
19102
20522
|
setSelection(void 0);
|
|
20523
|
+
setPreviewCovered([]);
|
|
19103
20524
|
window.getSelection()?.removeAllRanges?.();
|
|
19104
20525
|
};
|
|
19105
20526
|
(0, react.useEffect)(() => {
|
|
@@ -19130,6 +20551,11 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
19130
20551
|
};
|
|
19131
20552
|
(0, react.useEffect)(() => {
|
|
19132
20553
|
const update = () => {
|
|
20554
|
+
if (previewActive) {
|
|
20555
|
+
setPreviewCovered(coveredPreviewBlocks());
|
|
20556
|
+
selectionTextRef.current = "";
|
|
20557
|
+
return;
|
|
20558
|
+
}
|
|
19133
20559
|
const live = window.getSelection();
|
|
19134
20560
|
const range = splitView ? splitRowRangeOf(live) : rowRangeOf(live);
|
|
19135
20561
|
let text = "";
|
|
@@ -19151,7 +20577,12 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
19151
20577
|
return () => {
|
|
19152
20578
|
document.removeEventListener("selectionchange", update);
|
|
19153
20579
|
};
|
|
19154
|
-
}, [
|
|
20580
|
+
}, [
|
|
20581
|
+
file.id,
|
|
20582
|
+
splitView,
|
|
20583
|
+
mdPreview,
|
|
20584
|
+
lang
|
|
20585
|
+
]);
|
|
19155
20586
|
(0, react.useEffect)(() => {
|
|
19156
20587
|
const onCopy = (event) => {
|
|
19157
20588
|
const anchor = window.getSelection()?.anchorNode;
|
|
@@ -19219,8 +20650,9 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
19219
20650
|
(0, react.useEffect)(() => {
|
|
19220
20651
|
const onKeyDown = (event) => {
|
|
19221
20652
|
if (!matchesShortcut(event, keybindingOf("openSearch"))) return;
|
|
20653
|
+
if (pathPickerOpen()) return;
|
|
19222
20654
|
event.preventDefault();
|
|
19223
|
-
if (splitView) {
|
|
20655
|
+
if (splitView && !previewActive) {
|
|
19224
20656
|
splitDiffRef.current?.openSearch();
|
|
19225
20657
|
return;
|
|
19226
20658
|
}
|
|
@@ -19233,15 +20665,16 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
19233
20665
|
}, [searchOpen, splitView]);
|
|
19234
20666
|
(0, react.useEffect)(() => {
|
|
19235
20667
|
const onKeyDown = (event) => {
|
|
20668
|
+
if (!isSearchInputEvent(event)) return;
|
|
19236
20669
|
let direction = 0;
|
|
19237
20670
|
if (matchesShortcut(event, keybindingOf("searchNext"))) direction = 1;
|
|
19238
20671
|
else if (matchesShortcut(event, keybindingOf("searchPrev"))) direction = -1;
|
|
19239
20672
|
if (direction === 0) return;
|
|
19240
|
-
if (splitView) {
|
|
20673
|
+
if (splitView && !previewActive) {
|
|
19241
20674
|
if (splitDiffRef.current?.searchNext(direction)) event.preventDefault();
|
|
19242
20675
|
return;
|
|
19243
20676
|
}
|
|
19244
|
-
if (searchOpen &&
|
|
20677
|
+
if (searchOpen && searchMatchCount > 0) {
|
|
19245
20678
|
event.preventDefault();
|
|
19246
20679
|
goSearch(direction);
|
|
19247
20680
|
}
|
|
@@ -19253,7 +20686,55 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
19253
20686
|
}, [
|
|
19254
20687
|
searchOpen,
|
|
19255
20688
|
searchMatches,
|
|
19256
|
-
|
|
20689
|
+
searchMatchCount,
|
|
20690
|
+
splitView,
|
|
20691
|
+
previewActive
|
|
20692
|
+
]);
|
|
20693
|
+
(0, react.useEffect)(() => {
|
|
20694
|
+
const onKeyDown = (event) => {
|
|
20695
|
+
if (!isSearchInputEvent(event)) return;
|
|
20696
|
+
const option = matchesShortcut(event, keybindingOf("matchCase")) ? "case" : matchesShortcut(event, keybindingOf("matchWholeWord")) ? "word" : void 0;
|
|
20697
|
+
if (option === void 0) return;
|
|
20698
|
+
if (splitView && !previewActive) {
|
|
20699
|
+
const handle = splitDiffRef.current;
|
|
20700
|
+
if (option === "case" ? handle?.toggleMatchCase() === true : handle?.toggleMatchWholeWord() === true) event.preventDefault();
|
|
20701
|
+
return;
|
|
20702
|
+
}
|
|
20703
|
+
if (!searchOpen) return;
|
|
20704
|
+
event.preventDefault();
|
|
20705
|
+
if (option === "case") search.toggleCase();
|
|
20706
|
+
else search.toggleWord();
|
|
20707
|
+
};
|
|
20708
|
+
window.addEventListener("keydown", onKeyDown, true);
|
|
20709
|
+
return () => {
|
|
20710
|
+
window.removeEventListener("keydown", onKeyDown, true);
|
|
20711
|
+
};
|
|
20712
|
+
}, [
|
|
20713
|
+
searchOpen,
|
|
20714
|
+
splitView,
|
|
20715
|
+
previewActive,
|
|
20716
|
+
search
|
|
20717
|
+
]);
|
|
20718
|
+
(0, react.useEffect)(() => {
|
|
20719
|
+
const onKeyDown = (event) => {
|
|
20720
|
+
if (event.key !== "Escape" || !isInPanelEvent(event)) return;
|
|
20721
|
+
if (pathPickerOpen()) return;
|
|
20722
|
+
if (!previewActive && splitView) {
|
|
20723
|
+
if (splitDiffRef.current?.closeSearch() === true) event.preventDefault();
|
|
20724
|
+
return;
|
|
20725
|
+
}
|
|
20726
|
+
if (!searchOpen) return;
|
|
20727
|
+
event.preventDefault();
|
|
20728
|
+
closeSearch();
|
|
20729
|
+
};
|
|
20730
|
+
window.addEventListener("keydown", onKeyDown, true);
|
|
20731
|
+
return () => {
|
|
20732
|
+
window.removeEventListener("keydown", onKeyDown, true);
|
|
20733
|
+
};
|
|
20734
|
+
}, [
|
|
20735
|
+
searchOpen,
|
|
20736
|
+
splitView,
|
|
20737
|
+
previewActive
|
|
19257
20738
|
]);
|
|
19258
20739
|
const jumpRef = (0, react.useRef)(jumpBlock);
|
|
19259
20740
|
jumpRef.current = jumpBlock;
|
|
@@ -19263,8 +20744,8 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
19263
20744
|
if (matchesShortcut(event, keybindingOf("jumpUp"))) direction = -1;
|
|
19264
20745
|
else if (matchesShortcut(event, keybindingOf("jumpDown"))) direction = 1;
|
|
19265
20746
|
if (direction === 0) return;
|
|
19266
|
-
|
|
19267
|
-
if (
|
|
20747
|
+
if (pathPickerOpen()) return;
|
|
20748
|
+
if (isTextFieldEvent(event)) return;
|
|
19268
20749
|
event.preventDefault();
|
|
19269
20750
|
jumpRef.current(direction, true);
|
|
19270
20751
|
};
|
|
@@ -19284,6 +20765,123 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
19284
20765
|
const flashTop = focusedBlock === void 0 ? 0 : Math.max(0, offsetOf(focusedBlock.start) - scrollTop);
|
|
19285
20766
|
const flashBottom = focusedBlock === void 0 ? 0 : Math.min(viewportHeight > 0 ? viewportHeight : Number.POSITIVE_INFINITY, offsetOf(focusedBlock.end + 1) - scrollTop);
|
|
19286
20767
|
const flashHeight = Math.max(0, flashBottom - flashTop);
|
|
20768
|
+
const previewFlashNow = previewActive ? previewFlashBox(previewFlash, previewScrollTopRef.current) : void 0;
|
|
20769
|
+
const searchBar = searchOpen ? (0, react_jsx_runtime.jsxs)("div", {
|
|
20770
|
+
className: PendingPanel_module_css_default.searchBar,
|
|
20771
|
+
"data-diff-searchbar": true,
|
|
20772
|
+
children: [
|
|
20773
|
+
(0, react_jsx_runtime.jsx)("input", {
|
|
20774
|
+
ref: searchInputRef,
|
|
20775
|
+
className: PendingPanel_module_css_default.searchInput,
|
|
20776
|
+
"data-diff-search-input": true,
|
|
20777
|
+
value: searchQuery,
|
|
20778
|
+
placeholder: t("panel.searchPlaceholder"),
|
|
20779
|
+
onChange: (event) => {
|
|
20780
|
+
const value = event.target.value;
|
|
20781
|
+
setSearchQuery(value);
|
|
20782
|
+
if (previewActive) {
|
|
20783
|
+
previewSearchAnchorRef.current = true;
|
|
20784
|
+
setSearchIndex(0);
|
|
20785
|
+
return;
|
|
20786
|
+
}
|
|
20787
|
+
setSearchIndex(startIndexFor(value));
|
|
20788
|
+
},
|
|
20789
|
+
onKeyDown: (event) => {
|
|
20790
|
+
if (event.key === "Enter") {
|
|
20791
|
+
event.preventDefault();
|
|
20792
|
+
goSearch(event.shiftKey ? -1 : 1);
|
|
20793
|
+
}
|
|
20794
|
+
}
|
|
20795
|
+
}),
|
|
20796
|
+
(0, react_jsx_runtime.jsx)("span", {
|
|
20797
|
+
className: PendingPanel_module_css_default.searchCount,
|
|
20798
|
+
"data-diff-search-count": true,
|
|
20799
|
+
children: searchMatchCount === 0 ? "0/0" : `${searchIndex % searchMatchCount + 1}/${searchMatchCount}`
|
|
20800
|
+
}),
|
|
20801
|
+
(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
20802
|
+
label: withChord(t("action.matchCase"), "matchCase"),
|
|
20803
|
+
side: "bottom",
|
|
20804
|
+
delayMs: 500,
|
|
20805
|
+
children: (0, react_jsx_runtime.jsx)("button", {
|
|
20806
|
+
type: "button",
|
|
20807
|
+
className: search.caseSensitive ? `${PendingPanel_module_css_default.searchToggle} ${PendingPanel_module_css_default.searchToggleOn}` : PendingPanel_module_css_default.searchToggle,
|
|
20808
|
+
"data-diff-search-case": true,
|
|
20809
|
+
"data-on": search.caseSensitive ? "" : void 0,
|
|
20810
|
+
"aria-label": t("action.matchCase"),
|
|
20811
|
+
"aria-pressed": search.caseSensitive,
|
|
20812
|
+
onClick: () => {
|
|
20813
|
+
andRefocus(() => {
|
|
20814
|
+
search.toggleCase();
|
|
20815
|
+
});
|
|
20816
|
+
},
|
|
20817
|
+
children: (0, react_jsx_runtime.jsx)(SearchOptionIcon, { kind: "case" })
|
|
20818
|
+
})
|
|
20819
|
+
}),
|
|
20820
|
+
(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
20821
|
+
label: withChord(t("action.matchWholeWord"), "matchWholeWord"),
|
|
20822
|
+
side: "bottom",
|
|
20823
|
+
delayMs: 500,
|
|
20824
|
+
children: (0, react_jsx_runtime.jsx)("button", {
|
|
20825
|
+
type: "button",
|
|
20826
|
+
className: search.wholeWord ? `${PendingPanel_module_css_default.searchToggle} ${PendingPanel_module_css_default.searchToggleOn}` : PendingPanel_module_css_default.searchToggle,
|
|
20827
|
+
"data-diff-search-word": true,
|
|
20828
|
+
"data-on": search.wholeWord ? "" : void 0,
|
|
20829
|
+
"aria-label": t("action.matchWholeWord"),
|
|
20830
|
+
"aria-pressed": search.wholeWord,
|
|
20831
|
+
onClick: () => {
|
|
20832
|
+
andRefocus(() => {
|
|
20833
|
+
search.toggleWord();
|
|
20834
|
+
});
|
|
20835
|
+
},
|
|
20836
|
+
children: (0, react_jsx_runtime.jsx)(SearchOptionIcon, { kind: "word" })
|
|
20837
|
+
})
|
|
20838
|
+
}),
|
|
20839
|
+
(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
20840
|
+
label: withChord(t("action.prevDiff"), "searchPrev"),
|
|
20841
|
+
side: "bottom",
|
|
20842
|
+
delayMs: 500,
|
|
20843
|
+
children: (0, react_jsx_runtime.jsx)("button", {
|
|
20844
|
+
type: "button",
|
|
20845
|
+
className: `${PendingPanel_module_css_default.action} ${PendingPanel_module_css_default.iconAction}`,
|
|
20846
|
+
"data-diff-search-prev": true,
|
|
20847
|
+
"aria-label": t("action.prevDiff"),
|
|
20848
|
+
disabled: searchMatchCount === 0,
|
|
20849
|
+
onClick: () => {
|
|
20850
|
+
andRefocus(() => {
|
|
20851
|
+
goSearch(-1);
|
|
20852
|
+
});
|
|
20853
|
+
},
|
|
20854
|
+
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronUpOutline14, { size: 14 })
|
|
20855
|
+
})
|
|
20856
|
+
}),
|
|
20857
|
+
(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
20858
|
+
label: withChord(t("action.nextDiff"), "searchNext"),
|
|
20859
|
+
side: "bottom",
|
|
20860
|
+
delayMs: 500,
|
|
20861
|
+
children: (0, react_jsx_runtime.jsx)("button", {
|
|
20862
|
+
type: "button",
|
|
20863
|
+
className: `${PendingPanel_module_css_default.action} ${PendingPanel_module_css_default.iconAction}`,
|
|
20864
|
+
"data-diff-search-next": true,
|
|
20865
|
+
"aria-label": t("action.nextDiff"),
|
|
20866
|
+
disabled: searchMatchCount === 0,
|
|
20867
|
+
onClick: () => {
|
|
20868
|
+
andRefocus(() => {
|
|
20869
|
+
goSearch(1);
|
|
20870
|
+
});
|
|
20871
|
+
},
|
|
20872
|
+
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, { size: 14 })
|
|
20873
|
+
})
|
|
20874
|
+
}),
|
|
20875
|
+
(0, react_jsx_runtime.jsx)("button", {
|
|
20876
|
+
type: "button",
|
|
20877
|
+
className: `${PendingPanel_module_css_default.action} ${PendingPanel_module_css_default.iconAction}`,
|
|
20878
|
+
"data-diff-search-close": true,
|
|
20879
|
+
"aria-label": t("action.close"),
|
|
20880
|
+
onClick: closeSearch,
|
|
20881
|
+
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCloseOutline16, { size: 14 })
|
|
20882
|
+
})
|
|
20883
|
+
]
|
|
20884
|
+
}) : null;
|
|
19287
20885
|
return (0, react_jsx_runtime.jsxs)("div", {
|
|
19288
20886
|
className: PendingPanel_module_css_default.diff,
|
|
19289
20887
|
"data-diff-approval-diff": true,
|
|
@@ -19352,7 +20950,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
19352
20950
|
})
|
|
19353
20951
|
}),
|
|
19354
20952
|
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, {
|
|
19355
|
-
label:
|
|
20953
|
+
label: withChord(t("action.prevDiff"), "jumpUp"),
|
|
19356
20954
|
side: "bottom",
|
|
19357
20955
|
delayMs: 500,
|
|
19358
20956
|
children: (0, react_jsx_runtime.jsx)("button", {
|
|
@@ -19367,7 +20965,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
19367
20965
|
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronUpOutline14, { size: 14 })
|
|
19368
20966
|
})
|
|
19369
20967
|
}), (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
19370
|
-
label:
|
|
20968
|
+
label: withChord(t("action.nextDiff"), "jumpDown"),
|
|
19371
20969
|
side: "bottom",
|
|
19372
20970
|
delayMs: 500,
|
|
19373
20971
|
children: (0, react_jsx_runtime.jsx)("button", {
|
|
@@ -19383,7 +20981,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
19383
20981
|
})
|
|
19384
20982
|
})] }),
|
|
19385
20983
|
(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
19386
|
-
label: t("action.search"),
|
|
20984
|
+
label: withChord(t("action.search"), "openSearch"),
|
|
19387
20985
|
side: "bottom",
|
|
19388
20986
|
delayMs: 500,
|
|
19389
20987
|
children: (0, react_jsx_runtime.jsx)("button", {
|
|
@@ -19426,6 +21024,22 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
19426
21024
|
children: (0, react_jsx_runtime.jsx)(MarkdownModeIcon, { preview: mdPreview })
|
|
19427
21025
|
})
|
|
19428
21026
|
}),
|
|
21027
|
+
(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
21028
|
+
label: t("action.refreshVcs"),
|
|
21029
|
+
side: "bottom",
|
|
21030
|
+
delayMs: 500,
|
|
21031
|
+
children: (0, react_jsx_runtime.jsx)("button", {
|
|
21032
|
+
type: "button",
|
|
21033
|
+
className: `${PendingPanel_module_css_default.action} ${PendingPanel_module_css_default.iconAction}`,
|
|
21034
|
+
"data-diff-refresh-vcs": true,
|
|
21035
|
+
disabled: busy,
|
|
21036
|
+
"aria-label": t("action.refreshVcs"),
|
|
21037
|
+
onClick: () => {
|
|
21038
|
+
onRefreshVcs(file);
|
|
21039
|
+
},
|
|
21040
|
+
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconRefreshOutline16, { size: 14 })
|
|
21041
|
+
})
|
|
21042
|
+
}),
|
|
19429
21043
|
(0, react_jsx_runtime.jsx)("span", { className: PendingPanel_module_css_default.flexSpacer }),
|
|
19430
21044
|
(0, react_jsx_runtime.jsx)("button", {
|
|
19431
21045
|
type: "button",
|
|
@@ -19458,7 +21072,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
19458
21072
|
className: PendingPanel_module_css_default.missingHint,
|
|
19459
21073
|
children: t("panel.missingHint")
|
|
19460
21074
|
}),
|
|
19461
|
-
|
|
21075
|
+
previewActive ? (0, react_jsx_runtime.jsx)(MarkdownPreviewBoundary, {
|
|
19462
21076
|
fallback: (0, react_jsx_runtime.jsx)("div", {
|
|
19463
21077
|
className: PendingPanel_module_css_default.mdPreviewFallback,
|
|
19464
21078
|
"data-diff-md-preview-fallback": true,
|
|
@@ -19466,29 +21080,146 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
19466
21080
|
}),
|
|
19467
21081
|
children: (0, react_jsx_runtime.jsxs)("div", {
|
|
19468
21082
|
className: PendingPanel_module_css_default.mdPreviewWrap,
|
|
19469
|
-
|
|
19470
|
-
|
|
19471
|
-
|
|
19472
|
-
|
|
19473
|
-
|
|
19474
|
-
|
|
19475
|
-
|
|
19476
|
-
|
|
19477
|
-
|
|
19478
|
-
|
|
19479
|
-
|
|
19480
|
-
|
|
19481
|
-
|
|
19482
|
-
|
|
19483
|
-
|
|
19484
|
-
|
|
19485
|
-
|
|
21083
|
+
onMouseLeave: () => {
|
|
21084
|
+
setHoveredBlock(void 0);
|
|
21085
|
+
},
|
|
21086
|
+
children: [
|
|
21087
|
+
(0, react_jsx_runtime.jsx)("div", {
|
|
21088
|
+
className: `${PendingPanel_module_css_default.mdPreviewBody} ${splitView ? PendingPanel_module_css_default.mdPreviewDouble : PendingPanel_module_css_default.mdPreviewSingle}`,
|
|
21089
|
+
"data-diff-md-preview-body": true,
|
|
21090
|
+
"data-diff-md-mode": splitView ? "double" : "single",
|
|
21091
|
+
ref: mdPreviewBodyRef,
|
|
21092
|
+
onScroll: (event) => {
|
|
21093
|
+
previewScrollTopRef.current = event.currentTarget.scrollTop;
|
|
21094
|
+
applyPreviewOverlays();
|
|
21095
|
+
},
|
|
21096
|
+
onMouseOver: (event) => {
|
|
21097
|
+
const target = event.target;
|
|
21098
|
+
if (target instanceof Element && target.closest("[data-diff-block-actions],[data-diff-selection-actions]") !== null) return;
|
|
21099
|
+
const index = previewBlockAt(target);
|
|
21100
|
+
if (index !== hoveredBlock) setHoveredBlock(index);
|
|
21101
|
+
},
|
|
21102
|
+
children: (0, react_jsx_runtime.jsx)("div", {
|
|
21103
|
+
className: PendingPanel_module_css_default.mdPreviewContent,
|
|
21104
|
+
"data-diff-md-preview-content": true,
|
|
21105
|
+
style: { maxWidth: splitView ? mdPreviewMaxWidthPx * 2 : mdPreviewMaxWidthPx },
|
|
21106
|
+
dangerouslySetInnerHTML: { __html: renderMarkdownPreview(file.oldText, file.newText, splitView ? "double" : "single") }
|
|
21107
|
+
})
|
|
21108
|
+
}),
|
|
21109
|
+
searchBar,
|
|
21110
|
+
previewFlash !== void 0 && previewFlashNow !== void 0 && (0, react_jsx_runtime.jsx)("div", {
|
|
21111
|
+
ref: previewFlashRef,
|
|
21112
|
+
className: pinShakeRef.current ? `${PendingPanel_module_css_default.blockFlash} ${PendingPanel_module_css_default.blockFlashShake}` : PendingPanel_module_css_default.blockFlash,
|
|
21113
|
+
"data-diff-block-flash": true,
|
|
19486
21114
|
style: {
|
|
19487
|
-
top:
|
|
19488
|
-
height:
|
|
21115
|
+
top: previewFlashNow.top,
|
|
21116
|
+
height: previewFlashNow.height,
|
|
21117
|
+
left: previewFlash.left,
|
|
21118
|
+
right: previewFlash.right
|
|
19489
21119
|
}
|
|
19490
|
-
},
|
|
19491
|
-
|
|
21120
|
+
}, flashKey),
|
|
21121
|
+
previewCovered.length > 0 && selectionRange !== void 0 && previewSelectionFrame !== void 0 ? (0, react_jsx_runtime.jsxs)("div", {
|
|
21122
|
+
ref: previewSelectionFrameRef,
|
|
21123
|
+
className: PendingPanel_module_css_default.blockActions,
|
|
21124
|
+
"data-diff-selection-actions": true,
|
|
21125
|
+
style: {
|
|
21126
|
+
top: previewFrameTop(previewSelectionFrame, previewScrollTopRef.current) ?? 0,
|
|
21127
|
+
right: previewSelectionFrame.right
|
|
21128
|
+
},
|
|
21129
|
+
children: [(0, react_jsx_runtime.jsx)("button", {
|
|
21130
|
+
type: "button",
|
|
21131
|
+
className: `${PendingPanel_module_css_default.action} ${PendingPanel_module_css_default.actionPrimary}`,
|
|
21132
|
+
"data-diff-selection-keep": true,
|
|
21133
|
+
disabled: busy,
|
|
21134
|
+
onClick: () => {
|
|
21135
|
+
handleSelectionAction("keep");
|
|
21136
|
+
},
|
|
21137
|
+
children: t("action.keep")
|
|
21138
|
+
}), (0, react_jsx_runtime.jsx)("button", {
|
|
21139
|
+
type: "button",
|
|
21140
|
+
className: PendingPanel_module_css_default.action,
|
|
21141
|
+
"data-diff-selection-revert": true,
|
|
21142
|
+
disabled: busy,
|
|
21143
|
+
onClick: () => {
|
|
21144
|
+
handleSelectionAction("revert");
|
|
21145
|
+
},
|
|
21146
|
+
children: t("action.revert")
|
|
21147
|
+
})]
|
|
21148
|
+
}) : hoveredBlock !== void 0 && model.blocks[hoveredBlock] !== void 0 && previewFrame !== void 0 ? (0, react_jsx_runtime.jsxs)("div", {
|
|
21149
|
+
ref: previewHoverFrameRef,
|
|
21150
|
+
className: PendingPanel_module_css_default.blockActions,
|
|
21151
|
+
"data-diff-block-actions": true,
|
|
21152
|
+
style: {
|
|
21153
|
+
top: previewFrameTop(previewFrame, previewScrollTopRef.current) ?? 0,
|
|
21154
|
+
right: previewFrame.right
|
|
21155
|
+
},
|
|
21156
|
+
children: [
|
|
21157
|
+
(0, react_jsx_runtime.jsx)("span", {
|
|
21158
|
+
className: PendingPanel_module_css_default.blockPosition,
|
|
21159
|
+
"data-diff-block-position": true,
|
|
21160
|
+
children: t("panel.blockPosition", {
|
|
21161
|
+
current: hoveredBlock + 1,
|
|
21162
|
+
total: model.blocks.length
|
|
21163
|
+
})
|
|
21164
|
+
}),
|
|
21165
|
+
(0, react_jsx_runtime.jsx)("button", {
|
|
21166
|
+
type: "button",
|
|
21167
|
+
className: `${PendingPanel_module_css_default.action} ${PendingPanel_module_css_default.iconAction}`,
|
|
21168
|
+
"data-diff-block-prev": true,
|
|
21169
|
+
"aria-label": t("action.prevDiff"),
|
|
21170
|
+
disabled: busy,
|
|
21171
|
+
onClick: () => {
|
|
21172
|
+
stepBlock(-1);
|
|
21173
|
+
},
|
|
21174
|
+
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronUpOutline14, { size: 14 })
|
|
21175
|
+
}),
|
|
21176
|
+
(0, react_jsx_runtime.jsx)("button", {
|
|
21177
|
+
type: "button",
|
|
21178
|
+
className: `${PendingPanel_module_css_default.action} ${PendingPanel_module_css_default.iconAction}`,
|
|
21179
|
+
"data-diff-block-next": true,
|
|
21180
|
+
"aria-label": t("action.nextDiff"),
|
|
21181
|
+
disabled: busy,
|
|
21182
|
+
onClick: () => {
|
|
21183
|
+
stepBlock(1);
|
|
21184
|
+
},
|
|
21185
|
+
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, { size: 14 })
|
|
21186
|
+
}),
|
|
21187
|
+
(0, react_jsx_runtime.jsx)("button", {
|
|
21188
|
+
type: "button",
|
|
21189
|
+
className: `${PendingPanel_module_css_default.action} ${PendingPanel_module_css_default.actionPrimary}`,
|
|
21190
|
+
"data-diff-block-keep": true,
|
|
21191
|
+
disabled: busy,
|
|
21192
|
+
onClick: () => {
|
|
21193
|
+
handleBlockAction("keep");
|
|
21194
|
+
},
|
|
21195
|
+
children: t("action.keep")
|
|
21196
|
+
}),
|
|
21197
|
+
(0, react_jsx_runtime.jsx)("button", {
|
|
21198
|
+
type: "button",
|
|
21199
|
+
className: PendingPanel_module_css_default.action,
|
|
21200
|
+
"data-diff-block-revert": true,
|
|
21201
|
+
disabled: busy,
|
|
21202
|
+
onClick: () => {
|
|
21203
|
+
handleBlockAction("revert");
|
|
21204
|
+
},
|
|
21205
|
+
children: t("action.revert")
|
|
21206
|
+
})
|
|
21207
|
+
]
|
|
21208
|
+
}) : null,
|
|
21209
|
+
mdRulerMarkers.length > 0 && (0, react_jsx_runtime.jsx)("div", {
|
|
21210
|
+
className: PendingPanel_module_css_default.overviewRuler,
|
|
21211
|
+
"data-diff-approval-ruler": true,
|
|
21212
|
+
"aria-hidden": "true",
|
|
21213
|
+
children: mdRulerMarkers.map((marker, index) => (0, react_jsx_runtime.jsx)("div", {
|
|
21214
|
+
className: `${PendingPanel_module_css_default.overviewMarker} ${marker.kind === "del" ? PendingPanel_module_css_default.markerDel : PendingPanel_module_css_default.markerAdd}`,
|
|
21215
|
+
"data-diff-ruler-marker": marker.kind,
|
|
21216
|
+
style: {
|
|
21217
|
+
top: `${marker.top}%`,
|
|
21218
|
+
height: `${marker.height}%`
|
|
21219
|
+
}
|
|
21220
|
+
}, index))
|
|
21221
|
+
})
|
|
21222
|
+
]
|
|
19492
21223
|
})
|
|
19493
21224
|
}) : splitView ? (0, react_jsx_runtime.jsx)(SplitDiff, {
|
|
19494
21225
|
ref: splitDiffRef,
|
|
@@ -19503,7 +21234,8 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
19503
21234
|
leadRows,
|
|
19504
21235
|
onBlockKeep,
|
|
19505
21236
|
onBlockRevert,
|
|
19506
|
-
onWrapToast: (text) => onToast(text)
|
|
21237
|
+
onWrapToast: (text) => onToast(text),
|
|
21238
|
+
onVisibleLines: onSplitVisibleLines
|
|
19507
21239
|
}) : (0, react_jsx_runtime.jsxs)("div", {
|
|
19508
21240
|
className: PendingPanel_module_css_default.diffBodyWrap,
|
|
19509
21241
|
onMouseLeave: () => {
|
|
@@ -19539,6 +21271,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
19539
21271
|
searchHit: searchHitSet.has(index),
|
|
19540
21272
|
searchCurrent: index === currentSearchRow,
|
|
19541
21273
|
searchQuery,
|
|
21274
|
+
searchOptions: search.options,
|
|
19542
21275
|
onRowHover,
|
|
19543
21276
|
wrappedLines: rowWrapped?.[index]
|
|
19544
21277
|
}, index);
|
|
@@ -19640,75 +21373,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
19640
21373
|
width: flashWidth
|
|
19641
21374
|
}
|
|
19642
21375
|
}, flashKey),
|
|
19643
|
-
|
|
19644
|
-
className: PendingPanel_module_css_default.searchBar,
|
|
19645
|
-
"data-diff-searchbar": true,
|
|
19646
|
-
children: [
|
|
19647
|
-
(0, react_jsx_runtime.jsx)("input", {
|
|
19648
|
-
ref: searchInputRef,
|
|
19649
|
-
className: PendingPanel_module_css_default.searchInput,
|
|
19650
|
-
"data-diff-search-input": true,
|
|
19651
|
-
value: searchQuery,
|
|
19652
|
-
placeholder: t("panel.searchPlaceholder"),
|
|
19653
|
-
onChange: (event) => {
|
|
19654
|
-
const value = event.target.value;
|
|
19655
|
-
setSearchQuery(value);
|
|
19656
|
-
setSearchIndex(startIndexFor(value));
|
|
19657
|
-
},
|
|
19658
|
-
onKeyDown: (event) => {
|
|
19659
|
-
if (event.key === "Enter") {
|
|
19660
|
-
event.preventDefault();
|
|
19661
|
-
goSearch(event.shiftKey ? -1 : 1);
|
|
19662
|
-
} else if (event.key === "Escape") closeSearch();
|
|
19663
|
-
}
|
|
19664
|
-
}),
|
|
19665
|
-
(0, react_jsx_runtime.jsx)("span", {
|
|
19666
|
-
className: PendingPanel_module_css_default.searchCount,
|
|
19667
|
-
"data-diff-search-count": true,
|
|
19668
|
-
children: searchMatches.length === 0 ? "0/0" : `${searchIndex % searchMatches.length + 1}/${searchMatches.length}`
|
|
19669
|
-
}),
|
|
19670
|
-
(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
19671
|
-
label: `${t("action.prevDiff")} (Shift+F3)`,
|
|
19672
|
-
side: "bottom",
|
|
19673
|
-
delayMs: 500,
|
|
19674
|
-
children: (0, react_jsx_runtime.jsx)("button", {
|
|
19675
|
-
type: "button",
|
|
19676
|
-
className: `${PendingPanel_module_css_default.action} ${PendingPanel_module_css_default.iconAction}`,
|
|
19677
|
-
"data-diff-search-prev": true,
|
|
19678
|
-
"aria-label": t("action.prevDiff"),
|
|
19679
|
-
disabled: searchMatches.length === 0,
|
|
19680
|
-
onClick: () => {
|
|
19681
|
-
goSearch(-1);
|
|
19682
|
-
},
|
|
19683
|
-
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronUpOutline14, { size: 14 })
|
|
19684
|
-
})
|
|
19685
|
-
}),
|
|
19686
|
-
(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
19687
|
-
label: `${t("action.nextDiff")} (F3)`,
|
|
19688
|
-
side: "bottom",
|
|
19689
|
-
delayMs: 500,
|
|
19690
|
-
children: (0, react_jsx_runtime.jsx)("button", {
|
|
19691
|
-
type: "button",
|
|
19692
|
-
className: `${PendingPanel_module_css_default.action} ${PendingPanel_module_css_default.iconAction}`,
|
|
19693
|
-
"data-diff-search-next": true,
|
|
19694
|
-
"aria-label": t("action.nextDiff"),
|
|
19695
|
-
disabled: searchMatches.length === 0,
|
|
19696
|
-
onClick: () => {
|
|
19697
|
-
goSearch(1);
|
|
19698
|
-
},
|
|
19699
|
-
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, { size: 14 })
|
|
19700
|
-
})
|
|
19701
|
-
}),
|
|
19702
|
-
(0, react_jsx_runtime.jsx)("button", {
|
|
19703
|
-
type: "button",
|
|
19704
|
-
className: `${PendingPanel_module_css_default.action} ${PendingPanel_module_css_default.iconAction}`,
|
|
19705
|
-
"data-diff-search-close": true,
|
|
19706
|
-
"aria-label": t("action.close"),
|
|
19707
|
-
onClick: closeSearch,
|
|
19708
|
-
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCloseOutline16, { size: 14 })
|
|
19709
|
-
})
|
|
19710
|
-
]
|
|
19711
|
-
}),
|
|
21376
|
+
searchBar,
|
|
19712
21377
|
rulerMarkers.length > 0 && (0, react_jsx_runtime.jsx)("div", {
|
|
19713
21378
|
className: PendingPanel_module_css_default.overviewRuler,
|
|
19714
21379
|
"data-diff-approval-ruler": true,
|
|
@@ -19730,7 +21395,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
19730
21395
|
"data-diff-status-bar": true,
|
|
19731
21396
|
children: [
|
|
19732
21397
|
selectionReference === void 0 ? null : (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
19733
|
-
label: copied ? t("action.copied") :
|
|
21398
|
+
label: copied ? t("action.copied") : withChord(t("action.copyHint"), "copyRef"),
|
|
19734
21399
|
side: "top",
|
|
19735
21400
|
delayMs: 300,
|
|
19736
21401
|
children: (0, react_jsx_runtime.jsx)("span", {
|
|
@@ -19755,15 +21420,17 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
19755
21420
|
})
|
|
19756
21421
|
}),
|
|
19757
21422
|
(0, react_jsx_runtime.jsx)("span", { className: PendingPanel_module_css_default.flexSpacer }),
|
|
19758
|
-
!
|
|
21423
|
+
!previewActive && (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Menu, {
|
|
19759
21424
|
open: langMenuOpen,
|
|
19760
21425
|
portal: true,
|
|
19761
21426
|
compact: true,
|
|
19762
21427
|
align: "end",
|
|
19763
21428
|
items: langMenuItems,
|
|
19764
|
-
selectedId:
|
|
21429
|
+
selectedId: effectiveLang ?? "",
|
|
19765
21430
|
onSelect: (id) => {
|
|
19766
|
-
|
|
21431
|
+
const next = id === "" ? void 0 : id;
|
|
21432
|
+
setLangOverride(next);
|
|
21433
|
+
if (langSuffix !== void 0) setLanguageForSuffix(langSuffix, next ?? null);
|
|
19767
21434
|
setLangMenuOpen(false);
|
|
19768
21435
|
},
|
|
19769
21436
|
onClose: () => {
|
|
@@ -19810,7 +21477,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
19810
21477
|
});
|
|
19811
21478
|
}
|
|
19812
21479
|
/** Render the pending-edit review panel and its unified footer action. */
|
|
19813
|
-
function PendingPanel({ wide, useSessions, usePending, onRefresh, onKeep, onRevert, onBlockKeep, onBlockRevert, onOpen, onPreviewImage, onPasteReference, onUndo, onRedo, onImportVcs, onKeepAll, onRevertAll, onAckRedoCleared, collapseSidebar, t }) {
|
|
21480
|
+
function PendingPanel({ wide, useSessions, usePending, onRefresh, onKeep, onRevert, onBlockKeep, onBlockRevert, onOpen, onPreviewImage, onPasteReference, onUndo, onRedo, onImportVcs, onRefreshVcs, onBrowse, onAddPath, onKeepAll, onRevertAll, onAckRedoCleared, collapseSidebar, t }) {
|
|
19814
21481
|
const current = useSessions((state) => state.current);
|
|
19815
21482
|
const currentBlank = useSessions((state) => {
|
|
19816
21483
|
const id = state.current;
|
|
@@ -19872,6 +21539,10 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
19872
21539
|
/** A last-block keep/revert awaiting the user's remove-or-keep choice; the
|
|
19873
21540
|
* choice rides the same block RPC as its `removeWhenResolved` flag. */
|
|
19874
21541
|
const [blockPrompt, setBlockPrompt] = (0, react.useState)(null);
|
|
21542
|
+
const [filePrompt, setFilePrompt] = (0, react.useState)(null);
|
|
21543
|
+
/** Whether the add-path dialog is open. One dialog covers both shapes: what
|
|
21544
|
+
* the browser settles on decides whether a file or a directory is added. */
|
|
21545
|
+
const [addOpen, setAddOpen] = (0, react.useState)(false);
|
|
19875
21546
|
/** Bottom offset tracking the chat composer's top edge so the input stays visible. */
|
|
19876
21547
|
const [bottomPx, setBottomPx] = (0, react.useState)(FALLBACK_BOTTOM_PX);
|
|
19877
21548
|
/** Fullscreen expanded: the panel bottom pins to the window edge, ignoring the composer offset. */
|
|
@@ -20036,6 +21707,28 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
20036
21707
|
}
|
|
20037
21708
|
return removeWhenResolved === void 0 ? onBlockRevert(sessionId, id, block) : onBlockRevert(sessionId, id, block, removeWhenResolved);
|
|
20038
21709
|
};
|
|
21710
|
+
const keepWithPrompt = (sessionId, id, keepListed) => {
|
|
21711
|
+
if (keepListed === void 0 && confirmFileRemoveEnabled()) {
|
|
21712
|
+
setFilePrompt({
|
|
21713
|
+
action: "keep",
|
|
21714
|
+
sessionId,
|
|
21715
|
+
id
|
|
21716
|
+
});
|
|
21717
|
+
return Promise.resolve();
|
|
21718
|
+
}
|
|
21719
|
+
return keepListed === void 0 ? onKeep(sessionId, id) : onKeep(sessionId, id, keepListed);
|
|
21720
|
+
};
|
|
21721
|
+
const revertWithPrompt = (sessionId, id, keepListed) => {
|
|
21722
|
+
if (keepListed === void 0 && confirmFileRemoveEnabled()) {
|
|
21723
|
+
setFilePrompt({
|
|
21724
|
+
action: "revert",
|
|
21725
|
+
sessionId,
|
|
21726
|
+
id
|
|
21727
|
+
});
|
|
21728
|
+
return Promise.resolve();
|
|
21729
|
+
}
|
|
21730
|
+
return keepListed === void 0 ? onRevert(sessionId, id) : onRevert(sessionId, id, keepListed);
|
|
21731
|
+
};
|
|
20039
21732
|
/** Per-file keep/revert failures, surfaced inline on the row and detail. */
|
|
20040
21733
|
const failed = snapshot.failed ?? EMPTY_FAILED_MAP;
|
|
20041
21734
|
const failedInitialized = (0, react.useRef)(false);
|
|
@@ -20088,6 +21781,12 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
20088
21781
|
(0, react.useEffect)(() => {
|
|
20089
21782
|
if (noSession) setOpen(false);
|
|
20090
21783
|
}, [noSession]);
|
|
21784
|
+
(0, react.useEffect)(() => {
|
|
21785
|
+
if (open) return;
|
|
21786
|
+
setAddOpen(false);
|
|
21787
|
+
setBlockPrompt(null);
|
|
21788
|
+
setFilePrompt(null);
|
|
21789
|
+
}, [open]);
|
|
20091
21790
|
/** Run the same decision over every current-session file, sequentially. */
|
|
20092
21791
|
const runBulk = async (kind) => {
|
|
20093
21792
|
if (current === void 0) return;
|
|
@@ -20099,6 +21798,40 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
20099
21798
|
setBulkBusy(null);
|
|
20100
21799
|
}
|
|
20101
21800
|
};
|
|
21801
|
+
/**
|
|
21802
|
+
* Replace one file's diff with its current local VCS change, then report what
|
|
21803
|
+
* the scan found. A scan that sees no change leaves the entry alone, so the
|
|
21804
|
+
* message names the file rather than silently blanking a review in progress.
|
|
21805
|
+
*/
|
|
21806
|
+
const runRefreshVcs = async (entry) => {
|
|
21807
|
+
if (current === void 0) return;
|
|
21808
|
+
const includeUntracked = includeUntrackedEnabled();
|
|
21809
|
+
let outcome;
|
|
21810
|
+
try {
|
|
21811
|
+
outcome = (await onRefreshVcs(current, entry.id, includeUntracked)).outcome;
|
|
21812
|
+
} catch (error) {
|
|
21813
|
+
showCopyToast(t("panel.refreshFailed", { message: error instanceof Error ? error.message : String(error) }));
|
|
21814
|
+
return;
|
|
21815
|
+
}
|
|
21816
|
+
if (outcome === "refreshed") {
|
|
21817
|
+
showCopyToast(t("panel.refreshDone"));
|
|
21818
|
+
return;
|
|
21819
|
+
}
|
|
21820
|
+
if (outcome === "unchanged") {
|
|
21821
|
+
showCopyToast(t("panel.refreshUnchanged"));
|
|
21822
|
+
return;
|
|
21823
|
+
}
|
|
21824
|
+
if (outcome === "no-change") {
|
|
21825
|
+
const hint = includeUntracked ? "" : ` ${t("panel.refreshUntrackedHint")}`;
|
|
21826
|
+
showCopyToast(`${t("panel.refreshNone")}${hint}`);
|
|
21827
|
+
return;
|
|
21828
|
+
}
|
|
21829
|
+
if (outcome === "no-vcs") {
|
|
21830
|
+
showCopyToast(t("panel.importNoVcs"));
|
|
21831
|
+
return;
|
|
21832
|
+
}
|
|
21833
|
+
showCopyToast(t("panel.fileNotPending"));
|
|
21834
|
+
};
|
|
20102
21835
|
const renderEntry = (entry) => (0, react_jsx_runtime.jsx)(PendingFileRow, {
|
|
20103
21836
|
file: entry,
|
|
20104
21837
|
selected: selected === entry.id,
|
|
@@ -20112,37 +21845,65 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
20112
21845
|
const selectedFile = files.find((file) => file.id === selected);
|
|
20113
21846
|
/** The file whose removal is being confirmed (a last-block action), if any. */
|
|
20114
21847
|
const promptFile = blockPrompt === null ? void 0 : files.find((file) => file.id === blockPrompt.id);
|
|
20115
|
-
|
|
20116
|
-
|
|
20117
|
-
|
|
20118
|
-
|
|
20119
|
-
|
|
20120
|
-
|
|
20121
|
-
|
|
20122
|
-
|
|
20123
|
-
|
|
20124
|
-
|
|
20125
|
-
|
|
20126
|
-
|
|
20127
|
-
|
|
20128
|
-
|
|
20129
|
-
|
|
20130
|
-
|
|
20131
|
-
|
|
20132
|
-
|
|
20133
|
-
|
|
20134
|
-
|
|
20135
|
-
|
|
20136
|
-
|
|
20137
|
-
|
|
20138
|
-
|
|
20139
|
-
|
|
20140
|
-
|
|
20141
|
-
|
|
20142
|
-
|
|
20143
|
-
|
|
20144
|
-
|
|
20145
|
-
|
|
21848
|
+
/** The file whose removal is being confirmed (a whole-file action), if any. */
|
|
21849
|
+
const promptEntry = filePrompt === null ? void 0 : files.find((file) => file.id === filePrompt.id);
|
|
21850
|
+
const fileListBody = (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
21851
|
+
files.length > 0 && (0, react_jsx_runtime.jsxs)("div", {
|
|
21852
|
+
className: PendingPanel_module_css_default.groupHead,
|
|
21853
|
+
children: [
|
|
21854
|
+
(0, react_jsx_runtime.jsx)("h3", {
|
|
21855
|
+
className: PendingPanel_module_css_default.group,
|
|
21856
|
+
children: t("panel.group.current")
|
|
21857
|
+
}),
|
|
21858
|
+
(0, react_jsx_runtime.jsx)("span", { className: PendingPanel_module_css_default.flexSpacer }),
|
|
21859
|
+
(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
21860
|
+
label: t("action.addPath"),
|
|
21861
|
+
side: "bottom",
|
|
21862
|
+
delayMs: 500,
|
|
21863
|
+
children: (0, react_jsx_runtime.jsx)("button", {
|
|
21864
|
+
type: "button",
|
|
21865
|
+
className: `${PendingPanel_module_css_default.action} ${PendingPanel_module_css_default.addButton}`,
|
|
21866
|
+
"data-diff-add": true,
|
|
21867
|
+
"aria-label": t("action.addPath"),
|
|
21868
|
+
onClick: () => {
|
|
21869
|
+
setAddOpen(true);
|
|
21870
|
+
},
|
|
21871
|
+
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutline16, { size: 12 })
|
|
21872
|
+
})
|
|
21873
|
+
})
|
|
21874
|
+
]
|
|
21875
|
+
}),
|
|
21876
|
+
(0, react_jsx_runtime.jsx)("div", {
|
|
21877
|
+
className: PendingPanel_module_css_default.listScroll,
|
|
21878
|
+
"data-diff-list-scroll": true,
|
|
21879
|
+
children: files.length > 0 && (0, react_jsx_runtime.jsx)("ul", {
|
|
21880
|
+
className: PendingPanel_module_css_default.rows,
|
|
21881
|
+
children: files.map(renderEntry)
|
|
21882
|
+
})
|
|
21883
|
+
}),
|
|
21884
|
+
files.length > 0 && (0, react_jsx_runtime.jsxs)("div", {
|
|
21885
|
+
className: PendingPanel_module_css_default.bulkActions,
|
|
21886
|
+
children: [(0, react_jsx_runtime.jsx)("button", {
|
|
21887
|
+
type: "button",
|
|
21888
|
+
className: `${PendingPanel_module_css_default.action} ${PendingPanel_module_css_default.actionPrimary}`,
|
|
21889
|
+
"data-diff-keep-all": true,
|
|
21890
|
+
disabled: bulkBusy !== null,
|
|
21891
|
+
onClick: () => {
|
|
21892
|
+
runBulk("keep");
|
|
21893
|
+
},
|
|
21894
|
+
children: bulkBusy === "keep" ? t("action.busy") : t("action.keepAll")
|
|
21895
|
+
}), (0, react_jsx_runtime.jsx)("button", {
|
|
21896
|
+
type: "button",
|
|
21897
|
+
className: PendingPanel_module_css_default.action,
|
|
21898
|
+
"data-diff-revert-all": true,
|
|
21899
|
+
disabled: bulkBusy !== null,
|
|
21900
|
+
onClick: () => {
|
|
21901
|
+
runBulk("revert");
|
|
21902
|
+
},
|
|
21903
|
+
children: bulkBusy === "revert" ? t("action.busy") : t("action.revertAll")
|
|
21904
|
+
})]
|
|
21905
|
+
})
|
|
21906
|
+
] });
|
|
20146
21907
|
const handleUndo = async (sessionId) => {
|
|
20147
21908
|
const id = await onUndo(sessionId);
|
|
20148
21909
|
if (id === void 0) return;
|
|
@@ -20159,6 +21920,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
20159
21920
|
if (!open || current === void 0) return;
|
|
20160
21921
|
const onKeyDown = (event) => {
|
|
20161
21922
|
if (!(event.ctrlKey || event.metaKey) || event.altKey) return;
|
|
21923
|
+
if (pathPickerOpen()) return;
|
|
20162
21924
|
const target = event.target;
|
|
20163
21925
|
if (target instanceof Element && target.closest("input, textarea, [contenteditable=\"true\"]") !== null) return;
|
|
20164
21926
|
if (matchesShortcut(event, keybindingOf("undo"))) {
|
|
@@ -20188,6 +21950,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
20188
21950
|
if (matchesShortcut(event, keybindingOf("cycleNext"))) direction = 1;
|
|
20189
21951
|
else if (matchesShortcut(event, keybindingOf("cyclePrev"))) direction = -1;
|
|
20190
21952
|
if (direction === 0) return;
|
|
21953
|
+
if (pathPickerOpen()) return;
|
|
20191
21954
|
const target = event.target;
|
|
20192
21955
|
if (target instanceof Element && target.closest("input, textarea, [contenteditable=\"true\"]") !== null) return;
|
|
20193
21956
|
if (files.length === 0) return;
|
|
@@ -20209,6 +21972,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
20209
21972
|
(0, react.useEffect)(() => {
|
|
20210
21973
|
const onKeyDown = (event) => {
|
|
20211
21974
|
if (!matchesShortcut(event, quickSummonKey())) return;
|
|
21975
|
+
if (pathPickerOpen()) return;
|
|
20212
21976
|
event.preventDefault();
|
|
20213
21977
|
if (!open) collapseSidebar();
|
|
20214
21978
|
setOpen((value) => !value);
|
|
@@ -20222,8 +21986,9 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
20222
21986
|
if (!open) return;
|
|
20223
21987
|
const onKeyDown = (event) => {
|
|
20224
21988
|
if (event.key !== "Escape") return;
|
|
21989
|
+
if (pathPickerOpen()) return;
|
|
20225
21990
|
const target = event.target;
|
|
20226
|
-
if (target instanceof
|
|
21991
|
+
if (target instanceof Node && panelRef.current?.contains(target) === true && document.querySelector("[data-diff-searchbar]") !== null) return;
|
|
20227
21992
|
event.preventDefault();
|
|
20228
21993
|
setOpen(false);
|
|
20229
21994
|
};
|
|
@@ -20366,15 +22131,26 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
20366
22131
|
className: `${PendingPanel_module_css_default.note} ${PendingPanel_module_css_default.noteCentered}`,
|
|
20367
22132
|
children: t("panel.empty")
|
|
20368
22133
|
}),
|
|
20369
|
-
(0, react_jsx_runtime.
|
|
20370
|
-
|
|
20371
|
-
|
|
20372
|
-
|
|
20373
|
-
|
|
20374
|
-
|
|
20375
|
-
|
|
20376
|
-
|
|
20377
|
-
|
|
22134
|
+
(0, react_jsx_runtime.jsxs)("div", {
|
|
22135
|
+
className: PendingPanel_module_css_default.emptyActions,
|
|
22136
|
+
children: [(0, react_jsx_runtime.jsx)("button", {
|
|
22137
|
+
type: "button",
|
|
22138
|
+
className: PendingPanel_module_css_default.importButton,
|
|
22139
|
+
"data-diff-import-vcs": true,
|
|
22140
|
+
disabled: importBusy,
|
|
22141
|
+
onClick: () => {
|
|
22142
|
+
runImportVcs();
|
|
22143
|
+
},
|
|
22144
|
+
children: importBusy ? t("action.importVcsBusy") : t("action.importVcs")
|
|
22145
|
+
}), (0, react_jsx_runtime.jsx)("button", {
|
|
22146
|
+
type: "button",
|
|
22147
|
+
className: PendingPanel_module_css_default.importButton,
|
|
22148
|
+
"data-diff-add": true,
|
|
22149
|
+
onClick: () => {
|
|
22150
|
+
setAddOpen(true);
|
|
22151
|
+
},
|
|
22152
|
+
children: t("action.addPath")
|
|
22153
|
+
})]
|
|
20378
22154
|
}),
|
|
20379
22155
|
importNote !== void 0 && (0, react_jsx_runtime.jsx)("p", {
|
|
20380
22156
|
className: PendingPanel_module_css_default.importNote,
|
|
@@ -20414,8 +22190,11 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
20414
22190
|
onPasteReference,
|
|
20415
22191
|
onToast: showCopyToast,
|
|
20416
22192
|
t,
|
|
20417
|
-
onKeep,
|
|
20418
|
-
onRevert,
|
|
22193
|
+
onKeep: keepWithPrompt,
|
|
22194
|
+
onRevert: revertWithPrompt,
|
|
22195
|
+
onRefreshVcs: (entry) => {
|
|
22196
|
+
runRefreshVcs(entry);
|
|
22197
|
+
},
|
|
20419
22198
|
onBlockKeep: blockKeepWithPrompt,
|
|
20420
22199
|
onBlockRevert: blockRevertWithPrompt,
|
|
20421
22200
|
onOpen,
|
|
@@ -20473,6 +22252,52 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
20473
22252
|
})]
|
|
20474
22253
|
})]
|
|
20475
22254
|
})
|
|
22255
|
+
}),
|
|
22256
|
+
filePrompt !== null && promptEntry !== void 0 && (0, react_jsx_runtime.jsx)("div", {
|
|
22257
|
+
className: PendingPanel_module_css_default.confirmBackdrop,
|
|
22258
|
+
"data-diff-confirm-file": true,
|
|
22259
|
+
children: (0, react_jsx_runtime.jsxs)("div", {
|
|
22260
|
+
className: PendingPanel_module_css_default.confirmCard,
|
|
22261
|
+
role: "dialog",
|
|
22262
|
+
"aria-modal": "true",
|
|
22263
|
+
children: [(0, react_jsx_runtime.jsx)("p", {
|
|
22264
|
+
className: PendingPanel_module_css_default.confirmText,
|
|
22265
|
+
children: t(filePrompt.action === "keep" ? "panel.fileKeptAsk" : "panel.fileRevertedAsk", { file: basenameOf(promptEntry.path) })
|
|
22266
|
+
}), (0, react_jsx_runtime.jsxs)("div", {
|
|
22267
|
+
className: PendingPanel_module_css_default.confirmActions,
|
|
22268
|
+
children: [(0, react_jsx_runtime.jsx)("button", {
|
|
22269
|
+
type: "button",
|
|
22270
|
+
className: `${PendingPanel_module_css_default.action} ${PendingPanel_module_css_default.actionPrimary}`,
|
|
22271
|
+
"data-diff-file-confirm-remove": true,
|
|
22272
|
+
onClick: () => {
|
|
22273
|
+
setFilePrompt(null);
|
|
22274
|
+
const { action, sessionId, id } = filePrompt;
|
|
22275
|
+
action === "keep" ? onKeep(sessionId, id, false) : onRevert(sessionId, id, false);
|
|
22276
|
+
},
|
|
22277
|
+
children: t("row.dismiss")
|
|
22278
|
+
}), (0, react_jsx_runtime.jsx)("button", {
|
|
22279
|
+
type: "button",
|
|
22280
|
+
className: PendingPanel_module_css_default.action,
|
|
22281
|
+
"data-diff-file-confirm-keep": true,
|
|
22282
|
+
onClick: () => {
|
|
22283
|
+
setFilePrompt(null);
|
|
22284
|
+
const { action, sessionId, id } = filePrompt;
|
|
22285
|
+
action === "keep" ? onKeep(sessionId, id, true) : onRevert(sessionId, id, true);
|
|
22286
|
+
},
|
|
22287
|
+
children: t("panel.keepInList")
|
|
22288
|
+
})]
|
|
22289
|
+
})]
|
|
22290
|
+
})
|
|
22291
|
+
}),
|
|
22292
|
+
addOpen && current !== void 0 && (0, react_jsx_runtime.jsx)(PathPicker, {
|
|
22293
|
+
rootPath: snapshot.workspacePath,
|
|
22294
|
+
onBrowse: (path) => onBrowse(current, path),
|
|
22295
|
+
onAdd: (path, includeUnchanged) => onAddPath(current, path, includeUnchanged),
|
|
22296
|
+
onToast: showCopyToast,
|
|
22297
|
+
onClose: () => {
|
|
22298
|
+
setAddOpen(false);
|
|
22299
|
+
},
|
|
22300
|
+
t
|
|
20476
22301
|
})
|
|
20477
22302
|
]
|
|
20478
22303
|
})] }), document.body),
|
|
@@ -21105,6 +22930,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
21105
22930
|
function DiffApprovalSettingsTab({ t }) {
|
|
21106
22931
|
const [pasteOnCopy, setPasteOnCopyState] = (0, react.useState)(pasteOnCopyEnabled);
|
|
21107
22932
|
const [includeUntracked, setIncludeUntrackedState] = (0, react.useState)(includeUntrackedEnabled);
|
|
22933
|
+
const [confirmFileRemove, setConfirmFileRemoveState] = (0, react.useState)(confirmFileRemoveEnabled);
|
|
21108
22934
|
const [tab, setTabState] = (0, react.useState)(tabWidth);
|
|
21109
22935
|
const [tabOpen, setTabOpen] = (0, react.useState)(false);
|
|
21110
22936
|
const [split, setSplitState] = (0, react.useState)(splitMode);
|
|
@@ -21144,6 +22970,10 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
21144
22970
|
setIncludeUntrackedState(value);
|
|
21145
22971
|
setIncludeUntrackedEnabled(value);
|
|
21146
22972
|
};
|
|
22973
|
+
const setConfirmFileRemove = (value) => {
|
|
22974
|
+
setConfirmFileRemoveState(value);
|
|
22975
|
+
setConfirmFileRemoveEnabled(value);
|
|
22976
|
+
};
|
|
21147
22977
|
const setTab = (value) => {
|
|
21148
22978
|
setTabState(value);
|
|
21149
22979
|
setTabWidth(value);
|
|
@@ -21357,6 +23187,14 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
21357
23187
|
onSelect: setIncludeUntracked,
|
|
21358
23188
|
dataAttribute: "data-diff-import-untracked-select",
|
|
21359
23189
|
t
|
|
23190
|
+
}),
|
|
23191
|
+
(0, react_jsx_runtime.jsx)(PreferenceRow, {
|
|
23192
|
+
title: t("panel.confirmFileRemove"),
|
|
23193
|
+
description: t("panel.confirmFileRemoveDesc"),
|
|
23194
|
+
value: confirmFileRemove,
|
|
23195
|
+
onSelect: setConfirmFileRemove,
|
|
23196
|
+
dataAttribute: "data-diff-confirm-file-remove-select",
|
|
23197
|
+
t
|
|
21360
23198
|
})
|
|
21361
23199
|
]
|
|
21362
23200
|
});
|
|
@@ -21380,16 +23218,24 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
21380
23218
|
async list(sessionId) {
|
|
21381
23219
|
return listValueOf(await rpc.call(DIFF_APPROVAL_CHANNEL, "list", { sessionId }));
|
|
21382
23220
|
},
|
|
21383
|
-
async keep(sessionId, id) {
|
|
21384
|
-
return actionOf(await rpc.call(DIFF_APPROVAL_CHANNEL, "keep", {
|
|
23221
|
+
async keep(sessionId, id, keepListed) {
|
|
23222
|
+
return actionOf(await rpc.call(DIFF_APPROVAL_CHANNEL, "keep", keepListed === void 0 ? {
|
|
21385
23223
|
sessionId,
|
|
21386
23224
|
id
|
|
23225
|
+
} : {
|
|
23226
|
+
sessionId,
|
|
23227
|
+
id,
|
|
23228
|
+
keepListed
|
|
21387
23229
|
}));
|
|
21388
23230
|
},
|
|
21389
|
-
async revert(sessionId, id) {
|
|
21390
|
-
return actionOf(await rpc.call(DIFF_APPROVAL_CHANNEL, "revert", {
|
|
23231
|
+
async revert(sessionId, id, keepListed) {
|
|
23232
|
+
return actionOf(await rpc.call(DIFF_APPROVAL_CHANNEL, "revert", keepListed === void 0 ? {
|
|
21391
23233
|
sessionId,
|
|
21392
23234
|
id
|
|
23235
|
+
} : {
|
|
23236
|
+
sessionId,
|
|
23237
|
+
id,
|
|
23238
|
+
keepListed
|
|
21393
23239
|
}));
|
|
21394
23240
|
},
|
|
21395
23241
|
async blockKeep(sessionId, id, block, removeWhenResolved) {
|
|
@@ -21420,6 +23266,26 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
21420
23266
|
includeUntracked
|
|
21421
23267
|
}));
|
|
21422
23268
|
},
|
|
23269
|
+
async refreshVcs(sessionId, id, includeUntracked) {
|
|
23270
|
+
return refreshValueOf(await rpc.call(DIFF_APPROVAL_CHANNEL, "vcs-refresh", {
|
|
23271
|
+
sessionId,
|
|
23272
|
+
id,
|
|
23273
|
+
includeUntracked
|
|
23274
|
+
}));
|
|
23275
|
+
},
|
|
23276
|
+
async browse(sessionId, path) {
|
|
23277
|
+
return browseValueOf(await rpc.call(DIFF_APPROVAL_CHANNEL, "list-path", path === void 0 ? { sessionId } : {
|
|
23278
|
+
sessionId,
|
|
23279
|
+
path
|
|
23280
|
+
}));
|
|
23281
|
+
},
|
|
23282
|
+
async addPath(sessionId, path, includeUnchanged) {
|
|
23283
|
+
return addValueOf(await rpc.call(DIFF_APPROVAL_CHANNEL, "add-path", {
|
|
23284
|
+
sessionId,
|
|
23285
|
+
path,
|
|
23286
|
+
includeUnchanged
|
|
23287
|
+
}));
|
|
23288
|
+
},
|
|
21423
23289
|
async open(sessionId, id, action) {
|
|
21424
23290
|
return openOf(await rpc.call(DIFF_APPROVAL_CHANNEL, "open", {
|
|
21425
23291
|
sessionId,
|
|
@@ -21519,6 +23385,64 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
21519
23385
|
detected
|
|
21520
23386
|
};
|
|
21521
23387
|
}
|
|
23388
|
+
/** Narrow the vcs-refresh endpoint's value; a malformed wire value is a failure. */
|
|
23389
|
+
function refreshValueOf(result) {
|
|
23390
|
+
if (!result.ok) throw new Error(`${result.error.code}: ${result.error.message}`);
|
|
23391
|
+
const value = result.value;
|
|
23392
|
+
if (typeof value !== "object" || value === null) throw new Error("the refresh returned a malformed value");
|
|
23393
|
+
const outcome = value.outcome;
|
|
23394
|
+
if (outcome !== "refreshed" && outcome !== "unchanged" && outcome !== "no-change" && outcome !== "missing" && outcome !== "no-vcs") throw new Error("the refresh returned a malformed outcome");
|
|
23395
|
+
return { outcome };
|
|
23396
|
+
}
|
|
23397
|
+
/** Narrow the list-path endpoint's value; a malformed wire value is a browse failure. */
|
|
23398
|
+
function browseValueOf(result) {
|
|
23399
|
+
if (!result.ok) throw new Error(`${result.error.code}: ${result.error.message}`);
|
|
23400
|
+
const value = result.value;
|
|
23401
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error("the browse returned a malformed value");
|
|
23402
|
+
const record = value;
|
|
23403
|
+
const path = record.path;
|
|
23404
|
+
if (typeof path !== "string") throw new Error("the browse returned a malformed value");
|
|
23405
|
+
const rows = record.entries;
|
|
23406
|
+
if (!Array.isArray(rows)) throw new Error("the browse returned a malformed value");
|
|
23407
|
+
const entries = [];
|
|
23408
|
+
for (const row of rows) {
|
|
23409
|
+
if (typeof row !== "object" || row === null || Array.isArray(row)) continue;
|
|
23410
|
+
const { name, type, path: childPath, size } = row;
|
|
23411
|
+
if (typeof name !== "string" || typeof childPath !== "string") continue;
|
|
23412
|
+
if (type !== "file" && type !== "directory" && type !== "other") continue;
|
|
23413
|
+
entries.push({
|
|
23414
|
+
name,
|
|
23415
|
+
type,
|
|
23416
|
+
path: childPath,
|
|
23417
|
+
size: typeof size === "number" && Number.isFinite(size) ? size : void 0
|
|
23418
|
+
});
|
|
23419
|
+
}
|
|
23420
|
+
return {
|
|
23421
|
+
path,
|
|
23422
|
+
entries,
|
|
23423
|
+
truncated: record.truncated === true
|
|
23424
|
+
};
|
|
23425
|
+
}
|
|
23426
|
+
/** Narrow the add-path endpoint's value; a malformed wire value is an add failure. */
|
|
23427
|
+
function addValueOf(result) {
|
|
23428
|
+
if (!result.ok) throw new Error(`${result.error.code}: ${result.error.message}`);
|
|
23429
|
+
const value = result.value;
|
|
23430
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error("the add returned a malformed value");
|
|
23431
|
+
const record = value;
|
|
23432
|
+
const outcome = record.outcome;
|
|
23433
|
+
if (outcome !== "added" && outcome !== "duplicate" && outcome !== "unchanged" && outcome !== "empty" && outcome !== "missing" && outcome !== "outside" && outcome !== "no-vcs" && outcome !== "failed") throw new Error("the add returned a malformed outcome");
|
|
23434
|
+
const added = record.added;
|
|
23435
|
+
const duplicates = record.duplicates;
|
|
23436
|
+
if (typeof added !== "number" || typeof duplicates !== "number") throw new Error("the add returned a malformed value");
|
|
23437
|
+
const message = record.message;
|
|
23438
|
+
return {
|
|
23439
|
+
outcome,
|
|
23440
|
+
added,
|
|
23441
|
+
duplicates,
|
|
23442
|
+
truncated: record.truncated === true ? true : void 0,
|
|
23443
|
+
message: typeof message === "string" && message.length > 0 ? message : void 0
|
|
23444
|
+
};
|
|
23445
|
+
}
|
|
21522
23446
|
/** Narrow the open endpoint's value; a malformed wire value is an open failure. */
|
|
21523
23447
|
function openOf(result) {
|
|
21524
23448
|
if (!result.ok) throw new Error(`${result.error.code}: ${result.error.message}`);
|
|
@@ -21633,6 +23557,36 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
21633
23557
|
busy: new Set([...snapshot.busy].filter((busy) => busy !== id))
|
|
21634
23558
|
});
|
|
21635
23559
|
};
|
|
23560
|
+
/**
|
|
23561
|
+
* Run a whole-file action whose entry STAYS listed (the panel chose "keep in
|
|
23562
|
+
* list"). The local list must not drop it optimistically — that made a kept
|
|
23563
|
+
* file blink out and come back on the next poll — so this takes the block
|
|
23564
|
+
* actions' shape instead: mark busy, run, then re-read so the entry's now-empty
|
|
23565
|
+
* diff shows at once.
|
|
23566
|
+
* @param id - the entry being acted on.
|
|
23567
|
+
* @param refresh - the store's refresh, awaited after a successful action.
|
|
23568
|
+
* @param action - the port call to run.
|
|
23569
|
+
*/
|
|
23570
|
+
const withKeptEntry = async (id, refresh, action) => {
|
|
23571
|
+
const { error: _cleared, ...base } = snapshot;
|
|
23572
|
+
publish({
|
|
23573
|
+
...base,
|
|
23574
|
+
failed: failedOf(snapshot),
|
|
23575
|
+
busy: /* @__PURE__ */ new Set([...snapshot.busy, id])
|
|
23576
|
+
});
|
|
23577
|
+
try {
|
|
23578
|
+
await action();
|
|
23579
|
+
} catch (error) {
|
|
23580
|
+
markFailed(id, error instanceof Error ? error.message : String(error));
|
|
23581
|
+
publish({
|
|
23582
|
+
...snapshot,
|
|
23583
|
+
busy: new Set([...snapshot.busy].filter((busy) => busy !== id))
|
|
23584
|
+
});
|
|
23585
|
+
return;
|
|
23586
|
+
}
|
|
23587
|
+
clearFailed(id);
|
|
23588
|
+
await refresh();
|
|
23589
|
+
};
|
|
21636
23590
|
return {
|
|
21637
23591
|
getSnapshot: () => snapshot,
|
|
21638
23592
|
subscribe(listener) {
|
|
@@ -21672,12 +23626,18 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
21672
23626
|
});
|
|
21673
23627
|
}
|
|
21674
23628
|
},
|
|
21675
|
-
keep(sessionId, id) {
|
|
23629
|
+
keep(sessionId, id, keepListed) {
|
|
23630
|
+
if (keepListed === true) return withKeptEntry(id, () => this.refresh(sessionId), async () => {
|
|
23631
|
+
await port.keep(sessionId, id, true);
|
|
23632
|
+
});
|
|
21676
23633
|
return withBusy(id, async () => {
|
|
21677
23634
|
await port.keep(sessionId, id);
|
|
21678
23635
|
});
|
|
21679
23636
|
},
|
|
21680
|
-
revert(sessionId, id) {
|
|
23637
|
+
revert(sessionId, id, keepListed) {
|
|
23638
|
+
if (keepListed === true) return withKeptEntry(id, () => this.refresh(sessionId), async () => {
|
|
23639
|
+
await port.revert(sessionId, id, true);
|
|
23640
|
+
});
|
|
21681
23641
|
return withBusy(id, async () => {
|
|
21682
23642
|
await port.revert(sessionId, id);
|
|
21683
23643
|
});
|
|
@@ -21747,6 +23707,31 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
21747
23707
|
await this.refresh(sessionId);
|
|
21748
23708
|
return value;
|
|
21749
23709
|
},
|
|
23710
|
+
async refreshVcs(sessionId, id, includeUntracked) {
|
|
23711
|
+
const { error: _cleared, ...base } = snapshot;
|
|
23712
|
+
publish({
|
|
23713
|
+
...base,
|
|
23714
|
+
busy: /* @__PURE__ */ new Set([...snapshot.busy, id])
|
|
23715
|
+
});
|
|
23716
|
+
try {
|
|
23717
|
+
const value = await port.refreshVcs(sessionId, id, includeUntracked);
|
|
23718
|
+
await this.refresh(sessionId);
|
|
23719
|
+
return value;
|
|
23720
|
+
} finally {
|
|
23721
|
+
publish({
|
|
23722
|
+
...snapshot,
|
|
23723
|
+
busy: new Set([...snapshot.busy].filter((busy) => busy !== id))
|
|
23724
|
+
});
|
|
23725
|
+
}
|
|
23726
|
+
},
|
|
23727
|
+
browse(sessionId, path) {
|
|
23728
|
+
return port.browse(sessionId, path);
|
|
23729
|
+
},
|
|
23730
|
+
async addPath(sessionId, path, includeUnchanged) {
|
|
23731
|
+
const value = await port.addPath(sessionId, path, includeUnchanged);
|
|
23732
|
+
await this.refresh(sessionId);
|
|
23733
|
+
return value;
|
|
23734
|
+
},
|
|
21750
23735
|
async open(sessionId, id, action) {
|
|
21751
23736
|
try {
|
|
21752
23737
|
await port.open(sessionId, id, action);
|
|
@@ -21925,7 +23910,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
21925
23910
|
/** Simplified Chinese pending-edit review messages. */
|
|
21926
23911
|
const zh = {
|
|
21927
23912
|
"panel.title": "待处理改动",
|
|
21928
|
-
"panel.empty": "
|
|
23913
|
+
"panel.empty": "列表中暂无文件",
|
|
21929
23914
|
"panel.loading": "读取中…",
|
|
21930
23915
|
"panel.readFailed": "读取待处理改动失败:{message}",
|
|
21931
23916
|
"panel.group.current": "当前会话",
|
|
@@ -21981,6 +23966,8 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
21981
23966
|
"panel.key.openSearch": "打开差异搜索",
|
|
21982
23967
|
"panel.key.searchNext": "下一个搜索结果",
|
|
21983
23968
|
"panel.key.searchPrev": "上一个搜索结果",
|
|
23969
|
+
"panel.key.matchCase": "切换区分大小写",
|
|
23970
|
+
"panel.key.matchWholeWord": "切换全字匹配",
|
|
21984
23971
|
"panel.key.undo": "撤销",
|
|
21985
23972
|
"panel.key.redo": "重做",
|
|
21986
23973
|
"panel.key.cycleNext": "下一个待处理文件",
|
|
@@ -22028,7 +24015,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
22028
24015
|
"panel.importDone": "已导入 {count} 个改动",
|
|
22029
24016
|
"panel.importNoVcs": "未检测到版本控制(Git / SVN / Perforce)",
|
|
22030
24017
|
"panel.importFailed": "导入失败:{message}",
|
|
22031
|
-
"action.search": "搜索
|
|
24018
|
+
"action.search": "搜索",
|
|
22032
24019
|
"action.settings": "设置",
|
|
22033
24020
|
"action.expand": "展开全屏",
|
|
22034
24021
|
"action.exitFullscreen": "退出全屏",
|
|
@@ -22037,12 +24024,44 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
22037
24024
|
"status.reverted": "已回退",
|
|
22038
24025
|
"status.missing": "该改动已不存在",
|
|
22039
24026
|
"panel.resolvedAsk": "「{file}」的所有改动都已处理,是否从列表移除?",
|
|
22040
|
-
"panel.keepInList": "保留在列表"
|
|
24027
|
+
"panel.keepInList": "保留在列表",
|
|
24028
|
+
"panel.fileKeptAsk": "「{file}」的改动已全部保留,是否从列表移除?",
|
|
24029
|
+
"panel.fileRevertedAsk": "「{file}」的改动已全部回退,是否从列表移除?",
|
|
24030
|
+
"panel.confirmFileRemove": "处理整文件后询问是否移出列表",
|
|
24031
|
+
"panel.confirmFileRemoveDesc": "对单个文件执行整文件 Keep/Revert 后,弹窗询问是否把该文件移出列表。关闭后直接移出(不影响批量 Keep All / Revert All)。",
|
|
24032
|
+
"action.refreshVcs": "刷新差异",
|
|
24033
|
+
"action.matchCase": "区分大小写",
|
|
24034
|
+
"action.matchWholeWord": "全字匹配",
|
|
24035
|
+
"panel.refreshDone": "差异已更新",
|
|
24036
|
+
"panel.refreshUnchanged": "差异已是最新",
|
|
24037
|
+
"panel.refreshNone": "未检测到本地 VCS 差异",
|
|
24038
|
+
"panel.refreshUntrackedHint": "(新建/未跟踪文件需要开启「导入未跟踪的文件改动」)",
|
|
24039
|
+
"panel.refreshFailed": "刷新失败:{message}",
|
|
24040
|
+
"action.addPath": "添加文件或目录",
|
|
24041
|
+
"panel.addTitle": "添加到列表",
|
|
24042
|
+
"panel.addPathPlaceholder": "输入或粘贴绝对路径",
|
|
24043
|
+
"panel.addPathGo": "添加",
|
|
24044
|
+
"panel.addHint": "展开目录逐级浏览,选中会把完整路径填到上方输入框;点「添加」时才判断有没有本地改动",
|
|
24045
|
+
"panel.addIncludeUnchanged": "包含无改动路径",
|
|
24046
|
+
"panel.addRoot": "工作区根目录",
|
|
24047
|
+
"panel.addTruncated": "目录内容过多,仅显示前 {count} 项",
|
|
24048
|
+
"panel.addDone": "已加入 {count} 个改动",
|
|
24049
|
+
"panel.addDoneTruncated": "已加入 {count} 个改动;没有改动的文件过多,其余未加入",
|
|
24050
|
+
"panel.addDuplicate": "该路径已在列表中",
|
|
24051
|
+
"panel.addUnchanged": "该文件没有本地差异;勾选「包含无改动路径」后再添加",
|
|
24052
|
+
"panel.addEmpty": "没有找到可加入的改动",
|
|
24053
|
+
"panel.addNoEntries": "此目录为空",
|
|
24054
|
+
"panel.addMissing": "路径不存在",
|
|
24055
|
+
"panel.addOutside": "路径不在当前工作区内",
|
|
24056
|
+
"panel.addFailed": "添加失败:{message}",
|
|
24057
|
+
"action.cancel": "取消",
|
|
24058
|
+
"action.collapseRow": "收起",
|
|
24059
|
+
"action.expandRow": "展开"
|
|
22041
24060
|
};
|
|
22042
24061
|
/** English pending-edit review messages. */
|
|
22043
24062
|
const en = {
|
|
22044
24063
|
"panel.title": "Pending changes",
|
|
22045
|
-
"panel.empty": "No
|
|
24064
|
+
"panel.empty": "No files in the list yet",
|
|
22046
24065
|
"panel.loading": "Reading…",
|
|
22047
24066
|
"panel.readFailed": "Reading pending changes failed: {message}",
|
|
22048
24067
|
"panel.group.current": "This session",
|
|
@@ -22098,6 +24117,8 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
22098
24117
|
"panel.key.openSearch": "Open diff search",
|
|
22099
24118
|
"panel.key.searchNext": "Next search result",
|
|
22100
24119
|
"panel.key.searchPrev": "Previous search result",
|
|
24120
|
+
"panel.key.matchCase": "Toggle match case",
|
|
24121
|
+
"panel.key.matchWholeWord": "Toggle match whole word",
|
|
22101
24122
|
"panel.key.undo": "Undo",
|
|
22102
24123
|
"panel.key.redo": "Redo",
|
|
22103
24124
|
"panel.key.cycleNext": "Next pending file",
|
|
@@ -22145,7 +24166,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
22145
24166
|
"panel.importDone": "Imported {count} changes",
|
|
22146
24167
|
"panel.importNoVcs": "No version control detected (Git / SVN / Perforce)",
|
|
22147
24168
|
"panel.importFailed": "Import failed: {message}",
|
|
22148
|
-
"action.search": "Search
|
|
24169
|
+
"action.search": "Search",
|
|
22149
24170
|
"action.settings": "Settings",
|
|
22150
24171
|
"action.expand": "Expand fullscreen",
|
|
22151
24172
|
"action.exitFullscreen": "Exit fullscreen",
|
|
@@ -22154,7 +24175,39 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
22154
24175
|
"status.reverted": "Reverted",
|
|
22155
24176
|
"status.missing": "This change no longer exists",
|
|
22156
24177
|
"panel.resolvedAsk": "All changes in \"{file}\" are resolved. Remove it from the list?",
|
|
22157
|
-
"panel.keepInList": "Keep in list"
|
|
24178
|
+
"panel.keepInList": "Keep in list",
|
|
24179
|
+
"panel.fileKeptAsk": "All changes in \"{file}\" are kept. Remove it from the list?",
|
|
24180
|
+
"panel.fileRevertedAsk": "All changes in \"{file}\" are reverted. Remove it from the list?",
|
|
24181
|
+
"panel.confirmFileRemove": "Ask after a whole-file keep/revert",
|
|
24182
|
+
"panel.confirmFileRemoveDesc": "After a whole-file keep/revert on a single file, ask whether to remove that file from the list. When off, it is removed straight away (Keep All / Revert All are unaffected).",
|
|
24183
|
+
"action.refreshVcs": "Refresh diff",
|
|
24184
|
+
"action.matchCase": "Match case",
|
|
24185
|
+
"action.matchWholeWord": "Match whole word",
|
|
24186
|
+
"panel.refreshDone": "Diff updated",
|
|
24187
|
+
"panel.refreshUnchanged": "Already up to date",
|
|
24188
|
+
"panel.refreshNone": "No local VCS change found",
|
|
24189
|
+
"panel.refreshUntrackedHint": "(new or untracked files need \"Import changes to untracked files\")",
|
|
24190
|
+
"panel.refreshFailed": "Refresh failed: {message}",
|
|
24191
|
+
"action.addPath": "Add file or directory",
|
|
24192
|
+
"panel.addTitle": "Add to list",
|
|
24193
|
+
"panel.addPathPlaceholder": "Type or paste an absolute path",
|
|
24194
|
+
"panel.addPathGo": "Add",
|
|
24195
|
+
"panel.addHint": "Open directories to walk the tree; a selection fills the full path above, and Add is what checks for local changes",
|
|
24196
|
+
"panel.addIncludeUnchanged": "Include unchanged paths",
|
|
24197
|
+
"panel.addRoot": "Workspace root",
|
|
24198
|
+
"panel.addTruncated": "Too many entries to show; only the first {count} are listed",
|
|
24199
|
+
"panel.addDone": "Added {count} change(s)",
|
|
24200
|
+
"panel.addDoneTruncated": "Added {count} change(s); too many unchanged files to include the rest",
|
|
24201
|
+
"panel.addDuplicate": "That path is already in the list",
|
|
24202
|
+
"panel.addUnchanged": "That file has no local change; tick \"Include unchanged paths\" and add again",
|
|
24203
|
+
"panel.addEmpty": "No changes found to add",
|
|
24204
|
+
"panel.addNoEntries": "This directory is empty",
|
|
24205
|
+
"panel.addMissing": "No such path",
|
|
24206
|
+
"panel.addOutside": "That path is outside the workspace",
|
|
24207
|
+
"panel.addFailed": "Add failed: {message}",
|
|
24208
|
+
"action.cancel": "Cancel",
|
|
24209
|
+
"action.collapseRow": "Collapse",
|
|
24210
|
+
"action.expandRow": "Expand"
|
|
22158
24211
|
};
|
|
22159
24212
|
//#endregion
|
|
22160
24213
|
//#region lib/types/client/index.js
|
|
@@ -22269,13 +24322,13 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
22269
24322
|
currentSessionId = sessionId;
|
|
22270
24323
|
store.refresh(sessionId);
|
|
22271
24324
|
},
|
|
22272
|
-
onKeep: (sessionId, path) => store.keep(sessionId, path),
|
|
22273
|
-
onRevert: (sessionId, path) => {
|
|
24325
|
+
onKeep: (sessionId, path, keepListed) => store.keep(sessionId, path, keepListed),
|
|
24326
|
+
onRevert: (sessionId, path, keepListed) => {
|
|
22274
24327
|
const entry = store.getSnapshot().files.find((file) => file.id === path);
|
|
22275
24328
|
const before = entry?.newText;
|
|
22276
24329
|
const after = entry?.oldText;
|
|
22277
24330
|
const filePath = entry?.path;
|
|
22278
|
-
return store.revert(sessionId, path).then(() => {
|
|
24331
|
+
return store.revert(sessionId, path, keepListed).then(() => {
|
|
22279
24332
|
if (filePath !== void 0 && before !== void 0 && after !== void 0) remapFile(filePath, before, after);
|
|
22280
24333
|
});
|
|
22281
24334
|
},
|
|
@@ -22286,6 +24339,9 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
22286
24339
|
onUndo: (sessionId) => store.undo(sessionId),
|
|
22287
24340
|
onRedo: (sessionId) => store.redo(sessionId),
|
|
22288
24341
|
onImportVcs: (sessionId, includeUntracked) => store.importVcs(sessionId, includeUntracked),
|
|
24342
|
+
onRefreshVcs: (sessionId, id, includeUntracked) => store.refreshVcs(sessionId, id, includeUntracked),
|
|
24343
|
+
onBrowse: (sessionId, path) => store.browse(sessionId, path),
|
|
24344
|
+
onAddPath: (sessionId, path, includeUnchanged) => store.addPath(sessionId, path, includeUnchanged),
|
|
22289
24345
|
onKeepAll: (sessionId) => store.keepAll(sessionId),
|
|
22290
24346
|
onRevertAll: (sessionId) => store.revertAll(sessionId),
|
|
22291
24347
|
onAckRedoCleared: () => store.clearRedoCleared(),
|