dsh-diff-approval 0.19.3 → 0.20.1
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 +2046 -601
- package/lib/index.js +1251 -60
- package/lib/types/client/PathPicker.d.ts +43 -0
- package/lib/types/client/PendingPanel.d.ts +9 -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 +43 -0
- package/lib/types/client/port.d.ts +5 -1
- package/lib/types/client/settings.d.ts +16 -0
- package/lib/types/client/slots.d.ts +5 -1
- package/lib/types/client/store.d.ts +5 -1
- package/lib/types/client/whole-file-diff.d.ts +21 -0
- package/lib/types/client/windowed-highlight.d.ts +66 -0
- package/lib/types/pending.d.ts +10 -0
- package/lib/types/types.d.ts +51 -0
- package/package.json +1 -13
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{min-width:0;height:var(--dsh-scrollbar-width,8px);flex:1 1 0;overflow:auto hidden}.F1KBNa_splitHScroll::-webkit-scrollbar{height:var(--dsh-scrollbar-width,8px)}.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
|
+
"settingsGroupTitle": "F1KBNa_settingsGroupTitle",
|
|
35
|
+
"addCount": "F1KBNa_addCount",
|
|
36
|
+
"delCount": "F1KBNa_delCount",
|
|
37
|
+
"kindTag": "F1KBNa_kindTag",
|
|
38
|
+
"treeRow": "F1KBNa_treeRow",
|
|
39
|
+
"divider": "F1KBNa_divider",
|
|
40
|
+
"searchToggleOn": "F1KBNa_searchToggleOn",
|
|
41
|
+
"settingsGroupText": "F1KBNa_settingsGroupText",
|
|
42
|
+
"treeName": "F1KBNa_treeName",
|
|
43
|
+
"iconAction": "F1KBNa_iconAction",
|
|
44
|
+
"expandExpanded": "F1KBNa_expandExpanded",
|
|
45
|
+
"settingsRowDesc": "F1KBNa_settingsRowDesc",
|
|
46
|
+
"split": "F1KBNa_split",
|
|
47
|
+
"splitHScrollFill": "F1KBNa_splitHScrollFill",
|
|
48
|
+
"mdPreviewBody": "F1KBNa_mdPreviewBody",
|
|
49
|
+
"badge": "F1KBNa_badge",
|
|
50
|
+
"blockPosition": "F1KBNa_blockPosition",
|
|
51
|
+
"searchBar": "F1KBNa_searchBar",
|
|
52
|
+
"badgeLabel": "F1KBNa_badgeLabel",
|
|
53
|
+
"pickerTitle": "F1KBNa_pickerTitle",
|
|
54
|
+
"splitRoot": "F1KBNa_splitRoot",
|
|
55
|
+
"splitCol": "F1KBNa_splitCol",
|
|
56
|
+
"wrapActive": "F1KBNa_wrapActive",
|
|
57
|
+
"missing": "F1KBNa_missing",
|
|
58
|
+
"splitLadd": "F1KBNa_splitLadd",
|
|
59
|
+
"blockFlash": "F1KBNa_blockFlash",
|
|
60
|
+
"diffActions": "F1KBNa_diffActions",
|
|
61
|
+
"confirmBackdrop": "F1KBNa_confirmBackdrop",
|
|
62
|
+
"splitHScroll": "F1KBNa_splitHScroll",
|
|
63
|
+
"pickerNote": "F1KBNa_pickerNote",
|
|
64
|
+
"statusAction": "F1KBNa_statusAction",
|
|
65
|
+
"colorBackdrop": "F1KBNa_colorBackdrop",
|
|
66
|
+
"close": "F1KBNa_close",
|
|
67
|
+
"notice": "F1KBNa_notice",
|
|
68
|
+
"layer": "F1KBNa_layer",
|
|
69
|
+
"detailEmpty": "F1KBNa_detailEmpty",
|
|
70
|
+
"settingsRowTitle": "F1KBNa_settingsRowTitle",
|
|
71
|
+
"pickerIcon": "F1KBNa_pickerIcon",
|
|
72
|
+
"fileList": "F1KBNa_fileList",
|
|
73
|
+
"settingsGroupChevron": "F1KBNa_settingsGroupChevron",
|
|
74
|
+
"searchCount": "F1KBNa_searchCount",
|
|
75
|
+
"splitHScrollRow": "F1KBNa_splitHScrollRow",
|
|
76
|
+
"toggleOn": "F1KBNa_toggleOn",
|
|
77
|
+
"title": "F1KBNa_title",
|
|
78
|
+
"blockFlashShake": "F1KBNa_blockFlashShake",
|
|
79
|
+
"colorHue": "F1KBNa_colorHue",
|
|
80
|
+
"rowHead": "F1KBNa_rowHead",
|
|
81
|
+
"splitRdel": "F1KBNa_splitRdel",
|
|
82
|
+
"fullscreenBackdrop": "F1KBNa_fullscreenBackdrop",
|
|
83
|
+
"diffPreview": "F1KBNa_diffPreview",
|
|
84
|
+
"diffPath": "F1KBNa_diffPath",
|
|
85
|
+
"detail": "F1KBNa_detail",
|
|
86
|
+
"importNote": "F1KBNa_importNote",
|
|
87
|
+
"colorPickerPanel": "F1KBNa_colorPickerPanel",
|
|
88
|
+
"row": "F1KBNa_row",
|
|
89
|
+
"code": "F1KBNa_code",
|
|
90
|
+
"mdPreviewDouble": "F1KBNa_mdPreviewDouble",
|
|
91
|
+
"gutter": "F1KBNa_gutter",
|
|
92
|
+
"splitLdel": "F1KBNa_splitLdel",
|
|
93
|
+
"bulkActions": "F1KBNa_bulkActions",
|
|
94
|
+
"listScroll": "F1KBNa_listScroll",
|
|
95
|
+
"pickerInput": "F1KBNa_pickerInput",
|
|
96
|
+
"stepperButtonUp": "F1KBNa_stepperButtonUp",
|
|
97
|
+
"settingsSelector": "F1KBNa_settingsSelector",
|
|
98
|
+
"toggleThumb": "F1KBNa_toggleThumb",
|
|
99
|
+
"rowPath": "F1KBNa_rowPath",
|
|
100
|
+
"rowFailed": "F1KBNa_rowFailed",
|
|
101
|
+
"stepperValue": "F1KBNa_stepperValue",
|
|
102
|
+
"addButton": "F1KBNa_addButton",
|
|
103
|
+
"searchInput": "F1KBNa_searchInput",
|
|
104
|
+
"searchMatch": "F1KBNa_searchMatch",
|
|
105
|
+
"add": "F1KBNa_add",
|
|
106
|
+
"actionPrimary": "F1KBNa_actionPrimary",
|
|
107
|
+
"rail": "F1KBNa_rail",
|
|
108
|
+
"colorSvCursor": "F1KBNa_colorSvCursor",
|
|
109
|
+
"diffStats": "F1KBNa_diffStats",
|
|
110
|
+
"intraAdd": "F1KBNa_intraAdd",
|
|
111
|
+
"colorPickerChevron": "F1KBNa_colorPickerChevron",
|
|
112
|
+
"treeNote": "F1KBNa_treeNote",
|
|
113
|
+
"headerActions": "F1KBNa_headerActions",
|
|
114
|
+
"settingsGroupBody": "F1KBNa_settingsGroupBody",
|
|
115
|
+
"settingsRowText": "F1KBNa_settingsRowText",
|
|
116
|
+
"mdPreviewFallback": "F1KBNa_mdPreviewFallback",
|
|
117
|
+
"line": "F1KBNa_line",
|
|
118
|
+
"wrap": "F1KBNa_wrap",
|
|
119
|
+
"rows": "F1KBNa_rows",
|
|
120
|
+
"missingHint": "F1KBNa_missingHint",
|
|
121
|
+
"actionQuietDisabled": "F1KBNa_actionQuietDisabled",
|
|
122
|
+
"mdPreviewContent": "F1KBNa_mdPreviewContent",
|
|
123
|
+
"group": "F1KBNa_group",
|
|
124
|
+
"badgeCount": "F1KBNa_badgeCount",
|
|
125
|
+
"context": "F1KBNa_context",
|
|
126
|
+
"del": "F1KBNa_del",
|
|
127
|
+
"flexSpacer": "F1KBNa_flexSpacer",
|
|
128
|
+
"colorHueCursor": "F1KBNa_colorHueCursor",
|
|
129
|
+
"colorSv": "F1KBNa_colorSv",
|
|
130
|
+
"confirmCard": "F1KBNa_confirmCard",
|
|
131
|
+
"kindHint": "F1KBNa_kindHint",
|
|
132
|
+
"expand": "F1KBNa_expand",
|
|
133
|
+
"pickerCheck": "F1KBNa_pickerCheck",
|
|
134
|
+
"settingsSelectorChevron": "F1KBNa_settingsSelectorChevron",
|
|
135
|
+
"colorValue": "F1KBNa_colorValue",
|
|
136
|
+
"footerButtons": "F1KBNa_footerButtons",
|
|
137
|
+
"divergedHint": "F1KBNa_divergedHint",
|
|
138
|
+
"stepperButton": "F1KBNa_stepperButton",
|
|
139
|
+
"pickerClose": "F1KBNa_pickerClose",
|
|
140
|
+
"panel": "F1KBNa_panel",
|
|
141
|
+
"importButton": "F1KBNa_importButton",
|
|
142
|
+
"searchToggle": "F1KBNa_searchToggle",
|
|
143
|
+
"diffHeader": "F1KBNa_diffHeader",
|
|
144
|
+
"markerAdd": "F1KBNa_markerAdd",
|
|
145
|
+
"colorPickerPreview": "F1KBNa_colorPickerPreview",
|
|
146
|
+
"pickerCard": "F1KBNa_pickerCard",
|
|
147
|
+
"mdPreviewWrap": "F1KBNa_mdPreviewWrap",
|
|
148
|
+
"colorPickerInputRow": "F1KBNa_colorPickerInputRow",
|
|
149
|
+
"emptyActions": "F1KBNa_emptyActions",
|
|
150
|
+
"note": "F1KBNa_note",
|
|
151
|
+
"lines": "F1KBNa_lines",
|
|
152
|
+
"colorPickerPopover": "F1KBNa_colorPickerPopover",
|
|
153
|
+
"stepper": "F1KBNa_stepper",
|
|
154
|
+
"diffFlashShake": "F1KBNa_diffFlashShake",
|
|
155
|
+
"header": "F1KBNa_header",
|
|
156
|
+
"groupHead": "F1KBNa_groupHead",
|
|
157
|
+
"resizeHandle": "F1KBNa_resizeHandle",
|
|
158
|
+
"treeCaret": "F1KBNa_treeCaret",
|
|
159
|
+
"rowMeta": "F1KBNa_rowMeta",
|
|
160
|
+
"markerDel": "F1KBNa_markerDel",
|
|
161
|
+
"vSpacer": "F1KBNa_vSpacer",
|
|
162
|
+
"fileListFloat": "F1KBNa_fileListFloat",
|
|
163
|
+
"noticeButton": "F1KBNa_noticeButton",
|
|
164
|
+
"intraDel": "F1KBNa_intraDel",
|
|
165
|
+
"mdPreviewSingle": "F1KBNa_mdPreviewSingle",
|
|
166
|
+
"settingsGroup": "F1KBNa_settingsGroup",
|
|
167
|
+
"tree": "F1KBNa_tree",
|
|
168
|
+
"searchMatchCurrent": "F1KBNa_searchMatchCurrent",
|
|
169
|
+
"diffBody": "F1KBNa_diffBody",
|
|
170
|
+
"splitRadd": "F1KBNa_splitRadd",
|
|
171
|
+
"action": "F1KBNa_action",
|
|
172
|
+
"emptyState": "F1KBNa_emptyState",
|
|
173
|
+
"confirmActions": "F1KBNa_confirmActions",
|
|
174
|
+
"overviewMarker": "F1KBNa_overviewMarker",
|
|
175
|
+
"noticeText": "F1KBNa_noticeText",
|
|
176
|
+
"actionError": "F1KBNa_actionError",
|
|
177
|
+
"diffPreviewScroll": "F1KBNa_diffPreviewScroll",
|
|
178
|
+
"diffPreviewTitle": "F1KBNa_diffPreviewTitle",
|
|
179
|
+
"blockActions": "F1KBNa_blockActions",
|
|
180
|
+
"diffFlashFade": "F1KBNa_diffFlashFade",
|
|
181
|
+
"subline": "F1KBNa_subline",
|
|
182
|
+
"langSelect": "F1KBNa_langSelect",
|
|
183
|
+
"splitCols": "F1KBNa_splitCols",
|
|
184
|
+
"colorPickerTrigger": "F1KBNa_colorPickerTrigger",
|
|
185
|
+
"readError": "F1KBNa_readError",
|
|
186
|
+
"colorPicker": "F1KBNa_colorPicker",
|
|
187
|
+
"toggle": "F1KBNa_toggle",
|
|
188
|
+
"noteCentered": "F1KBNa_noteCentered",
|
|
189
|
+
"overviewRuler": "F1KBNa_overviewRuler",
|
|
190
|
+
"hint": "F1KBNa_hint",
|
|
191
|
+
"splitDivider": "F1KBNa_splitDivider",
|
|
192
|
+
"pickerName": "F1KBNa_pickerName",
|
|
193
|
+
"langLabel": "F1KBNa_langLabel",
|
|
194
|
+
"diffBodySplit": "F1KBNa_diffBodySplit",
|
|
195
|
+
"settingsGroupHeader": "F1KBNa_settingsGroupHeader",
|
|
196
|
+
"settingsGroupDesc": "F1KBNa_settingsGroupDesc",
|
|
197
|
+
"pickerForm": "F1KBNa_pickerForm",
|
|
198
|
+
"states": "F1KBNa_states",
|
|
199
|
+
"settingsRow": "F1KBNa_settingsRow",
|
|
200
|
+
"diffBodyWrap": "F1KBNa_diffBodyWrap",
|
|
201
|
+
"confirmText": "F1KBNa_confirmText",
|
|
202
|
+
"statusBar": "F1KBNa_statusBar",
|
|
203
|
+
"colorSwatch": "F1KBNa_colorSwatch",
|
|
204
|
+
"diff": "F1KBNa_diff",
|
|
205
|
+
"colorPickerInput": "F1KBNa_colorPickerInput",
|
|
206
|
+
"settingsPage": "F1KBNa_settingsPage"
|
|
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 = {}) {
|
|
@@ -643,6 +1176,72 @@ window.__ModuleLoader__.load({
|
|
|
643
1176
|
* derivation; the panel owns rendering. The `diff` package is browser-safe.
|
|
644
1177
|
* @module dsh-diff-approval/client/whole-file-diff
|
|
645
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
|
+
}
|
|
646
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
|
|
@@ -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>`;
|
|
@@ -16227,31 +16839,25 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
16227
16839
|
/**
|
|
16228
16840
|
* Tokenize guards: the viewer must never let the synchronous JS-regex engine
|
|
16229
16841
|
* block the main thread on a hostile file. Lines above the length cap are
|
|
16230
|
-
* returned plain by shiki, the per-line budget caps a single pathological
|
|
16231
|
-
*
|
|
16232
|
-
*
|
|
16233
|
-
*
|
|
16234
|
-
*
|
|
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.
|
|
16235
16847
|
*/
|
|
16236
16848
|
const MAX_LINE_LENGTH = 2e3;
|
|
16237
16849
|
const TOKENIZE_TIME_LIMIT_MS = 100;
|
|
16238
|
-
const
|
|
16239
|
-
const MAX_HIGHLIGHT_LINES = 1e4;
|
|
16850
|
+
const MAX_WINDOW_CHARS = 5e5;
|
|
16240
16851
|
/**
|
|
16241
|
-
* Bounded tokenize cache keyed by the
|
|
16242
|
-
*
|
|
16243
|
-
*
|
|
16244
|
-
*
|
|
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).
|
|
16245
16858
|
*/
|
|
16246
|
-
const TOKENIZE_CACHE_MAX =
|
|
16859
|
+
const TOKENIZE_CACHE_MAX = 24;
|
|
16247
16860
|
const tokenizeCache = /* @__PURE__ */ new Map();
|
|
16248
|
-
/** Whether `code` is too large to highlight safely (short-circuits fast). */
|
|
16249
|
-
function tooLargeToHighlight(code) {
|
|
16250
|
-
if (code.length > MAX_HIGHLIGHT_CHARS) return true;
|
|
16251
|
-
let newlines = 0;
|
|
16252
|
-
for (let index = 0; index < code.length; index++) if (code.charCodeAt(index) === 10 && ++newlines > MAX_HIGHLIGHT_LINES) return true;
|
|
16253
|
-
return false;
|
|
16254
|
-
}
|
|
16255
16861
|
/** All grammars this bundle registers; each entry's own `name` is the tokenize id. */
|
|
16256
16862
|
const LANGS = [
|
|
16257
16863
|
c_default,
|
|
@@ -16392,43 +16998,316 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
16392
16998
|
highlighter();
|
|
16393
16999
|
}, 0).unref?.();
|
|
16394
17000
|
/**
|
|
16395
|
-
* Tokenize `
|
|
16396
|
-
* registered grammar; `undefined` means the caller renders its plain
|
|
16397
|
-
* Each run's color is a `--shiki-*` custom property, keeping token
|
|
16398
|
-
* the harness theme's sheets. The trailing newline shiki appends as a
|
|
16399
|
-
* empty line is dropped so the run count matches the caller's own
|
|
16400
|
-
*
|
|
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.
|
|
16401
17015
|
* @param lang - the Shiki grammar id, or `undefined` for plain text.
|
|
16402
|
-
* @
|
|
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.
|
|
16403
17021
|
*/
|
|
16404
|
-
function
|
|
16405
|
-
if (lang === void 0
|
|
17022
|
+
function highlightWindow(lines, lang, from, to, options = {}) {
|
|
17023
|
+
if (lang === void 0) return void 0;
|
|
16406
17024
|
if (!highlighter().getLoadedLanguages().includes(lang)) return void 0;
|
|
16407
|
-
|
|
16408
|
-
const
|
|
16409
|
-
if (
|
|
16410
|
-
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, {
|
|
16411
17040
|
lang,
|
|
16412
17041
|
theme: "css-variables",
|
|
16413
17042
|
tokenizeTimeLimit: TOKENIZE_TIME_LIMIT_MS,
|
|
16414
|
-
tokenizeMaxLineLength: MAX_LINE_LENGTH
|
|
17043
|
+
tokenizeMaxLineLength: MAX_LINE_LENGTH,
|
|
17044
|
+
...options.state === void 0 ? {} : { grammarState: options.state }
|
|
16415
17045
|
});
|
|
16416
|
-
const
|
|
16417
|
-
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) => ({
|
|
16418
17047
|
text: token.content,
|
|
16419
17048
|
style: { color: token.color }
|
|
16420
17049
|
})));
|
|
16421
|
-
|
|
16422
|
-
if (byLang === void 0) {
|
|
17050
|
+
if (cacheKey !== void 0) {
|
|
16423
17051
|
if (tokenizeCache.size >= TOKENIZE_CACHE_MAX) {
|
|
16424
17052
|
const oldest = tokenizeCache.keys().next().value;
|
|
16425
17053
|
if (oldest !== void 0) tokenizeCache.delete(oldest);
|
|
16426
17054
|
}
|
|
16427
|
-
|
|
16428
|
-
tokenizeCache.set(code, byLang);
|
|
17055
|
+
tokenizeCache.set(cacheKey, runs);
|
|
16429
17056
|
}
|
|
16430
|
-
|
|
16431
|
-
|
|
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;
|
|
16432
17311
|
}
|
|
16433
17312
|
//#endregion
|
|
16434
17313
|
//#region lib/types/client/lang.js
|
|
@@ -16487,10 +17366,19 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
16487
17366
|
* @returns the language id, or `undefined` when unknown.
|
|
16488
17367
|
*/
|
|
16489
17368
|
function langFromPath(path) {
|
|
16490
|
-
const
|
|
16491
|
-
|
|
16492
|
-
|
|
16493
|
-
|
|
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();
|
|
16494
17382
|
}
|
|
16495
17383
|
//#endregion
|
|
16496
17384
|
//#region lib/types/client/reference.js
|
|
@@ -16754,6 +17642,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
16754
17642
|
const DIFF_LINE_HEIGHT_KEY = "diff-approval:diff-line-height";
|
|
16755
17643
|
const DIFF_ADD_COLOR_KEY = "diff-approval:diff-add-color";
|
|
16756
17644
|
const DIFF_DEL_COLOR_KEY = "diff-approval:diff-del-color";
|
|
17645
|
+
const LANG_BY_SUFFIX_KEY = "diff-approval:lang-by-suffix";
|
|
16757
17646
|
const WRAP_PREFIX = "diff-approval:wrap:";
|
|
16758
17647
|
const MD_MAX_WIDTH_MAX = 1600;
|
|
16759
17648
|
/**
|
|
@@ -16910,6 +17799,49 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
16910
17799
|
return themeColor("--dsw-alias-state-error-primary", "#ef4444");
|
|
16911
17800
|
}
|
|
16912
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
|
+
/**
|
|
16913
17845
|
* Whether the whole-file diff view uses the two-column (side-by-side) layout.
|
|
16914
17846
|
* Default off (single column): the unified diff. Only an explicit `'1'` enables
|
|
16915
17847
|
* split mode.
|
|
@@ -16954,244 +17886,75 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
16954
17886
|
* How many rows of lead the diff block jump leaves above the jumped-to block,
|
|
16955
17887
|
* and how far the anchored navigation scans. Defaults to 2; an out-of-range or
|
|
16956
17888
|
* non-integer value falls back to the default.
|
|
16957
|
-
* @returns the lead row count.
|
|
16958
|
-
*/
|
|
16959
|
-
function navLeadRows() {
|
|
16960
|
-
const raw = Number.parseInt(localStorage.getItem(NAV_LEAD_KEY) ?? "", 10);
|
|
16961
|
-
if (!Number.isInteger(raw)) return 2;
|
|
16962
|
-
return Math.max(0, Math.min(10, raw));
|
|
16963
|
-
}
|
|
16964
|
-
/** Persist the block-jump lead row count. */
|
|
16965
|
-
function setNavLeadRows(value) {
|
|
16966
|
-
localStorage.setItem(NAV_LEAD_KEY, String(Math.max(0, Math.min(10, value))));
|
|
16967
|
-
}
|
|
16968
|
-
const QUICK_SUMMON_KEY = "diff-approval:quick-summon-key";
|
|
16969
|
-
/**
|
|
16970
|
-
* The quick-summon chord. Stored as `Modifier+...+Key`; falls back to
|
|
16971
|
-
* {@link DEFAULT_QUICK_SUMMON}.
|
|
16972
|
-
* @returns the chord string.
|
|
16973
|
-
*/
|
|
16974
|
-
function quickSummonKey() {
|
|
16975
|
-
return localStorage.getItem(QUICK_SUMMON_KEY) ?? "Ctrl+D";
|
|
16976
|
-
}
|
|
16977
|
-
/** Persist the quick-summon chord. */
|
|
16978
|
-
function setQuickSummonKey(value) {
|
|
16979
|
-
localStorage.setItem(QUICK_SUMMON_KEY, value);
|
|
16980
|
-
}
|
|
16981
|
-
const KEY_PREFIX = "diff-approval:key:";
|
|
16982
|
-
/** Default chord for each configurable action (every supported key except the
|
|
16983
|
-
* panel's own ESC-to-close, which is intentionally not remapped). */
|
|
16984
|
-
const DEFAULT_KEYBINDINGS = {
|
|
16985
|
-
jumpUp: "Ctrl+ArrowUp",
|
|
16986
|
-
jumpDown: "Ctrl+ArrowDown",
|
|
16987
|
-
copyRef: "Ctrl+L",
|
|
16988
|
-
openSearch: "Ctrl+F",
|
|
16989
|
-
searchNext: "F3",
|
|
16990
|
-
searchPrev: "Shift+F3",
|
|
16991
|
-
matchCase: "Alt+C",
|
|
16992
|
-
matchWholeWord: "Alt+W",
|
|
16993
|
-
undo: "Ctrl+Z",
|
|
16994
|
-
redo: "Ctrl+Shift+Z",
|
|
16995
|
-
cycleNext: "Ctrl+Tab",
|
|
16996
|
-
cyclePrev: "Ctrl+Shift+Tab"
|
|
16997
|
-
};
|
|
16998
|
-
/** The currently configured chord for one action; falls back to its default. */
|
|
16999
|
-
function keybindingOf(action) {
|
|
17000
|
-
return localStorage.getItem(`${KEY_PREFIX}${action}`) ?? DEFAULT_KEYBINDINGS[action] ?? "";
|
|
17001
|
-
}
|
|
17002
|
-
/** Persist one action's chord. */
|
|
17003
|
-
function setKeybinding(action, chord) {
|
|
17004
|
-
localStorage.setItem(`${KEY_PREFIX}${action}`, chord);
|
|
17005
|
-
}
|
|
17006
|
-
/**
|
|
17007
|
-
* Whether a keyboard event matches a chord string like `Ctrl+D`. Modifier
|
|
17008
|
-
* names are matched case-insensitively (`Ctrl`/`Control`, `Alt`/`Option`,
|
|
17009
|
-
* `Shift`, `Meta`/`Cmd`/`Command`/`Win`); the final part is the key. Exact
|
|
17010
|
-
* modifier set is required (extra modifiers do not match).
|
|
17011
|
-
* @param event - the keydown event.
|
|
17012
|
-
* @param shortcut - the chord string.
|
|
17013
|
-
* @returns whether the event matches.
|
|
17014
|
-
*/
|
|
17015
|
-
function matchesShortcut(event, shortcut) {
|
|
17016
|
-
const parts = shortcut.split("+").map((part) => part.trim().toLowerCase());
|
|
17017
|
-
const key = parts.pop();
|
|
17018
|
-
if (key === void 0 || key === "") return false;
|
|
17019
|
-
const mods = new Set(parts);
|
|
17020
|
-
const ctrl = mods.has("ctrl") || mods.has("control");
|
|
17021
|
-
const alt = mods.has("alt") || mods.has("option");
|
|
17022
|
-
const shift = mods.has("shift");
|
|
17023
|
-
const meta = mods.has("meta") || mods.has("cmd") || mods.has("command") || mods.has("win");
|
|
17024
|
-
return event.key.toLowerCase() === key && event.ctrlKey === ctrl && event.altKey === alt && event.shiftKey === shift && event.metaKey === meta;
|
|
17025
|
-
}
|
|
17026
|
-
//#endregion
|
|
17027
|
-
//#region \0dsh-css:/home/runner/work/dsh-diff-approval/dsh-diff-approval/src/client/PendingPanel.module.css.mjs
|
|
17028
|
-
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_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{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)}";
|
|
17029
|
-
const tagId = "dsh-diff-approval/PendingPanel.module.css";
|
|
17030
|
-
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
|
|
17031
|
-
const tag = document.createElement("style");
|
|
17032
|
-
tag.dataset.plugin = "dsh-diff-approval";
|
|
17033
|
-
tag.dataset.pluginCss = tagId;
|
|
17034
|
-
tag.textContent = css;
|
|
17035
|
-
document.head.appendChild(tag);
|
|
17036
|
-
}
|
|
17037
|
-
var PendingPanel_module_css_default = {
|
|
17038
|
-
"blockFlashShake": "F1KBNa_blockFlashShake",
|
|
17039
|
-
"del": "F1KBNa_del",
|
|
17040
|
-
"noticeButton": "F1KBNa_noticeButton",
|
|
17041
|
-
"settingsGroupBody": "F1KBNa_settingsGroupBody",
|
|
17042
|
-
"colorSwatch": "F1KBNa_colorSwatch",
|
|
17043
|
-
"fullscreenBackdrop": "F1KBNa_fullscreenBackdrop",
|
|
17044
|
-
"confirmText": "F1KBNa_confirmText",
|
|
17045
|
-
"diffActions": "F1KBNa_diffActions",
|
|
17046
|
-
"splitLdel": "F1KBNa_splitLdel",
|
|
17047
|
-
"toggle": "F1KBNa_toggle",
|
|
17048
|
-
"importButton": "F1KBNa_importButton",
|
|
17049
|
-
"diffPreview": "F1KBNa_diffPreview",
|
|
17050
|
-
"delCount": "F1KBNa_delCount",
|
|
17051
|
-
"addCount": "F1KBNa_addCount",
|
|
17052
|
-
"settingsGroupChevron": "F1KBNa_settingsGroupChevron",
|
|
17053
|
-
"searchToggleOn": "F1KBNa_searchToggleOn",
|
|
17054
|
-
"settingsGroup": "F1KBNa_settingsGroup",
|
|
17055
|
-
"colorHue": "F1KBNa_colorHue",
|
|
17056
|
-
"searchCount": "F1KBNa_searchCount",
|
|
17057
|
-
"searchMatchCurrent": "F1KBNa_searchMatchCurrent",
|
|
17058
|
-
"lines": "F1KBNa_lines",
|
|
17059
|
-
"mdPreviewContent": "F1KBNa_mdPreviewContent",
|
|
17060
|
-
"rowMeta": "F1KBNa_rowMeta",
|
|
17061
|
-
"expandExpanded": "F1KBNa_expandExpanded",
|
|
17062
|
-
"listScroll": "F1KBNa_listScroll",
|
|
17063
|
-
"kindHint": "F1KBNa_kindHint",
|
|
17064
|
-
"fileList": "F1KBNa_fileList",
|
|
17065
|
-
"settingsGroupHeader": "F1KBNa_settingsGroupHeader",
|
|
17066
|
-
"divergedHint": "F1KBNa_divergedHint",
|
|
17067
|
-
"actionQuietDisabled": "F1KBNa_actionQuietDisabled",
|
|
17068
|
-
"colorPickerPopover": "F1KBNa_colorPickerPopover",
|
|
17069
|
-
"context": "F1KBNa_context",
|
|
17070
|
-
"confirmActions": "F1KBNa_confirmActions",
|
|
17071
|
-
"markerAdd": "F1KBNa_markerAdd",
|
|
17072
|
-
"searchInput": "F1KBNa_searchInput",
|
|
17073
|
-
"toggleOn": "F1KBNa_toggleOn",
|
|
17074
|
-
"header": "F1KBNa_header",
|
|
17075
|
-
"colorValue": "F1KBNa_colorValue",
|
|
17076
|
-
"split": "F1KBNa_split",
|
|
17077
|
-
"colorBackdrop": "F1KBNa_colorBackdrop",
|
|
17078
|
-
"settingsRowTitle": "F1KBNa_settingsRowTitle",
|
|
17079
|
-
"diffPath": "F1KBNa_diffPath",
|
|
17080
|
-
"statusBar": "F1KBNa_statusBar",
|
|
17081
|
-
"group": "F1KBNa_group",
|
|
17082
|
-
"colorSv": "F1KBNa_colorSv",
|
|
17083
|
-
"diffFlashFade": "F1KBNa_diffFlashFade",
|
|
17084
|
-
"detailEmpty": "F1KBNa_detailEmpty",
|
|
17085
|
-
"confirmCard": "F1KBNa_confirmCard",
|
|
17086
|
-
"badge": "F1KBNa_badge",
|
|
17087
|
-
"splitRdel": "F1KBNa_splitRdel",
|
|
17088
|
-
"line": "F1KBNa_line",
|
|
17089
|
-
"diffBody": "F1KBNa_diffBody",
|
|
17090
|
-
"stepperValue": "F1KBNa_stepperValue",
|
|
17091
|
-
"stepperButton": "F1KBNa_stepperButton",
|
|
17092
|
-
"settingsSelector": "F1KBNa_settingsSelector",
|
|
17093
|
-
"colorHueCursor": "F1KBNa_colorHueCursor",
|
|
17094
|
-
"badgeLabel": "F1KBNa_badgeLabel",
|
|
17095
|
-
"panel": "F1KBNa_panel",
|
|
17096
|
-
"diffPreviewScroll": "F1KBNa_diffPreviewScroll",
|
|
17097
|
-
"fileListFloat": "F1KBNa_fileListFloat",
|
|
17098
|
-
"note": "F1KBNa_note",
|
|
17099
|
-
"diff": "F1KBNa_diff",
|
|
17100
|
-
"mdPreviewWrap": "F1KBNa_mdPreviewWrap",
|
|
17101
|
-
"blockFlash": "F1KBNa_blockFlash",
|
|
17102
|
-
"vSpacer": "F1KBNa_vSpacer",
|
|
17103
|
-
"hint": "F1KBNa_hint",
|
|
17104
|
-
"settingsRowDesc": "F1KBNa_settingsRowDesc",
|
|
17105
|
-
"gutter": "F1KBNa_gutter",
|
|
17106
|
-
"intraAdd": "F1KBNa_intraAdd",
|
|
17107
|
-
"colorPickerTrigger": "F1KBNa_colorPickerTrigger",
|
|
17108
|
-
"splitCol": "F1KBNa_splitCol",
|
|
17109
|
-
"resizeHandle": "F1KBNa_resizeHandle",
|
|
17110
|
-
"detail": "F1KBNa_detail",
|
|
17111
|
-
"diffBodySplit": "F1KBNa_diffBodySplit",
|
|
17112
|
-
"missing": "F1KBNa_missing",
|
|
17113
|
-
"bulkActions": "F1KBNa_bulkActions",
|
|
17114
|
-
"close": "F1KBNa_close",
|
|
17115
|
-
"colorPickerChevron": "F1KBNa_colorPickerChevron",
|
|
17116
|
-
"diffStats": "F1KBNa_diffStats",
|
|
17117
|
-
"badgeCount": "F1KBNa_badgeCount",
|
|
17118
|
-
"colorPickerPreview": "F1KBNa_colorPickerPreview",
|
|
17119
|
-
"divider": "F1KBNa_divider",
|
|
17120
|
-
"stepper": "F1KBNa_stepper",
|
|
17121
|
-
"splitDivider": "F1KBNa_splitDivider",
|
|
17122
|
-
"readError": "F1KBNa_readError",
|
|
17123
|
-
"title": "F1KBNa_title",
|
|
17124
|
-
"overviewMarker": "F1KBNa_overviewMarker",
|
|
17125
|
-
"statusAction": "F1KBNa_statusAction",
|
|
17126
|
-
"mdPreviewFallback": "F1KBNa_mdPreviewFallback",
|
|
17127
|
-
"code": "F1KBNa_code",
|
|
17128
|
-
"settingsRowText": "F1KBNa_settingsRowText",
|
|
17129
|
-
"splitLadd": "F1KBNa_splitLadd",
|
|
17130
|
-
"toggleThumb": "F1KBNa_toggleThumb",
|
|
17131
|
-
"missingHint": "F1KBNa_missingHint",
|
|
17132
|
-
"blockPosition": "F1KBNa_blockPosition",
|
|
17133
|
-
"settingsSelectorChevron": "F1KBNa_settingsSelectorChevron",
|
|
17134
|
-
"settingsGroupText": "F1KBNa_settingsGroupText",
|
|
17135
|
-
"colorPickerInputRow": "F1KBNa_colorPickerInputRow",
|
|
17136
|
-
"diffBodyWrap": "F1KBNa_diffBodyWrap",
|
|
17137
|
-
"rail": "F1KBNa_rail",
|
|
17138
|
-
"colorPicker": "F1KBNa_colorPicker",
|
|
17139
|
-
"mdPreviewBody": "F1KBNa_mdPreviewBody",
|
|
17140
|
-
"action": "F1KBNa_action",
|
|
17141
|
-
"expand": "F1KBNa_expand",
|
|
17142
|
-
"markerDel": "F1KBNa_markerDel",
|
|
17143
|
-
"splitHScrollFill": "F1KBNa_splitHScrollFill",
|
|
17144
|
-
"langLabel": "F1KBNa_langLabel",
|
|
17145
|
-
"importNote": "F1KBNa_importNote",
|
|
17146
|
-
"wrap": "F1KBNa_wrap",
|
|
17147
|
-
"subline": "F1KBNa_subline",
|
|
17148
|
-
"colorPickerInput": "F1KBNa_colorPickerInput",
|
|
17149
|
-
"colorPickerPanel": "F1KBNa_colorPickerPanel",
|
|
17150
|
-
"mdPreviewDouble": "F1KBNa_mdPreviewDouble",
|
|
17151
|
-
"settingsRow": "F1KBNa_settingsRow",
|
|
17152
|
-
"noteCentered": "F1KBNa_noteCentered",
|
|
17153
|
-
"add": "F1KBNa_add",
|
|
17154
|
-
"notice": "F1KBNa_notice",
|
|
17155
|
-
"emptyState": "F1KBNa_emptyState",
|
|
17156
|
-
"kindTag": "F1KBNa_kindTag",
|
|
17157
|
-
"diffFlashShake": "F1KBNa_diffFlashShake",
|
|
17158
|
-
"splitCols": "F1KBNa_splitCols",
|
|
17159
|
-
"searchBar": "F1KBNa_searchBar",
|
|
17160
|
-
"confirmBackdrop": "F1KBNa_confirmBackdrop",
|
|
17161
|
-
"searchToggle": "F1KBNa_searchToggle",
|
|
17162
|
-
"noticeText": "F1KBNa_noticeText",
|
|
17163
|
-
"splitRoot": "F1KBNa_splitRoot",
|
|
17164
|
-
"intraDel": "F1KBNa_intraDel",
|
|
17165
|
-
"rows": "F1KBNa_rows",
|
|
17166
|
-
"mdPreviewSingle": "F1KBNa_mdPreviewSingle",
|
|
17167
|
-
"colorSvCursor": "F1KBNa_colorSvCursor",
|
|
17168
|
-
"wrapActive": "F1KBNa_wrapActive",
|
|
17169
|
-
"states": "F1KBNa_states",
|
|
17170
|
-
"settingsGroupTitle": "F1KBNa_settingsGroupTitle",
|
|
17171
|
-
"footerButtons": "F1KBNa_footerButtons",
|
|
17172
|
-
"rowFailed": "F1KBNa_rowFailed",
|
|
17173
|
-
"searchMatch": "F1KBNa_searchMatch",
|
|
17174
|
-
"splitHScroll": "F1KBNa_splitHScroll",
|
|
17175
|
-
"actionPrimary": "F1KBNa_actionPrimary",
|
|
17176
|
-
"diffHeader": "F1KBNa_diffHeader",
|
|
17177
|
-
"settingsGroupDesc": "F1KBNa_settingsGroupDesc",
|
|
17178
|
-
"splitRadd": "F1KBNa_splitRadd",
|
|
17179
|
-
"settingsPage": "F1KBNa_settingsPage",
|
|
17180
|
-
"stepperButtonUp": "F1KBNa_stepperButtonUp",
|
|
17181
|
-
"rowHead": "F1KBNa_rowHead",
|
|
17182
|
-
"splitHScrollRow": "F1KBNa_splitHScrollRow",
|
|
17183
|
-
"diffPreviewTitle": "F1KBNa_diffPreviewTitle",
|
|
17184
|
-
"blockActions": "F1KBNa_blockActions",
|
|
17185
|
-
"rowPath": "F1KBNa_rowPath",
|
|
17186
|
-
"actionError": "F1KBNa_actionError",
|
|
17187
|
-
"flexSpacer": "F1KBNa_flexSpacer",
|
|
17188
|
-
"iconAction": "F1KBNa_iconAction",
|
|
17189
|
-
"overviewRuler": "F1KBNa_overviewRuler",
|
|
17190
|
-
"langSelect": "F1KBNa_langSelect",
|
|
17191
|
-
"headerActions": "F1KBNa_headerActions",
|
|
17192
|
-
"layer": "F1KBNa_layer",
|
|
17193
|
-
"row": "F1KBNa_row"
|
|
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"
|
|
17194
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
|
+
}
|
|
17195
17958
|
//#endregion
|
|
17196
17959
|
//#region lib/types/client/PendingPanel.js
|
|
17197
17960
|
/** Sidebar-foot pending-edit review action and the split review panel it opens. */
|
|
@@ -17215,8 +17978,11 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
17215
17978
|
const COMPOSER_HEIGHT_VAR = "--dsh-composer-height";
|
|
17216
17979
|
/** Interactive composer/approval cards; clicking these keeps the panel open.
|
|
17217
17980
|
Deliberately the cards themselves, not the seat: the approval frame's wide
|
|
17218
|
-
side gutters are blank space, so a click there must still close.
|
|
17219
|
-
|
|
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\"]";
|
|
17220
17986
|
/** Seat counts as docked when its bottom is this close to the window bottom. */
|
|
17221
17987
|
const DOCKED_TOLERANCE_PX = 48;
|
|
17222
17988
|
/** File-list pane width bounds for the manual split drag, in px. */
|
|
@@ -17468,6 +18234,10 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
17468
18234
|
* 5px frame padding on each side + 1px border on each side, plus a little
|
|
17469
18235
|
* breathing room so the bottom padding never sits flush against it. */
|
|
17470
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;
|
|
17471
18241
|
/** The trailing file-name segment of a path, used for row display. */
|
|
17472
18242
|
function basenameOf(path) {
|
|
17473
18243
|
const index = Math.max(path.lastIndexOf("/"), path.lastIndexOf("\\"));
|
|
@@ -17799,69 +18569,6 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
17799
18569
|
]
|
|
17800
18570
|
});
|
|
17801
18571
|
});
|
|
17802
|
-
/**
|
|
17803
|
-
* One diff block's old/new line ranges, 1-based inclusive, for block-level
|
|
17804
|
-
* keep/revert. A side with no lines (a pure addition or deletion) is empty;
|
|
17805
|
-
* its start is that side's insertion point — the line after the surrounding
|
|
17806
|
-
* context — so the host can insert there.
|
|
17807
|
-
* @param rows - the whole-file diff rows.
|
|
17808
|
-
* @param block - the block's row range.
|
|
17809
|
-
* @returns the old and new line ranges.
|
|
17810
|
-
*/
|
|
17811
|
-
function blockRangesOf(rows, block) {
|
|
17812
|
-
let oldStart = Infinity;
|
|
17813
|
-
let oldEnd = -Infinity;
|
|
17814
|
-
let newStart = Infinity;
|
|
17815
|
-
let newEnd = -Infinity;
|
|
17816
|
-
for (let index = block.start; index <= block.end; index++) {
|
|
17817
|
-
const row = rows[index];
|
|
17818
|
-
if (row === void 0) continue;
|
|
17819
|
-
if (row.oldLine !== void 0) {
|
|
17820
|
-
oldStart = Math.min(oldStart, row.oldLine);
|
|
17821
|
-
oldEnd = Math.max(oldEnd, row.oldLine);
|
|
17822
|
-
}
|
|
17823
|
-
if (row.newLine !== void 0) {
|
|
17824
|
-
newStart = Math.min(newStart, row.newLine);
|
|
17825
|
-
newEnd = Math.max(newEnd, row.newLine);
|
|
17826
|
-
}
|
|
17827
|
-
}
|
|
17828
|
-
const before = rows[block.start - 1];
|
|
17829
|
-
if (oldStart === Infinity) {
|
|
17830
|
-
oldStart = (before?.oldLine ?? 0) + 1;
|
|
17831
|
-
oldEnd = oldStart - 1;
|
|
17832
|
-
}
|
|
17833
|
-
if (newStart === Infinity) {
|
|
17834
|
-
newStart = (before?.newLine ?? 0) + 1;
|
|
17835
|
-
newEnd = newStart - 1;
|
|
17836
|
-
}
|
|
17837
|
-
return {
|
|
17838
|
-
oldStart,
|
|
17839
|
-
oldEnd,
|
|
17840
|
-
newStart,
|
|
17841
|
-
newEnd
|
|
17842
|
-
};
|
|
17843
|
-
}
|
|
17844
|
-
/** Split a row list into maximal runs of non-context rows. */
|
|
17845
|
-
function changeBlocksOf(diff) {
|
|
17846
|
-
const blocks = [];
|
|
17847
|
-
let start = -1;
|
|
17848
|
-
diff.rows.forEach((row, index) => {
|
|
17849
|
-
if (row.kind !== "context") {
|
|
17850
|
-
if (start === -1) start = index;
|
|
17851
|
-
} else if (start !== -1) {
|
|
17852
|
-
blocks.push({
|
|
17853
|
-
start,
|
|
17854
|
-
end: index - 1
|
|
17855
|
-
});
|
|
17856
|
-
start = -1;
|
|
17857
|
-
}
|
|
17858
|
-
});
|
|
17859
|
-
if (start !== -1) blocks.push({
|
|
17860
|
-
start,
|
|
17861
|
-
end: diff.rows.length - 1
|
|
17862
|
-
});
|
|
17863
|
-
return blocks;
|
|
17864
|
-
}
|
|
17865
18572
|
/** Whether a block keep/revert range covers the file's entire change region, so
|
|
17866
18573
|
* applying it leaves the file with no pending diff — the "remove or keep in
|
|
17867
18574
|
* list" prompt applies. This generalises the single-block case to a selection
|
|
@@ -17892,6 +18599,42 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
17892
18599
|
for (let i = 0; i < rows.length; i++) if (matchRangesOf(rows[i].text, query, options).length > 0) out.push(i);
|
|
17893
18600
|
return out;
|
|
17894
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
|
+
}
|
|
17895
18638
|
/** One side's line-content for the split view: the highlighted runs or plain text. */
|
|
17896
18639
|
function splitSideContent(side, wrapped, runs, intra, options, query, current) {
|
|
17897
18640
|
if (side === void 0) return "";
|
|
@@ -17940,7 +18683,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
17940
18683
|
});
|
|
17941
18684
|
}
|
|
17942
18685
|
/** The two-column (side-by-side) whole-file diff view. */
|
|
17943
|
-
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) {
|
|
17944
18687
|
const ROW_HEIGHT_PX = diffLineHeight();
|
|
17945
18688
|
const NAV_ANCHOR_TOLERANCE_PX = ROW_HEIGHT_PX / 4;
|
|
17946
18689
|
const { pairs, pairOfRow } = (0, react.useMemo)(() => computeSideBySideDiff(model.diff.rows, true), [model]);
|
|
@@ -18230,6 +18973,19 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
18230
18973
|
end = Math.max(end, selection.end + 1);
|
|
18231
18974
|
}
|
|
18232
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
|
+
]);
|
|
18233
18989
|
const wrapArmedRef = (0, react.useRef)(0);
|
|
18234
18990
|
const jump = (direction, wrapGuard = false, singleToast = wrapGuard) => {
|
|
18235
18991
|
const count = blockOfPair.length;
|
|
@@ -18694,6 +19450,21 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
18694
19450
|
function codeCellAt(node) {
|
|
18695
19451
|
return ((node instanceof Element ? node : node.parentElement)?.closest("[data-diff-row], [data-diff-split-row]"))?.querySelector("[data-diff-code]") ?? null;
|
|
18696
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
|
+
}
|
|
18697
19468
|
/** Character offset of a selection boundary within its line's code text. A
|
|
18698
19469
|
* boundary outside the code cell (the line-number gutter) sits at the line's
|
|
18699
19470
|
* start — offset 0, never the line's end — so it never skips the line. */
|
|
@@ -18873,8 +19644,11 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
18873
19644
|
label: languageDisplayName(language)
|
|
18874
19645
|
}))], [t]);
|
|
18875
19646
|
const detectedLang = (0, react.useMemo)(() => langFromPath(file.path), [file.path]);
|
|
18876
|
-
const
|
|
18877
|
-
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);
|
|
18878
19652
|
const wrapKey = lang ?? "";
|
|
18879
19653
|
const [langWrap, setLangWrap] = (0, react.useState)(() => wrapEnabled(wrapKey));
|
|
18880
19654
|
(0, react.useEffect)(() => {
|
|
@@ -18888,6 +19662,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
18888
19662
|
const [tabWidthSpaces] = (0, react.useState)(() => tabWidth());
|
|
18889
19663
|
const [splitView, setSplitView] = (0, react.useState)(() => splitMode());
|
|
18890
19664
|
const [mdPreview, setMdPreview] = (0, react.useState)(() => mdPreviewEnabled());
|
|
19665
|
+
const previewActive = mdPreview && lang === "markdown";
|
|
18891
19666
|
const mdPreviewBodyRef = (0, react.useRef)(null);
|
|
18892
19667
|
const [mdRulerMarkers, setMdRulerMarkers] = (0, react.useState)([]);
|
|
18893
19668
|
const [mdImageTick, setMdImageTick] = (0, react.useState)(0);
|
|
@@ -18929,12 +19704,12 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
18929
19704
|
]);
|
|
18930
19705
|
const splitPairs = (0, react.useMemo)(() => splitView ? computeSideBySideDiff(model.diff.rows, true).pairs : null, [splitView, model]);
|
|
18931
19706
|
(0, react.useEffect)(() => {
|
|
18932
|
-
if (!
|
|
19707
|
+
if (!previewActive) return;
|
|
18933
19708
|
const body = mdPreviewBodyRef.current;
|
|
18934
19709
|
if (body === null) return;
|
|
18935
19710
|
resolvePreviewImages(body, file.path, workspacePath, (path) => onPreviewImage(file.sessionId, path)).then(() => setMdImageTick((tick) => tick + 1));
|
|
18936
19711
|
}, [
|
|
18937
|
-
|
|
19712
|
+
previewActive,
|
|
18938
19713
|
splitView,
|
|
18939
19714
|
file.path,
|
|
18940
19715
|
file.sessionId,
|
|
@@ -18977,37 +19752,31 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
18977
19752
|
return markers;
|
|
18978
19753
|
}, [model]);
|
|
18979
19754
|
(0, react.useLayoutEffect)(() => {
|
|
18980
|
-
if (!
|
|
19755
|
+
if (!previewActive) return;
|
|
18981
19756
|
const body = mdPreviewBodyRef.current;
|
|
18982
19757
|
if (body === null) return;
|
|
18983
19758
|
const measured = markdownPreviewMarkers(body);
|
|
18984
19759
|
setMdRulerMarkers(measured.length > 0 ? measured : rulerMarkers);
|
|
18985
19760
|
}, [
|
|
18986
|
-
|
|
19761
|
+
previewActive,
|
|
18987
19762
|
splitView,
|
|
18988
19763
|
file.oldText,
|
|
18989
19764
|
file.newText,
|
|
18990
19765
|
mdImageTick,
|
|
18991
19766
|
rulerMarkers
|
|
18992
19767
|
]);
|
|
18993
|
-
const
|
|
18994
|
-
(0, react.
|
|
18995
|
-
|
|
18996
|
-
|
|
18997
|
-
|
|
18998
|
-
|
|
18999
|
-
newRuns: highlightLines(file.newText, lang)
|
|
19000
|
-
});
|
|
19001
|
-
}, 0);
|
|
19002
|
-
return () => {
|
|
19003
|
-
window.clearTimeout(timer);
|
|
19004
|
-
};
|
|
19005
|
-
}, [
|
|
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
|
+
}), [
|
|
19006
19774
|
file.id,
|
|
19775
|
+
lang,
|
|
19007
19776
|
file.oldText,
|
|
19008
|
-
file.newText
|
|
19009
|
-
lang
|
|
19777
|
+
file.newText
|
|
19010
19778
|
]);
|
|
19779
|
+
const [splitVisible, setSplitVisible] = (0, react.useState)(void 0);
|
|
19011
19780
|
const bodyRef = (0, react.useRef)(null);
|
|
19012
19781
|
const [focus, setFocus] = (0, react.useState)(0);
|
|
19013
19782
|
const [scrollTick, setScrollTick] = (0, react.useState)(0);
|
|
@@ -19017,6 +19786,18 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
19017
19786
|
const [hScrollbarPx, setHScrollbarPx] = (0, react.useState)(0);
|
|
19018
19787
|
const [hoveredBlock, setHoveredBlock] = (0, react.useState)(void 0);
|
|
19019
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);
|
|
19020
19801
|
const selectionTextRef = (0, react.useRef)("");
|
|
19021
19802
|
const [copied, setCopied] = (0, react.useState)(false);
|
|
19022
19803
|
const [searchOpen, setSearchOpen] = (0, react.useState)(false);
|
|
@@ -19029,6 +19810,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
19029
19810
|
const bumpFlash = (shake) => {
|
|
19030
19811
|
pinShakeRef.current = shake;
|
|
19031
19812
|
setFlashKey((prev) => prev + 1);
|
|
19813
|
+
previewFlashPendingRef.current = true;
|
|
19032
19814
|
};
|
|
19033
19815
|
(0, react.useEffect)(() => {
|
|
19034
19816
|
setFocus(0);
|
|
@@ -19054,39 +19836,353 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
19054
19836
|
return model.blocks.map((block) => blockRangesOf(model.diff.rows, block));
|
|
19055
19837
|
}, [model]);
|
|
19056
19838
|
const coveredBlockIndices = (0, react.useMemo)(() => {
|
|
19839
|
+
if (previewActive) return previewCovered;
|
|
19057
19840
|
if (selection === void 0 || splitView) return [];
|
|
19058
19841
|
const covered = [];
|
|
19059
19842
|
for (let index = 0; index < model.blocks.length; index++) {
|
|
19060
19843
|
const block = model.blocks[index];
|
|
19061
19844
|
if (selection.start <= block.start && block.end <= selection.end) covered.push(index);
|
|
19062
19845
|
}
|
|
19063
|
-
return covered;
|
|
19846
|
+
return covered;
|
|
19847
|
+
}, [
|
|
19848
|
+
selection,
|
|
19849
|
+
splitView,
|
|
19850
|
+
model,
|
|
19851
|
+
mdPreview,
|
|
19852
|
+
lang,
|
|
19853
|
+
previewCovered
|
|
19854
|
+
]);
|
|
19855
|
+
const selectionRange = (0, react.useMemo)(() => {
|
|
19856
|
+
if (coveredBlockIndices.length === 0) return void 0;
|
|
19857
|
+
const firstIndex = coveredBlockIndices[0];
|
|
19858
|
+
const lastIndex = coveredBlockIndices[coveredBlockIndices.length - 1];
|
|
19859
|
+
if (firstIndex === void 0 || lastIndex === void 0) return void 0;
|
|
19860
|
+
const first = model.blocks[firstIndex];
|
|
19861
|
+
const last = model.blocks[lastIndex];
|
|
19862
|
+
if (first === void 0 || last === void 0) return void 0;
|
|
19863
|
+
return blockRangesOf(model.diff.rows, {
|
|
19864
|
+
start: first.start,
|
|
19865
|
+
end: last.end
|
|
19866
|
+
});
|
|
19867
|
+
}, [coveredBlockIndices, model]);
|
|
19868
|
+
const searchMatches = (0, react.useMemo)(() => matchingRows(model.diff.rows, searchQuery, search.options), [
|
|
19869
|
+
model,
|
|
19870
|
+
searchQuery,
|
|
19871
|
+
search.options
|
|
19872
|
+
]);
|
|
19873
|
+
const searchHitSet = (0, react.useMemo)(() => new Set(searchMatches), [searchMatches]);
|
|
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
|
+
}
|
|
19064
20154
|
}, [
|
|
19065
|
-
|
|
19066
|
-
|
|
19067
|
-
|
|
20155
|
+
previewActive,
|
|
20156
|
+
searchQuery,
|
|
20157
|
+
search.options,
|
|
20158
|
+
file.oldText,
|
|
20159
|
+
file.newText,
|
|
20160
|
+
splitView
|
|
19068
20161
|
]);
|
|
19069
|
-
|
|
19070
|
-
|
|
19071
|
-
|
|
19072
|
-
const
|
|
19073
|
-
|
|
19074
|
-
|
|
19075
|
-
|
|
19076
|
-
|
|
19077
|
-
return blockRangesOf(model.diff.rows, {
|
|
19078
|
-
start: first.start,
|
|
19079
|
-
end: last.end
|
|
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");
|
|
19080
20170
|
});
|
|
19081
|
-
}, [
|
|
19082
|
-
|
|
19083
|
-
|
|
19084
|
-
|
|
19085
|
-
search.options
|
|
20171
|
+
}, [
|
|
20172
|
+
previewActive,
|
|
20173
|
+
previewHitCount,
|
|
20174
|
+
searchIndex
|
|
19086
20175
|
]);
|
|
19087
|
-
const searchHitSet = (0, react.useMemo)(() => new Set(searchMatches), [searchMatches]);
|
|
19088
|
-
const currentSearchRow = searchMatches.length === 0 ? void 0 : searchMatches[searchIndex % searchMatches.length];
|
|
19089
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
|
+
}
|
|
19090
20186
|
if (searchMatches.length === 0) return;
|
|
19091
20187
|
if (cursorPosRef.current !== void 0) {
|
|
19092
20188
|
setSearchIndex(startIndexFor(searchQuery));
|
|
@@ -19122,14 +20218,17 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
19122
20218
|
}
|
|
19123
20219
|
search.sync();
|
|
19124
20220
|
const live = window.getSelection();
|
|
19125
|
-
const liveRange = splitView ? splitRowRangeOf(live) : rowRangeOf(live);
|
|
20221
|
+
const liveRange = splitView && !previewActive ? splitRowRangeOf(live) : rowRangeOf(live);
|
|
19126
20222
|
const pos = liveRange !== void 0 ? liveRange : selection;
|
|
19127
20223
|
cursorPosRef.current = pos;
|
|
19128
20224
|
lastRecordedCursorRef.current = pos;
|
|
19129
20225
|
const liveText = (live?.toString() ?? "").trim();
|
|
19130
20226
|
const value = liveText !== "" && !liveText.includes("\n") ? liveText : selectionTextRef.current;
|
|
19131
20227
|
setSearchQuery(value);
|
|
19132
|
-
|
|
20228
|
+
if (previewActive) {
|
|
20229
|
+
previewSearchAnchorRef.current = true;
|
|
20230
|
+
setSearchIndex(0);
|
|
20231
|
+
} else setSearchIndex(startIndexFor(value));
|
|
19133
20232
|
setSearchOpen(true);
|
|
19134
20233
|
requestAnimationFrame(() => {
|
|
19135
20234
|
searchInputRef.current?.focus();
|
|
@@ -19148,7 +20247,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
19148
20247
|
searchInputRef.current?.focus();
|
|
19149
20248
|
};
|
|
19150
20249
|
const toggleSearch = () => {
|
|
19151
|
-
if (splitView) {
|
|
20250
|
+
if (splitView && !previewActive) {
|
|
19152
20251
|
splitDiffRef.current?.toggleSearch();
|
|
19153
20252
|
return;
|
|
19154
20253
|
}
|
|
@@ -19254,6 +20353,28 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
19254
20353
|
end = Math.max(end, selection.end + 1);
|
|
19255
20354
|
}
|
|
19256
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
|
+
});
|
|
19257
20378
|
const blockEnd = hoveredBlock === void 0 ? void 0 : model.blocks[hoveredBlock]?.end;
|
|
19258
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)));
|
|
19259
20380
|
const selectionBlockEnd = (() => {
|
|
@@ -19283,7 +20404,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
19283
20404
|
}, [
|
|
19284
20405
|
file.id,
|
|
19285
20406
|
splitView,
|
|
19286
|
-
|
|
20407
|
+
previewActive
|
|
19287
20408
|
]);
|
|
19288
20409
|
(0, react.useEffect)(() => {
|
|
19289
20410
|
const body = bodyRef.current;
|
|
@@ -19301,12 +20422,20 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
19301
20422
|
}, [
|
|
19302
20423
|
file.id,
|
|
19303
20424
|
langWrap,
|
|
19304
|
-
|
|
20425
|
+
previewActive
|
|
19305
20426
|
]);
|
|
19306
20427
|
(0, react.useLayoutEffect)(() => {
|
|
19307
20428
|
if (rowCount === 0) return;
|
|
19308
20429
|
const block = model.blocks[focus];
|
|
19309
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
|
+
}
|
|
19310
20439
|
const body = bodyRef.current;
|
|
19311
20440
|
if (body === null) return;
|
|
19312
20441
|
const target = offsetOf(block.start) - leadRows * ROW_HEIGHT_PX;
|
|
@@ -19316,7 +20445,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
19316
20445
|
}, [
|
|
19317
20446
|
scrollTick,
|
|
19318
20447
|
rowOffsets === null,
|
|
19319
|
-
|
|
20448
|
+
previewActive
|
|
19320
20449
|
]);
|
|
19321
20450
|
const wrapArmedRef = (0, react.useRef)(0);
|
|
19322
20451
|
const jump = (direction, wrapGuard = false, singleToast = wrapGuard) => {
|
|
@@ -19336,7 +20465,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
19336
20465
|
wrapArmedRef.current = 0;
|
|
19337
20466
|
} else wrapArmedRef.current = 0;
|
|
19338
20467
|
}
|
|
19339
|
-
|
|
20468
|
+
const targetOf = (current) => {
|
|
19340
20469
|
if (direction === -1) return (current - 1 + count) % count;
|
|
19341
20470
|
const top = bodyRef.current?.scrollTop ?? 0;
|
|
19342
20471
|
for (let index = current + 1; index < count; index++) {
|
|
@@ -19345,12 +20474,14 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
19345
20474
|
if (offsetOf(block.start) >= top) return index;
|
|
19346
20475
|
}
|
|
19347
20476
|
return 0;
|
|
19348
|
-
}
|
|
20477
|
+
};
|
|
20478
|
+
const landed = targetOf(focus);
|
|
20479
|
+
setFocus(landed);
|
|
19349
20480
|
setScrollTick((tick) => tick + 1);
|
|
19350
20481
|
bumpFlash(false);
|
|
19351
20482
|
};
|
|
19352
20483
|
const jumpBlock = (direction, wrapGuard = false, singleToast = wrapGuard) => {
|
|
19353
|
-
if (splitView) {
|
|
20484
|
+
if (splitView && !previewActive) {
|
|
19354
20485
|
splitDiffRef.current?.jump(direction, wrapGuard, singleToast);
|
|
19355
20486
|
return;
|
|
19356
20487
|
}
|
|
@@ -19389,6 +20520,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
19389
20520
|
if (firstCovered === void 0) return;
|
|
19390
20521
|
await runBlockAction(action, selectionRange, firstCovered);
|
|
19391
20522
|
setSelection(void 0);
|
|
20523
|
+
setPreviewCovered([]);
|
|
19392
20524
|
window.getSelection()?.removeAllRanges?.();
|
|
19393
20525
|
};
|
|
19394
20526
|
(0, react.useEffect)(() => {
|
|
@@ -19419,6 +20551,11 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
19419
20551
|
};
|
|
19420
20552
|
(0, react.useEffect)(() => {
|
|
19421
20553
|
const update = () => {
|
|
20554
|
+
if (previewActive) {
|
|
20555
|
+
setPreviewCovered(coveredPreviewBlocks());
|
|
20556
|
+
selectionTextRef.current = "";
|
|
20557
|
+
return;
|
|
20558
|
+
}
|
|
19422
20559
|
const live = window.getSelection();
|
|
19423
20560
|
const range = splitView ? splitRowRangeOf(live) : rowRangeOf(live);
|
|
19424
20561
|
let text = "";
|
|
@@ -19440,7 +20577,12 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
19440
20577
|
return () => {
|
|
19441
20578
|
document.removeEventListener("selectionchange", update);
|
|
19442
20579
|
};
|
|
19443
|
-
}, [
|
|
20580
|
+
}, [
|
|
20581
|
+
file.id,
|
|
20582
|
+
splitView,
|
|
20583
|
+
mdPreview,
|
|
20584
|
+
lang
|
|
20585
|
+
]);
|
|
19444
20586
|
(0, react.useEffect)(() => {
|
|
19445
20587
|
const onCopy = (event) => {
|
|
19446
20588
|
const anchor = window.getSelection()?.anchorNode;
|
|
@@ -19508,8 +20650,9 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
19508
20650
|
(0, react.useEffect)(() => {
|
|
19509
20651
|
const onKeyDown = (event) => {
|
|
19510
20652
|
if (!matchesShortcut(event, keybindingOf("openSearch"))) return;
|
|
20653
|
+
if (pathPickerOpen()) return;
|
|
19511
20654
|
event.preventDefault();
|
|
19512
|
-
if (splitView) {
|
|
20655
|
+
if (splitView && !previewActive) {
|
|
19513
20656
|
splitDiffRef.current?.openSearch();
|
|
19514
20657
|
return;
|
|
19515
20658
|
}
|
|
@@ -19527,11 +20670,11 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
19527
20670
|
if (matchesShortcut(event, keybindingOf("searchNext"))) direction = 1;
|
|
19528
20671
|
else if (matchesShortcut(event, keybindingOf("searchPrev"))) direction = -1;
|
|
19529
20672
|
if (direction === 0) return;
|
|
19530
|
-
if (splitView) {
|
|
20673
|
+
if (splitView && !previewActive) {
|
|
19531
20674
|
if (splitDiffRef.current?.searchNext(direction)) event.preventDefault();
|
|
19532
20675
|
return;
|
|
19533
20676
|
}
|
|
19534
|
-
if (searchOpen &&
|
|
20677
|
+
if (searchOpen && searchMatchCount > 0) {
|
|
19535
20678
|
event.preventDefault();
|
|
19536
20679
|
goSearch(direction);
|
|
19537
20680
|
}
|
|
@@ -19543,14 +20686,16 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
19543
20686
|
}, [
|
|
19544
20687
|
searchOpen,
|
|
19545
20688
|
searchMatches,
|
|
19546
|
-
|
|
20689
|
+
searchMatchCount,
|
|
20690
|
+
splitView,
|
|
20691
|
+
previewActive
|
|
19547
20692
|
]);
|
|
19548
20693
|
(0, react.useEffect)(() => {
|
|
19549
20694
|
const onKeyDown = (event) => {
|
|
19550
20695
|
if (!isSearchInputEvent(event)) return;
|
|
19551
20696
|
const option = matchesShortcut(event, keybindingOf("matchCase")) ? "case" : matchesShortcut(event, keybindingOf("matchWholeWord")) ? "word" : void 0;
|
|
19552
20697
|
if (option === void 0) return;
|
|
19553
|
-
if (splitView) {
|
|
20698
|
+
if (splitView && !previewActive) {
|
|
19554
20699
|
const handle = splitDiffRef.current;
|
|
19555
20700
|
if (option === "case" ? handle?.toggleMatchCase() === true : handle?.toggleMatchWholeWord() === true) event.preventDefault();
|
|
19556
20701
|
return;
|
|
@@ -19567,13 +20712,14 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
19567
20712
|
}, [
|
|
19568
20713
|
searchOpen,
|
|
19569
20714
|
splitView,
|
|
20715
|
+
previewActive,
|
|
19570
20716
|
search
|
|
19571
20717
|
]);
|
|
19572
20718
|
(0, react.useEffect)(() => {
|
|
19573
20719
|
const onKeyDown = (event) => {
|
|
19574
20720
|
if (event.key !== "Escape" || !isInPanelEvent(event)) return;
|
|
19575
|
-
if (
|
|
19576
|
-
if (splitView) {
|
|
20721
|
+
if (pathPickerOpen()) return;
|
|
20722
|
+
if (!previewActive && splitView) {
|
|
19577
20723
|
if (splitDiffRef.current?.closeSearch() === true) event.preventDefault();
|
|
19578
20724
|
return;
|
|
19579
20725
|
}
|
|
@@ -19588,8 +20734,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
19588
20734
|
}, [
|
|
19589
20735
|
searchOpen,
|
|
19590
20736
|
splitView,
|
|
19591
|
-
|
|
19592
|
-
lang
|
|
20737
|
+
previewActive
|
|
19593
20738
|
]);
|
|
19594
20739
|
const jumpRef = (0, react.useRef)(jumpBlock);
|
|
19595
20740
|
jumpRef.current = jumpBlock;
|
|
@@ -19599,6 +20744,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
19599
20744
|
if (matchesShortcut(event, keybindingOf("jumpUp"))) direction = -1;
|
|
19600
20745
|
else if (matchesShortcut(event, keybindingOf("jumpDown"))) direction = 1;
|
|
19601
20746
|
if (direction === 0) return;
|
|
20747
|
+
if (pathPickerOpen()) return;
|
|
19602
20748
|
if (isTextFieldEvent(event)) return;
|
|
19603
20749
|
event.preventDefault();
|
|
19604
20750
|
jumpRef.current(direction, true);
|
|
@@ -19619,6 +20765,123 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
19619
20765
|
const flashTop = focusedBlock === void 0 ? 0 : Math.max(0, offsetOf(focusedBlock.start) - scrollTop);
|
|
19620
20766
|
const flashBottom = focusedBlock === void 0 ? 0 : Math.min(viewportHeight > 0 ? viewportHeight : Number.POSITIVE_INFINITY, offsetOf(focusedBlock.end + 1) - scrollTop);
|
|
19621
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;
|
|
19622
20885
|
return (0, react_jsx_runtime.jsxs)("div", {
|
|
19623
20886
|
className: PendingPanel_module_css_default.diff,
|
|
19624
20887
|
"data-diff-approval-diff": true,
|
|
@@ -19809,7 +21072,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
19809
21072
|
className: PendingPanel_module_css_default.missingHint,
|
|
19810
21073
|
children: t("panel.missingHint")
|
|
19811
21074
|
}),
|
|
19812
|
-
|
|
21075
|
+
previewActive ? (0, react_jsx_runtime.jsx)(MarkdownPreviewBoundary, {
|
|
19813
21076
|
fallback: (0, react_jsx_runtime.jsx)("div", {
|
|
19814
21077
|
className: PendingPanel_module_css_default.mdPreviewFallback,
|
|
19815
21078
|
"data-diff-md-preview-fallback": true,
|
|
@@ -19817,29 +21080,146 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
19817
21080
|
}),
|
|
19818
21081
|
children: (0, react_jsx_runtime.jsxs)("div", {
|
|
19819
21082
|
className: PendingPanel_module_css_default.mdPreviewWrap,
|
|
19820
|
-
|
|
19821
|
-
|
|
19822
|
-
|
|
19823
|
-
|
|
19824
|
-
|
|
19825
|
-
|
|
19826
|
-
|
|
19827
|
-
|
|
19828
|
-
|
|
19829
|
-
|
|
19830
|
-
|
|
19831
|
-
|
|
19832
|
-
|
|
19833
|
-
|
|
19834
|
-
|
|
19835
|
-
|
|
19836
|
-
|
|
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,
|
|
19837
21114
|
style: {
|
|
19838
|
-
top:
|
|
19839
|
-
height:
|
|
21115
|
+
top: previewFlashNow.top,
|
|
21116
|
+
height: previewFlashNow.height,
|
|
21117
|
+
left: previewFlash.left,
|
|
21118
|
+
right: previewFlash.right
|
|
19840
21119
|
}
|
|
19841
|
-
},
|
|
19842
|
-
|
|
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
|
+
]
|
|
19843
21223
|
})
|
|
19844
21224
|
}) : splitView ? (0, react_jsx_runtime.jsx)(SplitDiff, {
|
|
19845
21225
|
ref: splitDiffRef,
|
|
@@ -19854,7 +21234,8 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
19854
21234
|
leadRows,
|
|
19855
21235
|
onBlockKeep,
|
|
19856
21236
|
onBlockRevert,
|
|
19857
|
-
onWrapToast: (text) => onToast(text)
|
|
21237
|
+
onWrapToast: (text) => onToast(text),
|
|
21238
|
+
onVisibleLines: onSplitVisibleLines
|
|
19858
21239
|
}) : (0, react_jsx_runtime.jsxs)("div", {
|
|
19859
21240
|
className: PendingPanel_module_css_default.diffBodyWrap,
|
|
19860
21241
|
onMouseLeave: () => {
|
|
@@ -19992,117 +21373,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
19992
21373
|
width: flashWidth
|
|
19993
21374
|
}
|
|
19994
21375
|
}, flashKey),
|
|
19995
|
-
|
|
19996
|
-
className: PendingPanel_module_css_default.searchBar,
|
|
19997
|
-
"data-diff-searchbar": true,
|
|
19998
|
-
children: [
|
|
19999
|
-
(0, react_jsx_runtime.jsx)("input", {
|
|
20000
|
-
ref: searchInputRef,
|
|
20001
|
-
className: PendingPanel_module_css_default.searchInput,
|
|
20002
|
-
"data-diff-search-input": true,
|
|
20003
|
-
value: searchQuery,
|
|
20004
|
-
placeholder: t("panel.searchPlaceholder"),
|
|
20005
|
-
onChange: (event) => {
|
|
20006
|
-
const value = event.target.value;
|
|
20007
|
-
setSearchQuery(value);
|
|
20008
|
-
setSearchIndex(startIndexFor(value));
|
|
20009
|
-
},
|
|
20010
|
-
onKeyDown: (event) => {
|
|
20011
|
-
if (event.key === "Enter") {
|
|
20012
|
-
event.preventDefault();
|
|
20013
|
-
goSearch(event.shiftKey ? -1 : 1);
|
|
20014
|
-
}
|
|
20015
|
-
}
|
|
20016
|
-
}),
|
|
20017
|
-
(0, react_jsx_runtime.jsx)("span", {
|
|
20018
|
-
className: PendingPanel_module_css_default.searchCount,
|
|
20019
|
-
"data-diff-search-count": true,
|
|
20020
|
-
children: searchMatches.length === 0 ? "0/0" : `${searchIndex % searchMatches.length + 1}/${searchMatches.length}`
|
|
20021
|
-
}),
|
|
20022
|
-
(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
20023
|
-
label: withChord(t("action.matchCase"), "matchCase"),
|
|
20024
|
-
side: "bottom",
|
|
20025
|
-
delayMs: 500,
|
|
20026
|
-
children: (0, react_jsx_runtime.jsx)("button", {
|
|
20027
|
-
type: "button",
|
|
20028
|
-
className: search.caseSensitive ? `${PendingPanel_module_css_default.searchToggle} ${PendingPanel_module_css_default.searchToggleOn}` : PendingPanel_module_css_default.searchToggle,
|
|
20029
|
-
"data-diff-search-case": true,
|
|
20030
|
-
"data-on": search.caseSensitive ? "" : void 0,
|
|
20031
|
-
"aria-label": t("action.matchCase"),
|
|
20032
|
-
"aria-pressed": search.caseSensitive,
|
|
20033
|
-
onClick: () => {
|
|
20034
|
-
andRefocus(() => {
|
|
20035
|
-
search.toggleCase();
|
|
20036
|
-
});
|
|
20037
|
-
},
|
|
20038
|
-
children: (0, react_jsx_runtime.jsx)(SearchOptionIcon, { kind: "case" })
|
|
20039
|
-
})
|
|
20040
|
-
}),
|
|
20041
|
-
(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
20042
|
-
label: withChord(t("action.matchWholeWord"), "matchWholeWord"),
|
|
20043
|
-
side: "bottom",
|
|
20044
|
-
delayMs: 500,
|
|
20045
|
-
children: (0, react_jsx_runtime.jsx)("button", {
|
|
20046
|
-
type: "button",
|
|
20047
|
-
className: search.wholeWord ? `${PendingPanel_module_css_default.searchToggle} ${PendingPanel_module_css_default.searchToggleOn}` : PendingPanel_module_css_default.searchToggle,
|
|
20048
|
-
"data-diff-search-word": true,
|
|
20049
|
-
"data-on": search.wholeWord ? "" : void 0,
|
|
20050
|
-
"aria-label": t("action.matchWholeWord"),
|
|
20051
|
-
"aria-pressed": search.wholeWord,
|
|
20052
|
-
onClick: () => {
|
|
20053
|
-
andRefocus(() => {
|
|
20054
|
-
search.toggleWord();
|
|
20055
|
-
});
|
|
20056
|
-
},
|
|
20057
|
-
children: (0, react_jsx_runtime.jsx)(SearchOptionIcon, { kind: "word" })
|
|
20058
|
-
})
|
|
20059
|
-
}),
|
|
20060
|
-
(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
20061
|
-
label: withChord(t("action.prevDiff"), "searchPrev"),
|
|
20062
|
-
side: "bottom",
|
|
20063
|
-
delayMs: 500,
|
|
20064
|
-
children: (0, react_jsx_runtime.jsx)("button", {
|
|
20065
|
-
type: "button",
|
|
20066
|
-
className: `${PendingPanel_module_css_default.action} ${PendingPanel_module_css_default.iconAction}`,
|
|
20067
|
-
"data-diff-search-prev": true,
|
|
20068
|
-
"aria-label": t("action.prevDiff"),
|
|
20069
|
-
disabled: searchMatches.length === 0,
|
|
20070
|
-
onClick: () => {
|
|
20071
|
-
andRefocus(() => {
|
|
20072
|
-
goSearch(-1);
|
|
20073
|
-
});
|
|
20074
|
-
},
|
|
20075
|
-
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronUpOutline14, { size: 14 })
|
|
20076
|
-
})
|
|
20077
|
-
}),
|
|
20078
|
-
(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
20079
|
-
label: withChord(t("action.nextDiff"), "searchNext"),
|
|
20080
|
-
side: "bottom",
|
|
20081
|
-
delayMs: 500,
|
|
20082
|
-
children: (0, react_jsx_runtime.jsx)("button", {
|
|
20083
|
-
type: "button",
|
|
20084
|
-
className: `${PendingPanel_module_css_default.action} ${PendingPanel_module_css_default.iconAction}`,
|
|
20085
|
-
"data-diff-search-next": true,
|
|
20086
|
-
"aria-label": t("action.nextDiff"),
|
|
20087
|
-
disabled: searchMatches.length === 0,
|
|
20088
|
-
onClick: () => {
|
|
20089
|
-
andRefocus(() => {
|
|
20090
|
-
goSearch(1);
|
|
20091
|
-
});
|
|
20092
|
-
},
|
|
20093
|
-
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, { size: 14 })
|
|
20094
|
-
})
|
|
20095
|
-
}),
|
|
20096
|
-
(0, react_jsx_runtime.jsx)("button", {
|
|
20097
|
-
type: "button",
|
|
20098
|
-
className: `${PendingPanel_module_css_default.action} ${PendingPanel_module_css_default.iconAction}`,
|
|
20099
|
-
"data-diff-search-close": true,
|
|
20100
|
-
"aria-label": t("action.close"),
|
|
20101
|
-
onClick: closeSearch,
|
|
20102
|
-
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCloseOutline16, { size: 14 })
|
|
20103
|
-
})
|
|
20104
|
-
]
|
|
20105
|
-
}),
|
|
21376
|
+
searchBar,
|
|
20106
21377
|
rulerMarkers.length > 0 && (0, react_jsx_runtime.jsx)("div", {
|
|
20107
21378
|
className: PendingPanel_module_css_default.overviewRuler,
|
|
20108
21379
|
"data-diff-approval-ruler": true,
|
|
@@ -20149,15 +21420,17 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
20149
21420
|
})
|
|
20150
21421
|
}),
|
|
20151
21422
|
(0, react_jsx_runtime.jsx)("span", { className: PendingPanel_module_css_default.flexSpacer }),
|
|
20152
|
-
!
|
|
21423
|
+
!previewActive && (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Menu, {
|
|
20153
21424
|
open: langMenuOpen,
|
|
20154
21425
|
portal: true,
|
|
20155
21426
|
compact: true,
|
|
20156
21427
|
align: "end",
|
|
20157
21428
|
items: langMenuItems,
|
|
20158
|
-
selectedId:
|
|
21429
|
+
selectedId: effectiveLang ?? "",
|
|
20159
21430
|
onSelect: (id) => {
|
|
20160
|
-
|
|
21431
|
+
const next = id === "" ? void 0 : id;
|
|
21432
|
+
setLangOverride(next);
|
|
21433
|
+
if (langSuffix !== void 0) setLanguageForSuffix(langSuffix, next ?? null);
|
|
20161
21434
|
setLangMenuOpen(false);
|
|
20162
21435
|
},
|
|
20163
21436
|
onClose: () => {
|
|
@@ -20204,7 +21477,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
20204
21477
|
});
|
|
20205
21478
|
}
|
|
20206
21479
|
/** Render the pending-edit review panel and its unified footer action. */
|
|
20207
|
-
function PendingPanel({ wide, useSessions, usePending, onRefresh, onKeep, onRevert, onBlockKeep, onBlockRevert, onOpen, onPreviewImage, onPasteReference, onUndo, onRedo, onImportVcs, onRefreshVcs, 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 }) {
|
|
20208
21481
|
const current = useSessions((state) => state.current);
|
|
20209
21482
|
const currentBlank = useSessions((state) => {
|
|
20210
21483
|
const id = state.current;
|
|
@@ -20267,6 +21540,9 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
20267
21540
|
* choice rides the same block RPC as its `removeWhenResolved` flag. */
|
|
20268
21541
|
const [blockPrompt, setBlockPrompt] = (0, react.useState)(null);
|
|
20269
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);
|
|
20270
21546
|
/** Bottom offset tracking the chat composer's top edge so the input stays visible. */
|
|
20271
21547
|
const [bottomPx, setBottomPx] = (0, react.useState)(FALLBACK_BOTTOM_PX);
|
|
20272
21548
|
/** Fullscreen expanded: the panel bottom pins to the window edge, ignoring the composer offset. */
|
|
@@ -20505,6 +21781,12 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
20505
21781
|
(0, react.useEffect)(() => {
|
|
20506
21782
|
if (noSession) setOpen(false);
|
|
20507
21783
|
}, [noSession]);
|
|
21784
|
+
(0, react.useEffect)(() => {
|
|
21785
|
+
if (open) return;
|
|
21786
|
+
setAddOpen(false);
|
|
21787
|
+
setBlockPrompt(null);
|
|
21788
|
+
setFilePrompt(null);
|
|
21789
|
+
}, [open]);
|
|
20508
21790
|
/** Run the same decision over every current-session file, sequentially. */
|
|
20509
21791
|
const runBulk = async (kind) => {
|
|
20510
21792
|
if (current === void 0) return;
|
|
@@ -20565,37 +21847,63 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
20565
21847
|
const promptFile = blockPrompt === null ? void 0 : files.find((file) => file.id === blockPrompt.id);
|
|
20566
21848
|
/** The file whose removal is being confirmed (a whole-file action), if any. */
|
|
20567
21849
|
const promptEntry = filePrompt === null ? void 0 : files.find((file) => file.id === filePrompt.id);
|
|
20568
|
-
const fileListBody = (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
20569
|
-
|
|
20570
|
-
|
|
20571
|
-
|
|
20572
|
-
|
|
20573
|
-
|
|
20574
|
-
|
|
20575
|
-
|
|
20576
|
-
|
|
20577
|
-
|
|
20578
|
-
|
|
20579
|
-
|
|
20580
|
-
|
|
20581
|
-
|
|
20582
|
-
|
|
20583
|
-
|
|
20584
|
-
|
|
20585
|
-
|
|
20586
|
-
|
|
20587
|
-
|
|
20588
|
-
|
|
20589
|
-
|
|
20590
|
-
|
|
20591
|
-
|
|
20592
|
-
|
|
20593
|
-
|
|
20594
|
-
|
|
20595
|
-
|
|
20596
|
-
|
|
20597
|
-
|
|
20598
|
-
|
|
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
|
+
] });
|
|
20599
21907
|
const handleUndo = async (sessionId) => {
|
|
20600
21908
|
const id = await onUndo(sessionId);
|
|
20601
21909
|
if (id === void 0) return;
|
|
@@ -20612,6 +21920,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
20612
21920
|
if (!open || current === void 0) return;
|
|
20613
21921
|
const onKeyDown = (event) => {
|
|
20614
21922
|
if (!(event.ctrlKey || event.metaKey) || event.altKey) return;
|
|
21923
|
+
if (pathPickerOpen()) return;
|
|
20615
21924
|
const target = event.target;
|
|
20616
21925
|
if (target instanceof Element && target.closest("input, textarea, [contenteditable=\"true\"]") !== null) return;
|
|
20617
21926
|
if (matchesShortcut(event, keybindingOf("undo"))) {
|
|
@@ -20641,6 +21950,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
20641
21950
|
if (matchesShortcut(event, keybindingOf("cycleNext"))) direction = 1;
|
|
20642
21951
|
else if (matchesShortcut(event, keybindingOf("cyclePrev"))) direction = -1;
|
|
20643
21952
|
if (direction === 0) return;
|
|
21953
|
+
if (pathPickerOpen()) return;
|
|
20644
21954
|
const target = event.target;
|
|
20645
21955
|
if (target instanceof Element && target.closest("input, textarea, [contenteditable=\"true\"]") !== null) return;
|
|
20646
21956
|
if (files.length === 0) return;
|
|
@@ -20662,6 +21972,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
20662
21972
|
(0, react.useEffect)(() => {
|
|
20663
21973
|
const onKeyDown = (event) => {
|
|
20664
21974
|
if (!matchesShortcut(event, quickSummonKey())) return;
|
|
21975
|
+
if (pathPickerOpen()) return;
|
|
20665
21976
|
event.preventDefault();
|
|
20666
21977
|
if (!open) collapseSidebar();
|
|
20667
21978
|
setOpen((value) => !value);
|
|
@@ -20675,6 +21986,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
20675
21986
|
if (!open) return;
|
|
20676
21987
|
const onKeyDown = (event) => {
|
|
20677
21988
|
if (event.key !== "Escape") return;
|
|
21989
|
+
if (pathPickerOpen()) return;
|
|
20678
21990
|
const target = event.target;
|
|
20679
21991
|
if (target instanceof Node && panelRef.current?.contains(target) === true && document.querySelector("[data-diff-searchbar]") !== null) return;
|
|
20680
21992
|
event.preventDefault();
|
|
@@ -20819,15 +22131,26 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
20819
22131
|
className: `${PendingPanel_module_css_default.note} ${PendingPanel_module_css_default.noteCentered}`,
|
|
20820
22132
|
children: t("panel.empty")
|
|
20821
22133
|
}),
|
|
20822
|
-
(0, react_jsx_runtime.
|
|
20823
|
-
|
|
20824
|
-
|
|
20825
|
-
|
|
20826
|
-
|
|
20827
|
-
|
|
20828
|
-
|
|
20829
|
-
|
|
20830
|
-
|
|
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
|
+
})]
|
|
20831
22154
|
}),
|
|
20832
22155
|
importNote !== void 0 && (0, react_jsx_runtime.jsx)("p", {
|
|
20833
22156
|
className: PendingPanel_module_css_default.importNote,
|
|
@@ -20965,6 +22288,16 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
20965
22288
|
})]
|
|
20966
22289
|
})]
|
|
20967
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
|
|
20968
22301
|
})
|
|
20969
22302
|
]
|
|
20970
22303
|
})] }), document.body),
|
|
@@ -21940,6 +23273,19 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
21940
23273
|
includeUntracked
|
|
21941
23274
|
}));
|
|
21942
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
|
+
},
|
|
21943
23289
|
async open(sessionId, id, action) {
|
|
21944
23290
|
return openOf(await rpc.call(DIFF_APPROVAL_CHANNEL, "open", {
|
|
21945
23291
|
sessionId,
|
|
@@ -22048,6 +23394,55 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
22048
23394
|
if (outcome !== "refreshed" && outcome !== "unchanged" && outcome !== "no-change" && outcome !== "missing" && outcome !== "no-vcs") throw new Error("the refresh returned a malformed outcome");
|
|
22049
23395
|
return { outcome };
|
|
22050
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
|
+
}
|
|
22051
23446
|
/** Narrow the open endpoint's value; a malformed wire value is an open failure. */
|
|
22052
23447
|
function openOf(result) {
|
|
22053
23448
|
if (!result.ok) throw new Error(`${result.error.code}: ${result.error.message}`);
|
|
@@ -22329,6 +23724,14 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
22329
23724
|
});
|
|
22330
23725
|
}
|
|
22331
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
|
+
},
|
|
22332
23735
|
async open(sessionId, id, action) {
|
|
22333
23736
|
try {
|
|
22334
23737
|
await port.open(sessionId, id, action);
|
|
@@ -22507,7 +23910,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
22507
23910
|
/** Simplified Chinese pending-edit review messages. */
|
|
22508
23911
|
const zh = {
|
|
22509
23912
|
"panel.title": "待处理改动",
|
|
22510
|
-
"panel.empty": "
|
|
23913
|
+
"panel.empty": "列表中暂无文件",
|
|
22511
23914
|
"panel.loading": "读取中…",
|
|
22512
23915
|
"panel.readFailed": "读取待处理改动失败:{message}",
|
|
22513
23916
|
"panel.group.current": "当前会话",
|
|
@@ -22633,12 +24036,32 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
22633
24036
|
"panel.refreshUnchanged": "差异已是最新",
|
|
22634
24037
|
"panel.refreshNone": "未检测到本地 VCS 差异",
|
|
22635
24038
|
"panel.refreshUntrackedHint": "(新建/未跟踪文件需要开启「导入未跟踪的文件改动」)",
|
|
22636
|
-
"panel.refreshFailed": "刷新失败:{message}"
|
|
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": "展开"
|
|
22637
24060
|
};
|
|
22638
24061
|
/** English pending-edit review messages. */
|
|
22639
24062
|
const en = {
|
|
22640
24063
|
"panel.title": "Pending changes",
|
|
22641
|
-
"panel.empty": "No
|
|
24064
|
+
"panel.empty": "No files in the list yet",
|
|
22642
24065
|
"panel.loading": "Reading…",
|
|
22643
24066
|
"panel.readFailed": "Reading pending changes failed: {message}",
|
|
22644
24067
|
"panel.group.current": "This session",
|
|
@@ -22764,7 +24187,27 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
22764
24187
|
"panel.refreshUnchanged": "Already up to date",
|
|
22765
24188
|
"panel.refreshNone": "No local VCS change found",
|
|
22766
24189
|
"panel.refreshUntrackedHint": "(new or untracked files need \"Import changes to untracked files\")",
|
|
22767
|
-
"panel.refreshFailed": "Refresh failed: {message}"
|
|
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"
|
|
22768
24211
|
};
|
|
22769
24212
|
//#endregion
|
|
22770
24213
|
//#region lib/types/client/index.js
|
|
@@ -22897,6 +24340,8 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
22897
24340
|
onRedo: (sessionId) => store.redo(sessionId),
|
|
22898
24341
|
onImportVcs: (sessionId, includeUntracked) => store.importVcs(sessionId, includeUntracked),
|
|
22899
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),
|
|
22900
24345
|
onKeepAll: (sessionId) => store.keepAll(sessionId),
|
|
22901
24346
|
onRevertAll: (sessionId) => store.revertAll(sessionId),
|
|
22902
24347
|
onAckRedoCleared: () => store.clearRedoCleared(),
|