dsh-diff-approval 0.20.1 → 0.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -5
- package/README.zh.md +7 -5
- package/lib/client.js +3006 -1114
- package/lib/types/client/PendingPanel.d.ts +33 -2
- package/lib/types/client/boundary.d.ts +38 -0
- package/lib/types/client/chords.d.ts +49 -0
- package/lib/types/client/coverage-control.d.ts +49 -0
- package/lib/types/client/dock.d.ts +198 -0
- package/lib/types/client/header-entry.d.ts +59 -0
- package/lib/types/client/locales.d.ts +70 -4
- package/lib/types/client/panel-memory.d.ts +47 -0
- package/lib/types/client/presentation-menu.d.ts +45 -0
- package/lib/types/client/settings.d.ts +59 -1
- package/lib/types/client/slots.d.ts +19 -2
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -16,12 +16,12 @@ window.__ModuleLoader__.load({
|
|
|
16
16
|
return target;
|
|
17
17
|
};
|
|
18
18
|
//#endregion
|
|
19
|
-
let react_jsx_runtime = require("react/jsx-runtime");
|
|
20
19
|
let react = require("react");
|
|
20
|
+
let react_jsx_runtime = require("react/jsx-runtime");
|
|
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
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)}";
|
|
24
|
+
const css = ".F1KBNa_panelFailure{box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-base);color:var(--dsw-alias-label-primary);border-radius:8px;align-items:center;gap:8px;padding:8px 10px;font-size:12px;line-height:16px;display:flex}.F1KBNa_panelFailureText{min-width:0}.F1KBNa_panelFailureRetry{border:1px solid var(--dsw-alias-border-l2);color:inherit;font:inherit;cursor:pointer;background:0 0;border-radius:6px;flex:none;margin-left:auto;padding:2px 8px}.F1KBNa_panelFailureRetry:hover{background:var(--dsw-alias-interactive-bg-hover)}.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_headerEntry{box-sizing:border-box;width:28px;height:28px;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;border-radius:28px;flex:none;justify-content:center;align-items:center;padding:6px;display:inline-flex;position:relative}.F1KBNa_headerEntry:hover{background:var(--dsw-alias-interactive-bg-hover)}.F1KBNa_headerEntry[data-active]{background:var(--dsw-alias-interactive-bg-hover-solid);color:var(--dsw-alias-label-primary)}.F1KBNa_headerEntry:disabled{color:var(--dsw-alias-label-tertiary);cursor:default;background:0 0}.F1KBNa_headerEntryCount{box-sizing:border-box;background:var(--dsw-alias-button-primary-fill);min-width:14px;height:14px;color:var(--dsw-alias-label-primary-foreground);text-align:center;font-variant-numeric:tabular-nums;pointer-events:none;border-radius:7px;padding:0 3px;font-size:10px;line-height:14px;position:absolute;top:-1px;right:-1px}.F1KBNa_headerEntry svg{display:block}.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_coverBackdrop{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_panelDocked{width:100%;max-width:none;height:100%;box-shadow:none;border:none;border-radius:0;position:static;inset:auto}.F1KBNa_dockHost{flex-direction:column;min-height:0;display:flex;position:absolute;inset:0}.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)}.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);min-width:72px;height:36px;font:inherit;color:var(--dsw-alias-label-primary);cursor:pointer;border:none;border-radius:18px;justify-content:center;align-items:center;padding:0 14px;font-size:14px;line-height:22px;display:inline-flex}.F1KBNa_settingsSelector:hover{background:var(--dsw-alias-interactive-bg-hover)}.F1KBNa_settingsSelector[data-recording]{box-shadow:inset 0 0 0 1px var(--dsw-alias-state-business-primary);color:var(--dsw-alias-label-secondary)}.F1KBNa_settingsReset{width:28px;height:28px;color:var(--dsw-alias-label-tertiary);cursor:pointer;background:0 0;border:none;border-radius:28px;justify-content:center;align-items:center;margin-left:6px;padding:0;display:inline-flex}.F1KBNa_settingsReset:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}.F1KBNa_settingsReset:disabled{opacity:.4;cursor:default}.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_fileListKnob{z-index:39;box-sizing:border-box;border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-base);width:28px;height:28px;color:var(--dsw-alias-label-primary);cursor:pointer;box-shadow:var(--dsw-shadow-lv2);border-radius:8px;justify-content:center;align-items:center;padding:0;display:inline-flex;position:absolute}.F1KBNa_fileListKnob:hover{background:var(--dsw-alias-interactive-bg-hover)}.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);transform-origin:0 0;border-radius:12px;flex-direction:column;padding:6px 8px 10px;animation:.14s ease-out F1KBNa_fileListGrow;display:flex;position:absolute;overflow:hidden}@keyframes F1KBNa_fileListGrow{0%{opacity:0;transform:scale(.92)}to{opacity:1;transform:scale(1)}}@media (prefers-reduced-motion:reduce){.F1KBNa_fileListFloat{animation:none}}.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_floatResizeHandle{z-index:41;cursor:col-resize;background:0 0;width:8px;position:absolute;top:0;bottom:0;right:0}.F1KBNa_floatResizeHandle: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;scrollbar-width:none;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_diffPath::-webkit-scrollbar{display:none}.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_presentationSelect{cursor:pointer;height:28px;color:var(--dsw-alias-label-tertiary);background:0 0;border:none;border-radius:28px;justify-content:center;align-items:center;gap:2px;padding:0 6px;display:inline-flex}.F1KBNa_presentationSelect:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}.F1KBNa_chip{justify-content:flex-end;align-items:center;gap:5px;min-width:0;padding-right:14px;display:inline-flex}.F1KBNa_chipLabel{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.F1KBNa_coverHost{align-items:center;display:inline-flex;position:relative}.F1KBNa_coverCard{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-base);box-shadow:var(--dsw-shadow-lv2);border-radius:12px;align-items:center;gap:4px;padding:6px;display:flex}.F1KBNa_coverPopover{z-index:50;position:absolute;top:calc(100% + 6px);right:0}.F1KBNa_coverNotice{z-index:80;pointer-events:none;animation:1.25s ease-out forwards F1KBNa_coverNoticeFade;position:fixed;top:50%;left:50%;transform:translate(-50%,-50%)}.F1KBNa_coverNoticeCard{gap:4px;padding:6px 8px}@keyframes F1KBNa_coverNoticeFade{0%,78%{opacity:1}to{opacity:0}}.F1KBNa_coverNoticeGlyph{width:44px;height:44px;color:var(--dsw-alias-label-tertiary);border-radius:10px;justify-content:center;align-items:center;display:inline-flex}.F1KBNa_coverNoticeGlyph[data-on]{color:var(--dsw-alias-label-primary)}.F1KBNa_coverNoticeGlyph[data-changed]{background:var(--dsw-alias-interactive-bg-hover)}.F1KBNa_coverNoticeText{clip-path:inset(50%);white-space:nowrap;width:1px;height:1px;position:absolute;overflow:hidden}.F1KBNa_coverButton{width:32px;height:32px;color:var(--dsw-alias-label-tertiary);cursor:pointer;background:0 0;border:none;border-radius:8px;justify-content:center;align-items:center;padding:0;display:inline-flex}.F1KBNa_coverButton:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}.F1KBNa_coverButton[data-on]{color:var(--dsw-alias-label-primary)}.F1KBNa_presentationHost{align-items:center;display:inline-flex}.F1KBNa_chipPresentation{min-width:36px;height:20px;color:var(--dsw-alias-label-tertiary);cursor:pointer;background:0 0;border:none;border-radius:20px;flex:none;justify-content:center;align-items:center;gap:2px;padding:0 6px;display:inline-flex}.F1KBNa_chipPresentation:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}.F1KBNa_mirrored{transform:scaleX(-1)}.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
25
|
const tagId = "dsh-diff-approval/PendingPanel.module.css";
|
|
26
26
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
|
|
27
27
|
const tag = document.createElement("style");
|
|
@@ -31,179 +31,204 @@ window.__ModuleLoader__.load({
|
|
|
31
31
|
document.head.appendChild(tag);
|
|
32
32
|
}
|
|
33
33
|
var PendingPanel_module_css_default = {
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"
|
|
37
|
-
"
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
"
|
|
34
|
+
"flexSpacer": "F1KBNa_flexSpacer",
|
|
35
|
+
"rowPath": "F1KBNa_rowPath",
|
|
36
|
+
"code": "F1KBNa_code",
|
|
37
|
+
"noticeButton": "F1KBNa_noticeButton",
|
|
38
|
+
"settingsGroupDesc": "F1KBNa_settingsGroupDesc",
|
|
39
|
+
"settingsRowTitle": "F1KBNa_settingsRowTitle",
|
|
40
|
+
"actionPrimary": "F1KBNa_actionPrimary",
|
|
41
|
+
"emptyActions": "F1KBNa_emptyActions",
|
|
42
|
+
"actionQuietDisabled": "F1KBNa_actionQuietDisabled",
|
|
43
|
+
"header": "F1KBNa_header",
|
|
44
|
+
"settingsGroupChevron": "F1KBNa_settingsGroupChevron",
|
|
45
|
+
"confirmCard": "F1KBNa_confirmCard",
|
|
46
|
+
"group": "F1KBNa_group",
|
|
47
|
+
"settingsRowText": "F1KBNa_settingsRowText",
|
|
48
|
+
"wrapActive": "F1KBNa_wrapActive",
|
|
49
|
+
"splitLdel": "F1KBNa_splitLdel",
|
|
50
|
+
"langSelect": "F1KBNa_langSelect",
|
|
51
|
+
"mdPreviewWrap": "F1KBNa_mdPreviewWrap",
|
|
52
|
+
"pickerCard": "F1KBNa_pickerCard",
|
|
53
|
+
"splitHScroll": "F1KBNa_splitHScroll",
|
|
54
|
+
"intraDel": "F1KBNa_intraDel",
|
|
55
|
+
"panelDocked": "F1KBNa_panelDocked",
|
|
56
|
+
"importNote": "F1KBNa_importNote",
|
|
48
57
|
"mdPreviewBody": "F1KBNa_mdPreviewBody",
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
"
|
|
53
|
-
"
|
|
54
|
-
"
|
|
58
|
+
"chip": "F1KBNa_chip",
|
|
59
|
+
"diffStats": "F1KBNa_diffStats",
|
|
60
|
+
"colorPickerInputRow": "F1KBNa_colorPickerInputRow",
|
|
61
|
+
"noticeText": "F1KBNa_noticeText",
|
|
62
|
+
"addButton": "F1KBNa_addButton",
|
|
63
|
+
"toggleThumb": "F1KBNa_toggleThumb",
|
|
64
|
+
"pickerCheck": "F1KBNa_pickerCheck",
|
|
65
|
+
"blockActions": "F1KBNa_blockActions",
|
|
66
|
+
"colorSvCursor": "F1KBNa_colorSvCursor",
|
|
67
|
+
"tree": "F1KBNa_tree",
|
|
68
|
+
"panelFailure": "F1KBNa_panelFailure",
|
|
69
|
+
"markerDel": "F1KBNa_markerDel",
|
|
70
|
+
"mirrored": "F1KBNa_mirrored",
|
|
71
|
+
"colorPickerChevron": "F1KBNa_colorPickerChevron",
|
|
72
|
+
"note": "F1KBNa_note",
|
|
73
|
+
"settingsGroupBody": "F1KBNa_settingsGroupBody",
|
|
74
|
+
"diffFlashShake": "F1KBNa_diffFlashShake",
|
|
75
|
+
"iconAction": "F1KBNa_iconAction",
|
|
55
76
|
"splitCol": "F1KBNa_splitCol",
|
|
56
|
-
"
|
|
77
|
+
"panel": "F1KBNa_panel",
|
|
57
78
|
"missing": "F1KBNa_missing",
|
|
58
|
-
"
|
|
59
|
-
"
|
|
60
|
-
"
|
|
61
|
-
"
|
|
62
|
-
"
|
|
63
|
-
"
|
|
64
|
-
"
|
|
65
|
-
"
|
|
79
|
+
"missingHint": "F1KBNa_missingHint",
|
|
80
|
+
"searchMatchCurrent": "F1KBNa_searchMatchCurrent",
|
|
81
|
+
"rows": "F1KBNa_rows",
|
|
82
|
+
"diff": "F1KBNa_diff",
|
|
83
|
+
"settingsGroupText": "F1KBNa_settingsGroupText",
|
|
84
|
+
"context": "F1KBNa_context",
|
|
85
|
+
"treeName": "F1KBNa_treeName",
|
|
86
|
+
"searchToggle": "F1KBNa_searchToggle",
|
|
87
|
+
"hint": "F1KBNa_hint",
|
|
66
88
|
"close": "F1KBNa_close",
|
|
67
|
-
"
|
|
68
|
-
"
|
|
69
|
-
"detailEmpty": "F1KBNa_detailEmpty",
|
|
70
|
-
"settingsRowTitle": "F1KBNa_settingsRowTitle",
|
|
71
|
-
"pickerIcon": "F1KBNa_pickerIcon",
|
|
89
|
+
"stepperButtonUp": "F1KBNa_stepperButtonUp",
|
|
90
|
+
"settingsPage": "F1KBNa_settingsPage",
|
|
72
91
|
"fileList": "F1KBNa_fileList",
|
|
73
|
-
"
|
|
74
|
-
"
|
|
75
|
-
"
|
|
76
|
-
"
|
|
77
|
-
"
|
|
78
|
-
"
|
|
79
|
-
"
|
|
92
|
+
"colorPickerTrigger": "F1KBNa_colorPickerTrigger",
|
|
93
|
+
"pickerInput": "F1KBNa_pickerInput",
|
|
94
|
+
"searchInput": "F1KBNa_searchInput",
|
|
95
|
+
"langLabel": "F1KBNa_langLabel",
|
|
96
|
+
"resizeHandle": "F1KBNa_resizeHandle",
|
|
97
|
+
"row": "F1KBNa_row",
|
|
98
|
+
"splitRoot": "F1KBNa_splitRoot",
|
|
99
|
+
"treeRow": "F1KBNa_treeRow",
|
|
100
|
+
"diffBodyWrap": "F1KBNa_diffBodyWrap",
|
|
101
|
+
"importButton": "F1KBNa_importButton",
|
|
80
102
|
"rowHead": "F1KBNa_rowHead",
|
|
81
|
-
"
|
|
82
|
-
"
|
|
103
|
+
"layer": "F1KBNa_layer",
|
|
104
|
+
"fileListKnob": "F1KBNa_fileListKnob",
|
|
105
|
+
"statusBar": "F1KBNa_statusBar",
|
|
106
|
+
"detailEmpty": "F1KBNa_detailEmpty",
|
|
107
|
+
"listScroll": "F1KBNa_listScroll",
|
|
108
|
+
"colorSv": "F1KBNa_colorSv",
|
|
109
|
+
"colorPickerPreview": "F1KBNa_colorPickerPreview",
|
|
110
|
+
"actionError": "F1KBNa_actionError",
|
|
111
|
+
"noteCentered": "F1KBNa_noteCentered",
|
|
83
112
|
"diffPreview": "F1KBNa_diffPreview",
|
|
84
|
-
"
|
|
85
|
-
"
|
|
86
|
-
"
|
|
87
|
-
"
|
|
88
|
-
"
|
|
89
|
-
"
|
|
113
|
+
"expandExpanded": "F1KBNa_expandExpanded",
|
|
114
|
+
"settingsGroupTitle": "F1KBNa_settingsGroupTitle",
|
|
115
|
+
"readError": "F1KBNa_readError",
|
|
116
|
+
"dockHost": "F1KBNa_dockHost",
|
|
117
|
+
"coverNoticeGlyph": "F1KBNa_coverNoticeGlyph",
|
|
118
|
+
"diffHeader": "F1KBNa_diffHeader",
|
|
119
|
+
"panelFailureRetry": "F1KBNa_panelFailureRetry",
|
|
120
|
+
"confirmActions": "F1KBNa_confirmActions",
|
|
121
|
+
"blockPosition": "F1KBNa_blockPosition",
|
|
122
|
+
"kindTag": "F1KBNa_kindTag",
|
|
123
|
+
"mdPreviewFallback": "F1KBNa_mdPreviewFallback",
|
|
90
124
|
"mdPreviewDouble": "F1KBNa_mdPreviewDouble",
|
|
91
|
-
"
|
|
92
|
-
"
|
|
93
|
-
"
|
|
94
|
-
"
|
|
95
|
-
"
|
|
96
|
-
"
|
|
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",
|
|
125
|
+
"presentationHost": "F1KBNa_presentationHost",
|
|
126
|
+
"diffPreviewScroll": "F1KBNa_diffPreviewScroll",
|
|
127
|
+
"chipPresentation": "F1KBNa_chipPresentation",
|
|
128
|
+
"splitRadd": "F1KBNa_splitRadd",
|
|
129
|
+
"coverHost": "F1KBNa_coverHost",
|
|
130
|
+
"stepperButton": "F1KBNa_stepperButton",
|
|
107
131
|
"rail": "F1KBNa_rail",
|
|
108
|
-
"
|
|
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",
|
|
132
|
+
"kindHint": "F1KBNa_kindHint",
|
|
117
133
|
"line": "F1KBNa_line",
|
|
134
|
+
"headerEntry": "F1KBNa_headerEntry",
|
|
135
|
+
"bulkActions": "F1KBNa_bulkActions",
|
|
136
|
+
"floatResizeHandle": "F1KBNa_floatResizeHandle",
|
|
137
|
+
"pickerNote": "F1KBNa_pickerNote",
|
|
138
|
+
"vSpacer": "F1KBNa_vSpacer",
|
|
139
|
+
"splitHScrollFill": "F1KBNa_splitHScrollFill",
|
|
140
|
+
"colorPicker": "F1KBNa_colorPicker",
|
|
141
|
+
"delCount": "F1KBNa_delCount",
|
|
142
|
+
"coverPopover": "F1KBNa_coverPopover",
|
|
143
|
+
"badgeCount": "F1KBNa_badgeCount",
|
|
144
|
+
"badgeLabel": "F1KBNa_badgeLabel",
|
|
145
|
+
"notice": "F1KBNa_notice",
|
|
118
146
|
"wrap": "F1KBNa_wrap",
|
|
119
|
-
"
|
|
120
|
-
"
|
|
121
|
-
"
|
|
147
|
+
"colorPickerPopover": "F1KBNa_colorPickerPopover",
|
|
148
|
+
"searchBar": "F1KBNa_searchBar",
|
|
149
|
+
"settingsGroupHeader": "F1KBNa_settingsGroupHeader",
|
|
150
|
+
"headerEntryCount": "F1KBNa_headerEntryCount",
|
|
151
|
+
"settingsRowDesc": "F1KBNa_settingsRowDesc",
|
|
152
|
+
"coverNotice": "F1KBNa_coverNotice",
|
|
153
|
+
"chipLabel": "F1KBNa_chipLabel",
|
|
154
|
+
"subline": "F1KBNa_subline",
|
|
155
|
+
"addCount": "F1KBNa_addCount",
|
|
156
|
+
"diffPath": "F1KBNa_diffPath",
|
|
122
157
|
"mdPreviewContent": "F1KBNa_mdPreviewContent",
|
|
123
|
-
"
|
|
124
|
-
"
|
|
125
|
-
"
|
|
158
|
+
"settingsRow": "F1KBNa_settingsRow",
|
|
159
|
+
"emptyState": "F1KBNa_emptyState",
|
|
160
|
+
"divergedHint": "F1KBNa_divergedHint",
|
|
161
|
+
"headerActions": "F1KBNa_headerActions",
|
|
162
|
+
"confirmBackdrop": "F1KBNa_confirmBackdrop",
|
|
163
|
+
"diffBody": "F1KBNa_diffBody",
|
|
164
|
+
"panelFailureText": "F1KBNa_panelFailureText",
|
|
165
|
+
"searchMatch": "F1KBNa_searchMatch",
|
|
166
|
+
"markerAdd": "F1KBNa_markerAdd",
|
|
167
|
+
"coverNoticeCard": "F1KBNa_coverNoticeCard",
|
|
168
|
+
"coverNoticeFade": "F1KBNa_coverNoticeFade",
|
|
169
|
+
"badge": "F1KBNa_badge",
|
|
170
|
+
"states": "F1KBNa_states",
|
|
126
171
|
"del": "F1KBNa_del",
|
|
127
|
-
"
|
|
172
|
+
"colorHue": "F1KBNa_colorHue",
|
|
128
173
|
"colorHueCursor": "F1KBNa_colorHueCursor",
|
|
129
|
-
"
|
|
130
|
-
"
|
|
131
|
-
"
|
|
132
|
-
"
|
|
133
|
-
"
|
|
134
|
-
"
|
|
135
|
-
"colorValue": "F1KBNa_colorValue",
|
|
136
|
-
"footerButtons": "F1KBNa_footerButtons",
|
|
137
|
-
"divergedHint": "F1KBNa_divergedHint",
|
|
138
|
-
"stepperButton": "F1KBNa_stepperButton",
|
|
174
|
+
"colorSwatch": "F1KBNa_colorSwatch",
|
|
175
|
+
"mdPreviewSingle": "F1KBNa_mdPreviewSingle",
|
|
176
|
+
"coverBackdrop": "F1KBNa_coverBackdrop",
|
|
177
|
+
"confirmText": "F1KBNa_confirmText",
|
|
178
|
+
"toggleOn": "F1KBNa_toggleOn",
|
|
179
|
+
"detail": "F1KBNa_detail",
|
|
139
180
|
"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
181
|
"rowMeta": "F1KBNa_rowMeta",
|
|
160
|
-
"
|
|
161
|
-
"
|
|
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",
|
|
182
|
+
"settingsReset": "F1KBNa_settingsReset",
|
|
183
|
+
"lines": "F1KBNa_lines",
|
|
178
184
|
"diffPreviewTitle": "F1KBNa_diffPreviewTitle",
|
|
179
|
-
"
|
|
185
|
+
"colorBackdrop": "F1KBNa_colorBackdrop",
|
|
186
|
+
"fileListGrow": "F1KBNa_fileListGrow",
|
|
187
|
+
"pickerTitle": "F1KBNa_pickerTitle",
|
|
188
|
+
"treeCaret": "F1KBNa_treeCaret",
|
|
189
|
+
"treeNote": "F1KBNa_treeNote",
|
|
190
|
+
"searchCount": "F1KBNa_searchCount",
|
|
191
|
+
"presentationSelect": "F1KBNa_presentationSelect",
|
|
192
|
+
"title": "F1KBNa_title",
|
|
193
|
+
"blockFlash": "F1KBNa_blockFlash",
|
|
194
|
+
"divider": "F1KBNa_divider",
|
|
195
|
+
"overviewRuler": "F1KBNa_overviewRuler",
|
|
196
|
+
"coverCard": "F1KBNa_coverCard",
|
|
197
|
+
"coverButton": "F1KBNa_coverButton",
|
|
198
|
+
"colorPickerInput": "F1KBNa_colorPickerInput",
|
|
199
|
+
"splitDivider": "F1KBNa_splitDivider",
|
|
200
|
+
"rowFailed": "F1KBNa_rowFailed",
|
|
201
|
+
"splitHScrollRow": "F1KBNa_splitHScrollRow",
|
|
202
|
+
"diffBodySplit": "F1KBNa_diffBodySplit",
|
|
180
203
|
"diffFlashFade": "F1KBNa_diffFlashFade",
|
|
181
|
-
"
|
|
182
|
-
"
|
|
204
|
+
"splitLadd": "F1KBNa_splitLadd",
|
|
205
|
+
"pickerName": "F1KBNa_pickerName",
|
|
206
|
+
"diffActions": "F1KBNa_diffActions",
|
|
207
|
+
"splitRdel": "F1KBNa_splitRdel",
|
|
208
|
+
"add": "F1KBNa_add",
|
|
209
|
+
"expand": "F1KBNa_expand",
|
|
210
|
+
"overviewMarker": "F1KBNa_overviewMarker",
|
|
211
|
+
"coverNoticeText": "F1KBNa_coverNoticeText",
|
|
212
|
+
"intraAdd": "F1KBNa_intraAdd",
|
|
183
213
|
"splitCols": "F1KBNa_splitCols",
|
|
184
|
-
"
|
|
185
|
-
"
|
|
186
|
-
"colorPicker": "F1KBNa_colorPicker",
|
|
214
|
+
"settingsSelector": "F1KBNa_settingsSelector",
|
|
215
|
+
"pickerIcon": "F1KBNa_pickerIcon",
|
|
187
216
|
"toggle": "F1KBNa_toggle",
|
|
188
|
-
"
|
|
189
|
-
"
|
|
190
|
-
"
|
|
191
|
-
"
|
|
192
|
-
"
|
|
193
|
-
"
|
|
194
|
-
"
|
|
195
|
-
"
|
|
196
|
-
"
|
|
197
|
-
"
|
|
198
|
-
"
|
|
199
|
-
"
|
|
200
|
-
"
|
|
201
|
-
"
|
|
202
|
-
"
|
|
203
|
-
"colorSwatch": "F1KBNa_colorSwatch",
|
|
204
|
-
"diff": "F1KBNa_diff",
|
|
205
|
-
"colorPickerInput": "F1KBNa_colorPickerInput",
|
|
206
|
-
"settingsPage": "F1KBNa_settingsPage"
|
|
217
|
+
"footerButtons": "F1KBNa_footerButtons",
|
|
218
|
+
"stepperValue": "F1KBNa_stepperValue",
|
|
219
|
+
"colorPickerPanel": "F1KBNa_colorPickerPanel",
|
|
220
|
+
"colorValue": "F1KBNa_colorValue",
|
|
221
|
+
"stepper": "F1KBNa_stepper",
|
|
222
|
+
"action": "F1KBNa_action",
|
|
223
|
+
"statusAction": "F1KBNa_statusAction",
|
|
224
|
+
"settingsGroup": "F1KBNa_settingsGroup",
|
|
225
|
+
"blockFlashShake": "F1KBNa_blockFlashShake",
|
|
226
|
+
"fileListFloat": "F1KBNa_fileListFloat",
|
|
227
|
+
"gutter": "F1KBNa_gutter",
|
|
228
|
+
"searchToggleOn": "F1KBNa_searchToggleOn",
|
|
229
|
+
"groupHead": "F1KBNa_groupHead",
|
|
230
|
+
"split": "F1KBNa_split",
|
|
231
|
+
"pickerForm": "F1KBNa_pickerForm"
|
|
207
232
|
};
|
|
208
233
|
//#endregion
|
|
209
234
|
//#region lib/types/client/PathPicker.js
|
|
@@ -553,201 +578,1096 @@ window.__ModuleLoader__.load({
|
|
|
553
578
|
});
|
|
554
579
|
}
|
|
555
580
|
//#endregion
|
|
556
|
-
//#region
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
const addPathNewPos = addPath.oldPos - diagonalPath;
|
|
602
|
-
canAdd = addPath && 0 <= addPathNewPos && addPathNewPos < newLen;
|
|
603
|
-
}
|
|
604
|
-
const canRemove = removePath && removePath.oldPos + 1 < oldLen;
|
|
605
|
-
if (!canAdd && !canRemove) {
|
|
606
|
-
bestPath[diagonalPath] = void 0;
|
|
607
|
-
continue;
|
|
608
|
-
}
|
|
609
|
-
if (!canRemove || canAdd && removePath.oldPos < addPath.oldPos) basePath = this.addToPath(addPath, true, false, 0, options);
|
|
610
|
-
else basePath = this.addToPath(removePath, false, true, 1, options);
|
|
611
|
-
newPos = this.extractCommon(basePath, newTokens, oldTokens, diagonalPath, options);
|
|
612
|
-
if (basePath.oldPos + 1 >= oldLen && newPos + 1 >= newLen) return done(this.buildValues(basePath.lastComponent, newTokens, oldTokens)) || true;
|
|
613
|
-
else {
|
|
614
|
-
bestPath[diagonalPath] = basePath;
|
|
615
|
-
if (basePath.oldPos + 1 >= oldLen) maxDiagonalToConsider = Math.min(maxDiagonalToConsider, diagonalPath - 1);
|
|
616
|
-
if (newPos + 1 >= newLen) minDiagonalToConsider = Math.max(minDiagonalToConsider, diagonalPath + 1);
|
|
617
|
-
}
|
|
618
|
-
}
|
|
619
|
-
editLength++;
|
|
620
|
-
};
|
|
621
|
-
if (callback) (function exec() {
|
|
622
|
-
setTimeout(function() {
|
|
623
|
-
if (editLength > maxEditLength || Date.now() > abortAfterTimestamp) return callback(void 0);
|
|
624
|
-
if (!execEditLength()) exec();
|
|
625
|
-
}, 0);
|
|
626
|
-
})();
|
|
627
|
-
else while (editLength <= maxEditLength && Date.now() <= abortAfterTimestamp) {
|
|
628
|
-
const ret = execEditLength();
|
|
629
|
-
if (ret) return ret;
|
|
630
|
-
}
|
|
631
|
-
}
|
|
632
|
-
addToPath(path, added, removed, oldPosInc, options) {
|
|
633
|
-
const last = path.lastComponent;
|
|
634
|
-
if (last && !options.oneChangePerToken && last.added === added && last.removed === removed) return {
|
|
635
|
-
oldPos: path.oldPos + oldPosInc,
|
|
636
|
-
lastComponent: {
|
|
637
|
-
count: last.count + 1,
|
|
638
|
-
added,
|
|
639
|
-
removed,
|
|
640
|
-
previousComponent: last.previousComponent
|
|
641
|
-
}
|
|
642
|
-
};
|
|
643
|
-
else return {
|
|
644
|
-
oldPos: path.oldPos + oldPosInc,
|
|
645
|
-
lastComponent: {
|
|
646
|
-
count: 1,
|
|
647
|
-
added,
|
|
648
|
-
removed,
|
|
649
|
-
previousComponent: last
|
|
650
|
-
}
|
|
651
|
-
};
|
|
652
|
-
}
|
|
653
|
-
extractCommon(basePath, newTokens, oldTokens, diagonalPath, options) {
|
|
654
|
-
const newLen = newTokens.length, oldLen = oldTokens.length;
|
|
655
|
-
let oldPos = basePath.oldPos, newPos = oldPos - diagonalPath, commonCount = 0;
|
|
656
|
-
while (newPos + 1 < newLen && oldPos + 1 < oldLen && this.equals(oldTokens[oldPos + 1], newTokens[newPos + 1], options)) {
|
|
657
|
-
newPos++;
|
|
658
|
-
oldPos++;
|
|
659
|
-
commonCount++;
|
|
660
|
-
if (options.oneChangePerToken) basePath.lastComponent = {
|
|
661
|
-
count: 1,
|
|
662
|
-
previousComponent: basePath.lastComponent,
|
|
663
|
-
added: false,
|
|
664
|
-
removed: false
|
|
665
|
-
};
|
|
666
|
-
}
|
|
667
|
-
if (commonCount && !options.oneChangePerToken) basePath.lastComponent = {
|
|
668
|
-
count: commonCount,
|
|
669
|
-
previousComponent: basePath.lastComponent,
|
|
670
|
-
added: false,
|
|
671
|
-
removed: false
|
|
672
|
-
};
|
|
673
|
-
basePath.oldPos = oldPos;
|
|
674
|
-
return newPos;
|
|
675
|
-
}
|
|
676
|
-
equals(left, right, options) {
|
|
677
|
-
if (options.comparator) return options.comparator(left, right);
|
|
678
|
-
else return left === right || !!options.ignoreCase && left.toLowerCase() === right.toLowerCase();
|
|
679
|
-
}
|
|
680
|
-
removeEmpty(array) {
|
|
681
|
-
const ret = [];
|
|
682
|
-
for (let i = 0; i < array.length; i++) if (array[i]) ret.push(array[i]);
|
|
683
|
-
return ret;
|
|
684
|
-
}
|
|
685
|
-
castInput(value, options) {
|
|
686
|
-
return value;
|
|
687
|
-
}
|
|
688
|
-
tokenize(value, options) {
|
|
689
|
-
return Array.from(value);
|
|
690
|
-
}
|
|
691
|
-
join(chars) {
|
|
692
|
-
return chars.join("");
|
|
693
|
-
}
|
|
694
|
-
postProcess(changeObjects, options) {
|
|
695
|
-
return changeObjects;
|
|
696
|
-
}
|
|
697
|
-
get useLongestToken() {
|
|
698
|
-
return false;
|
|
699
|
-
}
|
|
700
|
-
buildValues(lastComponent, newTokens, oldTokens) {
|
|
701
|
-
const components = [];
|
|
702
|
-
let nextComponent;
|
|
703
|
-
while (lastComponent) {
|
|
704
|
-
components.push(lastComponent);
|
|
705
|
-
nextComponent = lastComponent.previousComponent;
|
|
706
|
-
delete lastComponent.previousComponent;
|
|
707
|
-
lastComponent = nextComponent;
|
|
708
|
-
}
|
|
709
|
-
components.reverse();
|
|
710
|
-
const componentLen = components.length;
|
|
711
|
-
let componentPos = 0, newPos = 0, oldPos = 0;
|
|
712
|
-
for (; componentPos < componentLen; componentPos++) {
|
|
713
|
-
const component = components[componentPos];
|
|
714
|
-
if (!component.removed) {
|
|
715
|
-
if (!component.added && this.useLongestToken) {
|
|
716
|
-
let value = newTokens.slice(newPos, newPos + component.count);
|
|
717
|
-
value = value.map(function(value, i) {
|
|
718
|
-
const oldValue = oldTokens[oldPos + i];
|
|
719
|
-
return oldValue.length > value.length ? oldValue : value;
|
|
720
|
-
});
|
|
721
|
-
component.value = this.join(value);
|
|
722
|
-
} else component.value = this.join(newTokens.slice(newPos, newPos + component.count));
|
|
723
|
-
newPos += component.count;
|
|
724
|
-
if (!component.added) oldPos += component.count;
|
|
725
|
-
} else {
|
|
726
|
-
component.value = this.join(oldTokens.slice(oldPos, oldPos + component.count));
|
|
727
|
-
oldPos += component.count;
|
|
728
|
-
}
|
|
729
|
-
}
|
|
730
|
-
return components;
|
|
731
|
-
}
|
|
732
|
-
};
|
|
733
|
-
//#endregion
|
|
734
|
-
//#region node_modules/.pnpm/diff@9.0.0/node_modules/diff/libesm/diff/character.js
|
|
735
|
-
var CharacterDiff = class extends Diff {};
|
|
736
|
-
new CharacterDiff();
|
|
737
|
-
//#endregion
|
|
738
|
-
//#region node_modules/.pnpm/diff@9.0.0/node_modules/diff/libesm/util/string.js
|
|
739
|
-
function longestCommonPrefix(str1, str2) {
|
|
740
|
-
let i;
|
|
741
|
-
for (i = 0; i < str1.length && i < str2.length; i++) if (str1[i] != str2[i]) return str1.slice(0, i);
|
|
742
|
-
return str1.slice(0, i);
|
|
581
|
+
//#region lib/types/client/presentation-menu.js
|
|
582
|
+
/**
|
|
583
|
+
* The mode switch: where the panel shows — floating over the app, or docked as a
|
|
584
|
+
* tab in the app's right sidebar. (What the floating panel *covers* is the
|
|
585
|
+
* coverage control's business; see the panel's own header.)
|
|
586
|
+
*
|
|
587
|
+
* One implementation, two hosts. The floating panel draws it in its own header
|
|
588
|
+
* beside the gear. A *docked* panel draws no header at all — the tab is the
|
|
589
|
+
* frame, its chip already carries the title and a close button — so the chip
|
|
590
|
+
* (`sidebar.right.pane.tab.title`, which is this plugin's own component) hosts
|
|
591
|
+
* the same control instead: the seat's convention is a glyph *before* the title
|
|
592
|
+
* text, and the chip's tail is masked for the kit's own close button.
|
|
593
|
+
*
|
|
594
|
+
* @module dsh-diff-approval/client/presentation-menu
|
|
595
|
+
*/
|
|
596
|
+
/**
|
|
597
|
+
* The floating-overlay glyph: a window with another peeking out behind it. The
|
|
598
|
+
* product's icon set has no "float" mark, so this one is drawn to the set's
|
|
599
|
+
* conventions — a 16px grid, ~1.4px ink, `currentColor`.
|
|
600
|
+
* @param props - the drawn size in px.
|
|
601
|
+
* @returns the glyph.
|
|
602
|
+
*/
|
|
603
|
+
function FloatWindowIcon({ size }) {
|
|
604
|
+
return (0, react_jsx_runtime.jsxs)("svg", {
|
|
605
|
+
width: size,
|
|
606
|
+
height: size,
|
|
607
|
+
viewBox: "0 0 16 16",
|
|
608
|
+
fill: "none",
|
|
609
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
610
|
+
"aria-hidden": true,
|
|
611
|
+
children: [(0, react_jsx_runtime.jsx)("path", {
|
|
612
|
+
d: "M5.4 3.2H3.2A1.2 1.2 0 0 0 2 4.4v6.2",
|
|
613
|
+
stroke: "currentColor",
|
|
614
|
+
strokeWidth: "1.4",
|
|
615
|
+
strokeLinecap: "round"
|
|
616
|
+
}), (0, react_jsx_runtime.jsx)("rect", {
|
|
617
|
+
x: "5.4",
|
|
618
|
+
y: "5.4",
|
|
619
|
+
width: "9.4",
|
|
620
|
+
height: "7",
|
|
621
|
+
rx: "1.4",
|
|
622
|
+
stroke: "currentColor",
|
|
623
|
+
strokeWidth: "1.4"
|
|
624
|
+
})]
|
|
625
|
+
});
|
|
743
626
|
}
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
627
|
+
/**
|
|
628
|
+
* The mode switch's mark: the state it currently shows, and the same glyph each
|
|
629
|
+
* menu row leads with. The dock mark is the icon the app's own right sidebar uses
|
|
630
|
+
* (`IconPanelLeftOutline16`, mirrored — see ui-sidebar-right's ExpandButton), so
|
|
631
|
+
* "dock" points at the same affordance the column does.
|
|
632
|
+
* @param props - the state, and the drawn size in px.
|
|
633
|
+
* @returns the glyph.
|
|
634
|
+
*/
|
|
635
|
+
function PresentationIcon({ state, size }) {
|
|
636
|
+
if (state === "dock") return (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPanelLeftOutline16, {
|
|
637
|
+
size,
|
|
638
|
+
className: PendingPanel_module_css_default.mirrored
|
|
639
|
+
});
|
|
640
|
+
return (0, react_jsx_runtime.jsx)(FloatWindowIcon, { size });
|
|
749
641
|
}
|
|
750
|
-
|
|
642
|
+
/**
|
|
643
|
+
* The trigger's wrapper. The header's control says what it is on hover; the
|
|
644
|
+
* chip's carries no tooltip, because that bubble renders exactly where the menu
|
|
645
|
+
* list goes (both hang under the button) and would take the row clicks.
|
|
646
|
+
* @param props - the label to show, when there is one, and the trigger.
|
|
647
|
+
* @returns the trigger, wrapped or bare.
|
|
648
|
+
*/
|
|
649
|
+
function Trigger({ title, children }) {
|
|
650
|
+
if (title === void 0) return children;
|
|
651
|
+
return (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
652
|
+
label: title,
|
|
653
|
+
side: "bottom",
|
|
654
|
+
delayMs: 500,
|
|
655
|
+
children
|
|
656
|
+
});
|
|
657
|
+
}
|
|
658
|
+
/**
|
|
659
|
+
* Render the trigger and its menu. The trigger carries
|
|
660
|
+
* `data-diff-approval-presentation` in both hosts, so a test addresses whichever
|
|
661
|
+
* one the presentation it is testing actually draws.
|
|
662
|
+
* @param props - copy, the current state, what a choice does, and the extras.
|
|
663
|
+
* @returns the trigger element with its dropdown.
|
|
664
|
+
*/
|
|
665
|
+
/** Stop one event from leaving this control (see the boundary note below). */
|
|
666
|
+
function stopAll(event) {
|
|
667
|
+
event.stopPropagation();
|
|
668
|
+
}
|
|
669
|
+
function PresentationMenu({ t, current, onChoose, footer, onFooter, compact = false }) {
|
|
670
|
+
const [open, setOpen] = (0, react.useState)(false);
|
|
671
|
+
const host = compact ? "chip" : "header";
|
|
672
|
+
const title = t("action.presentationCurrent", { state: t(current === "dock" ? "action.presentationDocked" : "action.presentationFloat") });
|
|
673
|
+
const items = (0, react.useMemo)(() => [{
|
|
674
|
+
id: "float",
|
|
675
|
+
label: t("action.presentationFloat"),
|
|
676
|
+
icon: (0, react_jsx_runtime.jsx)(PresentationIcon, {
|
|
677
|
+
state: "float",
|
|
678
|
+
size: 16
|
|
679
|
+
})
|
|
680
|
+
}, {
|
|
681
|
+
id: "dock",
|
|
682
|
+
label: t("action.presentationDock"),
|
|
683
|
+
icon: (0, react_jsx_runtime.jsx)(PresentationIcon, {
|
|
684
|
+
state: "dock",
|
|
685
|
+
size: 16
|
|
686
|
+
})
|
|
687
|
+
}], [t]);
|
|
688
|
+
return (0, react_jsx_runtime.jsx)("span", {
|
|
689
|
+
className: PendingPanel_module_css_default.presentationHost,
|
|
690
|
+
...compact ? {
|
|
691
|
+
onPointerDown: stopAll,
|
|
692
|
+
onPointerUp: stopAll,
|
|
693
|
+
onClick: stopAll,
|
|
694
|
+
onContextMenu: stopAll,
|
|
695
|
+
onMouseDown: stopAll
|
|
696
|
+
} : {},
|
|
697
|
+
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Menu, {
|
|
698
|
+
open,
|
|
699
|
+
portal: true,
|
|
700
|
+
compact: true,
|
|
701
|
+
align: "end",
|
|
702
|
+
items,
|
|
703
|
+
...footer === void 0 ? {} : { footer },
|
|
704
|
+
selectedId: current,
|
|
705
|
+
onSelect: (id) => {
|
|
706
|
+
setOpen(false);
|
|
707
|
+
if (id === "float" || id === "dock") onChoose(id);
|
|
708
|
+
else onFooter?.(id);
|
|
709
|
+
},
|
|
710
|
+
onClose: () => {
|
|
711
|
+
setOpen(false);
|
|
712
|
+
},
|
|
713
|
+
anchor: (0, react_jsx_runtime.jsx)(Trigger, {
|
|
714
|
+
title: compact ? void 0 : title,
|
|
715
|
+
children: (0, react_jsx_runtime.jsxs)("button", {
|
|
716
|
+
type: "button",
|
|
717
|
+
className: compact ? PendingPanel_module_css_default.chipPresentation : PendingPanel_module_css_default.presentationSelect,
|
|
718
|
+
"data-diff-approval-presentation": true,
|
|
719
|
+
"data-diff-approval-presentation-host": host,
|
|
720
|
+
"aria-label": title,
|
|
721
|
+
"aria-haspopup": "menu",
|
|
722
|
+
"aria-expanded": open,
|
|
723
|
+
onPointerDown: (event) => {
|
|
724
|
+
event.stopPropagation();
|
|
725
|
+
},
|
|
726
|
+
onClick: (event) => {
|
|
727
|
+
event.stopPropagation();
|
|
728
|
+
setOpen((value) => !value);
|
|
729
|
+
},
|
|
730
|
+
children: [(0, react_jsx_runtime.jsx)(PresentationIcon, {
|
|
731
|
+
state: current,
|
|
732
|
+
size: compact ? 12 : 14
|
|
733
|
+
}), (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, { size: compact ? 10 : 12 })]
|
|
734
|
+
})
|
|
735
|
+
})
|
|
736
|
+
})
|
|
737
|
+
});
|
|
738
|
+
}
|
|
739
|
+
//#endregion
|
|
740
|
+
//#region lib/types/client/settings.js
|
|
741
|
+
/** Client preferences for the review panel, persisted in localStorage. */
|
|
742
|
+
const PASTE_ON_COPY_KEY = "diff-approval:paste-on-copy";
|
|
743
|
+
const IMPORT_UNTRACKED_KEY = "diff-approval:import-untracked";
|
|
744
|
+
const CONFIRM_FILE_REMOVE_KEY = "diff-approval:confirm-file-remove";
|
|
745
|
+
const SEARCH_CASE_KEY = "diff-approval:search-case";
|
|
746
|
+
const SEARCH_WORD_KEY = "diff-approval:search-word";
|
|
747
|
+
const TAB_WIDTH_KEY = "diff-approval:tab-size";
|
|
748
|
+
const SPLIT_MODE_KEY = "diff-approval:split-mode";
|
|
749
|
+
const MD_PREVIEW_KEY = "diff-approval:md-preview";
|
|
750
|
+
const MD_MAX_WIDTH_KEY = "diff-approval:md-max-width";
|
|
751
|
+
const NAV_LEAD_KEY = "diff-approval:nav-lead-rows";
|
|
752
|
+
const DIFF_FONT_SCALE_KEY = "diff-approval:diff-font-scale";
|
|
753
|
+
const DIFF_LINE_HEIGHT_KEY = "diff-approval:diff-line-height";
|
|
754
|
+
const DIFF_ADD_COLOR_KEY = "diff-approval:diff-add-color";
|
|
755
|
+
const DIFF_DEL_COLOR_KEY = "diff-approval:diff-del-color";
|
|
756
|
+
const LANG_BY_SUFFIX_KEY = "diff-approval:lang-by-suffix";
|
|
757
|
+
const PRESENTATION_KEY = "diff-approval:presentation";
|
|
758
|
+
const FLOAT_COVER_KEY = "diff-approval:float-cover";
|
|
759
|
+
const FILE_LIST_FLOAT_KEY = "diff-approval:file-list-float";
|
|
760
|
+
const WRAP_PREFIX = "diff-approval:wrap:";
|
|
761
|
+
/** The floating panel's default coverage: the header and both sidebars, not the
|
|
762
|
+
* composer. */
|
|
763
|
+
const FLOAT_COVER_DEFAULT = {
|
|
764
|
+
top: true,
|
|
765
|
+
left: true,
|
|
766
|
+
right: true,
|
|
767
|
+
composer: false
|
|
768
|
+
};
|
|
769
|
+
const MD_MAX_WIDTH_MAX = 1600;
|
|
770
|
+
/**
|
|
771
|
+
* Whether copying a reference should also paste it into the chat input and
|
|
772
|
+
* focus it. Defaults to on; only an explicit `'0'` disables it.
|
|
773
|
+
* @returns whether auto-paste is enabled.
|
|
774
|
+
*/
|
|
775
|
+
function pasteOnCopyEnabled() {
|
|
776
|
+
return localStorage.getItem(PASTE_ON_COPY_KEY) !== "0";
|
|
777
|
+
}
|
|
778
|
+
/** Persist the auto-paste preference. */
|
|
779
|
+
function setPasteOnCopyEnabled(value) {
|
|
780
|
+
localStorage.setItem(PASTE_ON_COPY_KEY, value ? "1" : "0");
|
|
781
|
+
}
|
|
782
|
+
/**
|
|
783
|
+
* Whether importing workspace VCS changes includes new/untracked files (git
|
|
784
|
+
* `??`, svn `?`, p4 unversioned). Defaults to off: collecting them scans the
|
|
785
|
+
* whole workspace, which can be slow on large trees, so the preference is
|
|
786
|
+
* opt-in. Only an explicit `'1'` enables it.
|
|
787
|
+
* @returns whether untracked files are imported.
|
|
788
|
+
*/
|
|
789
|
+
function includeUntrackedEnabled() {
|
|
790
|
+
return localStorage.getItem(IMPORT_UNTRACKED_KEY) === "1";
|
|
791
|
+
}
|
|
792
|
+
/** Persist the import-untracked preference. */
|
|
793
|
+
function setIncludeUntrackedEnabled(value) {
|
|
794
|
+
localStorage.setItem(IMPORT_UNTRACKED_KEY, value ? "1" : "0");
|
|
795
|
+
}
|
|
796
|
+
/**
|
|
797
|
+
* Whether a whole-file keep/revert asks before dropping the resolved file from
|
|
798
|
+
* the list. Defaults to on; only an explicit `'0'` disables it, which then
|
|
799
|
+
* removes the file straight away.
|
|
800
|
+
* @returns whether the remove prompt is enabled.
|
|
801
|
+
*/
|
|
802
|
+
function confirmFileRemoveEnabled() {
|
|
803
|
+
return localStorage.getItem(CONFIRM_FILE_REMOVE_KEY) !== "0";
|
|
804
|
+
}
|
|
805
|
+
/** Persist the whole-file remove-prompt preference. */
|
|
806
|
+
function setConfirmFileRemoveEnabled(value) {
|
|
807
|
+
localStorage.setItem(CONFIRM_FILE_REMOVE_KEY, value ? "1" : "0");
|
|
808
|
+
}
|
|
809
|
+
/**
|
|
810
|
+
* Whether the in-file search matches letter case exactly. Defaults to off, so a
|
|
811
|
+
* plain query keeps matching either case; only an explicit `'1'` turns it on.
|
|
812
|
+
* @returns whether the search is case-sensitive.
|
|
813
|
+
*/
|
|
814
|
+
function searchCaseSensitive() {
|
|
815
|
+
return localStorage.getItem(SEARCH_CASE_KEY) === "1";
|
|
816
|
+
}
|
|
817
|
+
/** Persist the search case-sensitivity preference. */
|
|
818
|
+
function setSearchCaseSensitive(value) {
|
|
819
|
+
localStorage.setItem(SEARCH_CASE_KEY, value ? "1" : "0");
|
|
820
|
+
}
|
|
821
|
+
/**
|
|
822
|
+
* Whether the in-file search only matches whole words. Defaults to off; only an
|
|
823
|
+
* explicit `'1'` enables it.
|
|
824
|
+
* @returns whether the search matches whole words only.
|
|
825
|
+
*/
|
|
826
|
+
function searchWholeWord() {
|
|
827
|
+
return localStorage.getItem(SEARCH_WORD_KEY) === "1";
|
|
828
|
+
}
|
|
829
|
+
/** Persist the search whole-word preference. */
|
|
830
|
+
function setSearchWholeWord(value) {
|
|
831
|
+
localStorage.setItem(SEARCH_WORD_KEY, value ? "1" : "0");
|
|
832
|
+
}
|
|
833
|
+
/**
|
|
834
|
+
* Where the review panel shows, as the user last had it. Two states, both
|
|
835
|
+
* remembered so the footer entry can restore the one they were using:
|
|
836
|
+
*
|
|
837
|
+
* - `float` — the panel floating over the app, covering what
|
|
838
|
+
* {@link panelCover} says (and the composer only when that says so);
|
|
839
|
+
* - `dock` — the panel docked in the app's right sidebar, as its own tab.
|
|
840
|
+
*
|
|
841
|
+
* A stored `fullscreen` — the third state before coverage became three switches —
|
|
842
|
+
* reads as `float`, with {@link panelCover} supplying the all-on coverage it
|
|
843
|
+
* meant.
|
|
844
|
+
* @returns the remembered presentation; `float` when unset or unrecognized.
|
|
845
|
+
*/
|
|
846
|
+
function panelPresentation() {
|
|
847
|
+
return localStorage.getItem(PRESENTATION_KEY) === "dock" ? "dock" : "float";
|
|
848
|
+
}
|
|
849
|
+
/** Persist the panel's presentation (see {@link panelPresentation}). */
|
|
850
|
+
function setPanelPresentation(value) {
|
|
851
|
+
localStorage.setItem(PRESENTATION_KEY, value);
|
|
852
|
+
}
|
|
853
|
+
/**
|
|
854
|
+
* What the floating panel covers (see {@link DiffApprovalCover}). Defaults to
|
|
855
|
+
* both sidebars and not the composer; a stored `fullscreen` presentation from
|
|
856
|
+
* before the switches means all three.
|
|
857
|
+
* @returns the remembered coverage.
|
|
858
|
+
*/
|
|
859
|
+
function panelCover() {
|
|
860
|
+
const raw = localStorage.getItem(FLOAT_COVER_KEY);
|
|
861
|
+
if (raw === null) return localStorage.getItem(PRESENTATION_KEY) === "fullscreen" ? {
|
|
862
|
+
top: true,
|
|
863
|
+
left: true,
|
|
864
|
+
right: true,
|
|
865
|
+
composer: true
|
|
866
|
+
} : FLOAT_COVER_DEFAULT;
|
|
867
|
+
try {
|
|
868
|
+
const parsed = JSON.parse(raw);
|
|
869
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return FLOAT_COVER_DEFAULT;
|
|
870
|
+
const stored = parsed;
|
|
871
|
+
const flag = (name) => typeof stored[name] === "boolean" ? stored[name] : FLOAT_COVER_DEFAULT[name];
|
|
872
|
+
return {
|
|
873
|
+
top: flag("top"),
|
|
874
|
+
left: flag("left"),
|
|
875
|
+
right: flag("right"),
|
|
876
|
+
composer: flag("composer")
|
|
877
|
+
};
|
|
878
|
+
} catch {
|
|
879
|
+
return FLOAT_COVER_DEFAULT;
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
/** Window event: the floating panel's coverage changed (see {@link setPanelCover}). */
|
|
883
|
+
const COVER_CHANGED_EVENT = "diff-approval:cover";
|
|
884
|
+
/** Persist what the floating panel covers. */
|
|
885
|
+
function setPanelCover(value) {
|
|
886
|
+
localStorage.setItem(FLOAT_COVER_KEY, JSON.stringify(value));
|
|
887
|
+
if (typeof window !== "undefined") window.dispatchEvent(new CustomEvent(COVER_CHANGED_EVENT));
|
|
888
|
+
}
|
|
889
|
+
/**
|
|
890
|
+
* Whether the file list always folds into its floating card, whatever width the
|
|
891
|
+
* panel has. Defaults to off, where the width decides: the list sits beside the
|
|
892
|
+
* diff while there is room for both. Only an explicit `'1'` forces the fold.
|
|
893
|
+
* @returns whether the file list is always folded.
|
|
894
|
+
*/
|
|
895
|
+
function fileListFloat() {
|
|
896
|
+
return localStorage.getItem(FILE_LIST_FLOAT_KEY) === "1";
|
|
897
|
+
}
|
|
898
|
+
/** Persist the always-fold preference for the file list. */
|
|
899
|
+
function setFileListFloat(value) {
|
|
900
|
+
localStorage.setItem(FILE_LIST_FLOAT_KEY, value ? "1" : "0");
|
|
901
|
+
}
|
|
902
|
+
/**
|
|
903
|
+
* Whether lines wrap (auto-wrap) in the diff for one highlight language.
|
|
904
|
+
* Defaults to off; only an explicit `'1'` enables it. Stored per language, so
|
|
905
|
+
* a language's preference never leaks into another's.
|
|
906
|
+
* @param lang - the highlight language (or `''` for the auto/default bucket).
|
|
907
|
+
* @returns whether lines wrap.
|
|
908
|
+
*/
|
|
909
|
+
function wrapEnabled(lang) {
|
|
910
|
+
return localStorage.getItem(`${WRAP_PREFIX}${lang}`) === "1";
|
|
911
|
+
}
|
|
912
|
+
/** Persist the per-language auto-wrap preference. */
|
|
913
|
+
function setWrapEnabled(lang, value) {
|
|
914
|
+
localStorage.setItem(`${WRAP_PREFIX}${lang}`, value ? "1" : "0");
|
|
915
|
+
}
|
|
916
|
+
/**
|
|
917
|
+
* The diff's tab width in spaces. Defaults to 4; the settings UI offers 2/4/8,
|
|
918
|
+
* but any positive integer is accepted. This drives both the rendered
|
|
919
|
+
* `tab-size` and the wrapped-line tab measurement, so they always agree.
|
|
920
|
+
* @returns the number of spaces one tab advances.
|
|
921
|
+
*/
|
|
922
|
+
function tabWidth() {
|
|
923
|
+
const value = Number.parseInt(localStorage.getItem(TAB_WIDTH_KEY) ?? "", 10);
|
|
924
|
+
return Number.isInteger(value) && value > 0 ? value : 4;
|
|
925
|
+
}
|
|
926
|
+
/** Persist the diff's tab width (in spaces). */
|
|
927
|
+
function setTabWidth(value) {
|
|
928
|
+
localStorage.setItem(TAB_WIDTH_KEY, String(value));
|
|
929
|
+
}
|
|
930
|
+
/**
|
|
931
|
+
* The diff code font size as a percentage of the current (theme) size. Defaults
|
|
932
|
+
* to 100 (the current look); the settings UI steps it by ±10.
|
|
933
|
+
* @returns the font-size scale, as a percentage (e.g. 100, 110, 90).
|
|
934
|
+
*/
|
|
935
|
+
function diffFontScale() {
|
|
936
|
+
const raw = Number.parseInt(localStorage.getItem(DIFF_FONT_SCALE_KEY) ?? "", 10);
|
|
937
|
+
if (!Number.isFinite(raw)) return 100;
|
|
938
|
+
return Math.max(50, Math.min(200, raw));
|
|
939
|
+
}
|
|
940
|
+
/** Persist the diff's code font-size scale (a percentage). */
|
|
941
|
+
function setDiffFontScale(value) {
|
|
942
|
+
localStorage.setItem(DIFF_FONT_SCALE_KEY, String(Math.max(50, Math.min(200, value))));
|
|
943
|
+
}
|
|
944
|
+
/**
|
|
945
|
+
* The diff code line height in px (the fixed row height the virtual window and
|
|
946
|
+
* jump math are keyed to). Defaults to 22 (the pre-customization value).
|
|
947
|
+
* @returns the line height in px.
|
|
948
|
+
*/
|
|
949
|
+
function diffLineHeight() {
|
|
950
|
+
const raw = Number.parseInt(localStorage.getItem(DIFF_LINE_HEIGHT_KEY) ?? "", 10);
|
|
951
|
+
if (!Number.isFinite(raw)) return 22;
|
|
952
|
+
return Math.max(10, Math.min(36, raw));
|
|
953
|
+
}
|
|
954
|
+
/** Persist the diff's code line height (px). */
|
|
955
|
+
function setDiffLineHeight(value) {
|
|
956
|
+
localStorage.setItem(DIFF_LINE_HEIGHT_KEY, String(Math.max(10, Math.min(36, value))));
|
|
957
|
+
}
|
|
958
|
+
/** Read a theme CSS custom property, with a fallback when it is unavailable. */
|
|
959
|
+
function themeColor(name, fallback) {
|
|
960
|
+
if (typeof document !== "undefined") {
|
|
961
|
+
const v = getComputedStyle(document.documentElement).getPropertyValue(name).trim();
|
|
962
|
+
if (/^#[0-9a-f]{6}$/i.test(v)) return v;
|
|
963
|
+
}
|
|
964
|
+
return fallback;
|
|
965
|
+
}
|
|
966
|
+
/** The added-line base color the user chose, or `undefined` (theme color). */
|
|
967
|
+
function diffAddColor() {
|
|
968
|
+
const v = localStorage.getItem(DIFF_ADD_COLOR_KEY);
|
|
969
|
+
return v !== null && v.length > 0 ? v : void 0;
|
|
970
|
+
}
|
|
971
|
+
/** Persist the added-line base color (a hex like `#22c55e`). */
|
|
972
|
+
function setDiffAddColor(value) {
|
|
973
|
+
localStorage.setItem(DIFF_ADD_COLOR_KEY, value);
|
|
974
|
+
}
|
|
975
|
+
/** The removed-line base color the user chose, or `undefined` (theme color). */
|
|
976
|
+
function diffDelColor() {
|
|
977
|
+
const v = localStorage.getItem(DIFF_DEL_COLOR_KEY);
|
|
978
|
+
return v !== null && v.length > 0 ? v : void 0;
|
|
979
|
+
}
|
|
980
|
+
/** Persist the removed-line base color (a hex like `#ef4444`). */
|
|
981
|
+
function setDiffDelColor(value) {
|
|
982
|
+
localStorage.setItem(DIFF_DEL_COLOR_KEY, value);
|
|
983
|
+
}
|
|
984
|
+
/** The theme's added-line base color, for the settings swatch default. */
|
|
985
|
+
function currentDiffAddColor() {
|
|
986
|
+
return themeColor("--dsw-alias-state-success-primary", "#22c55e");
|
|
987
|
+
}
|
|
988
|
+
/** The theme's removed-line base color, for the settings swatch default. */
|
|
989
|
+
function currentDiffDelColor() {
|
|
990
|
+
return themeColor("--dsw-alias-state-error-primary", "#ef4444");
|
|
991
|
+
}
|
|
992
|
+
/**
|
|
993
|
+
* The highlight language chosen by hand for one file suffix, remembered so the
|
|
994
|
+
* choice sticks for every file with that suffix. Not a Settings row: it is a
|
|
995
|
+
* per-suffix consequence of using the language picker, so it lives here as
|
|
996
|
+
* storage only.
|
|
997
|
+
* @param suffix - the lowercase extension without the dot (see `suffixOfPath`).
|
|
998
|
+
* @returns the remembered language id, or undefined for "auto".
|
|
999
|
+
*/
|
|
1000
|
+
function languageForSuffix(suffix) {
|
|
1001
|
+
const language = rememberedLanguages()[suffix];
|
|
1002
|
+
return language === void 0 || language === "" ? void 0 : language;
|
|
1003
|
+
}
|
|
1004
|
+
/**
|
|
1005
|
+
* Remember (or clear) the language chosen for one suffix. A `null` language
|
|
1006
|
+
* forgets the suffix, which is what picking "auto" means.
|
|
1007
|
+
* @param suffix - the lowercase extension without the dot.
|
|
1008
|
+
* @param language - the language id, or null to forget the suffix.
|
|
1009
|
+
*/
|
|
1010
|
+
function setLanguageForSuffix(suffix, language) {
|
|
1011
|
+
const stored = rememberedLanguages();
|
|
1012
|
+
if (language === null) {
|
|
1013
|
+
if (stored[suffix] === void 0) return;
|
|
1014
|
+
delete stored[suffix];
|
|
1015
|
+
} else {
|
|
1016
|
+
if (stored[suffix] === language) return;
|
|
1017
|
+
stored[suffix] = language;
|
|
1018
|
+
}
|
|
1019
|
+
localStorage.setItem(LANG_BY_SUFFIX_KEY, JSON.stringify(stored));
|
|
1020
|
+
}
|
|
1021
|
+
/** The whole suffix → language map (empty when unset or unreadable). */
|
|
1022
|
+
function rememberedLanguages() {
|
|
1023
|
+
const raw = localStorage.getItem(LANG_BY_SUFFIX_KEY);
|
|
1024
|
+
if (raw === null) return {};
|
|
1025
|
+
try {
|
|
1026
|
+
const parsed = JSON.parse(raw);
|
|
1027
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return {};
|
|
1028
|
+
const cleaned = {};
|
|
1029
|
+
for (const [suffix, language] of Object.entries(parsed)) if (typeof language === "string" && language !== "") cleaned[suffix] = language;
|
|
1030
|
+
return cleaned;
|
|
1031
|
+
} catch {
|
|
1032
|
+
return {};
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
/**
|
|
1036
|
+
* Whether the whole-file diff view uses the two-column (side-by-side) layout.
|
|
1037
|
+
* Default off (single column): the unified diff. Only an explicit `'1'` enables
|
|
1038
|
+
* split mode.
|
|
1039
|
+
* @returns whether the split (two-column) diff view is used.
|
|
1040
|
+
*/
|
|
1041
|
+
function splitMode() {
|
|
1042
|
+
return localStorage.getItem(SPLIT_MODE_KEY) === "1";
|
|
1043
|
+
}
|
|
1044
|
+
/** Persist the split-view preference. */
|
|
1045
|
+
function setSplitMode(value) {
|
|
1046
|
+
localStorage.setItem(SPLIT_MODE_KEY, value ? "1" : "0");
|
|
1047
|
+
}
|
|
1048
|
+
/**
|
|
1049
|
+
* Whether the rendered Markdown preview is shown by default (for Markdown files).
|
|
1050
|
+
* Default off: the source diff is shown unless the panel toggle is used. Only an
|
|
1051
|
+
* explicit `'1'` enables the preview default.
|
|
1052
|
+
* @returns whether the Markdown preview default is enabled.
|
|
1053
|
+
*/
|
|
1054
|
+
function mdPreviewEnabled() {
|
|
1055
|
+
return localStorage.getItem(MD_PREVIEW_KEY) === "1";
|
|
1056
|
+
}
|
|
1057
|
+
/** Persist the Markdown-preview default preference. */
|
|
1058
|
+
function setMdPreviewEnabled(value) {
|
|
1059
|
+
localStorage.setItem(MD_PREVIEW_KEY, value ? "1" : "0");
|
|
1060
|
+
}
|
|
1061
|
+
/**
|
|
1062
|
+
* The Markdown-preview content max width, in pixels (single column). Defaults to
|
|
1063
|
+
* 800; the double-column view totals 2x this. An out-of-range or non-integer
|
|
1064
|
+
* value falls back to the default.
|
|
1065
|
+
* @returns the max width in pixels.
|
|
1066
|
+
*/
|
|
1067
|
+
function mdMaxWidth() {
|
|
1068
|
+
const raw = Number.parseInt(localStorage.getItem(MD_MAX_WIDTH_KEY) ?? "", 10);
|
|
1069
|
+
if (!Number.isInteger(raw)) return 800;
|
|
1070
|
+
return Math.max(480, Math.min(MD_MAX_WIDTH_MAX, raw));
|
|
1071
|
+
}
|
|
1072
|
+
/** Persist the Markdown-preview content max width. */
|
|
1073
|
+
function setMdMaxWidth(value) {
|
|
1074
|
+
localStorage.setItem(MD_MAX_WIDTH_KEY, String(Math.max(480, Math.min(MD_MAX_WIDTH_MAX, value))));
|
|
1075
|
+
}
|
|
1076
|
+
/**
|
|
1077
|
+
* How many rows of lead the diff block jump leaves above the jumped-to block,
|
|
1078
|
+
* and how far the anchored navigation scans. Defaults to 2; an out-of-range or
|
|
1079
|
+
* non-integer value falls back to the default.
|
|
1080
|
+
* @returns the lead row count.
|
|
1081
|
+
*/
|
|
1082
|
+
function navLeadRows() {
|
|
1083
|
+
const raw = Number.parseInt(localStorage.getItem(NAV_LEAD_KEY) ?? "", 10);
|
|
1084
|
+
if (!Number.isInteger(raw)) return 2;
|
|
1085
|
+
return Math.max(0, Math.min(10, raw));
|
|
1086
|
+
}
|
|
1087
|
+
/** Persist the block-jump lead row count. */
|
|
1088
|
+
function setNavLeadRows(value) {
|
|
1089
|
+
localStorage.setItem(NAV_LEAD_KEY, String(Math.max(0, Math.min(10, value))));
|
|
1090
|
+
}
|
|
1091
|
+
const QUICK_SUMMON_KEY = "diff-approval:quick-summon-key";
|
|
1092
|
+
/**
|
|
1093
|
+
* The quick-summon chord. Stored as `Modifier+...+Key`; falls back to
|
|
1094
|
+
* {@link DEFAULT_QUICK_SUMMON}.
|
|
1095
|
+
* @returns the chord string.
|
|
1096
|
+
*/
|
|
1097
|
+
function quickSummonKey() {
|
|
1098
|
+
return localStorage.getItem(QUICK_SUMMON_KEY) ?? "Ctrl+D";
|
|
1099
|
+
}
|
|
1100
|
+
/** Persist the quick-summon chord. */
|
|
1101
|
+
function setQuickSummonKey(value) {
|
|
1102
|
+
localStorage.setItem(QUICK_SUMMON_KEY, value);
|
|
1103
|
+
}
|
|
1104
|
+
const KEY_PREFIX = "diff-approval:key:";
|
|
1105
|
+
/** Default chord for each configurable action (every supported key except the
|
|
1106
|
+
* panel's own ESC-to-close, which is intentionally not remapped). The coverage
|
|
1107
|
+
* switches are keyed like the edges they toggle, in the order the panel lists
|
|
1108
|
+
* them: left, top, right, bottom. */
|
|
1109
|
+
const DEFAULT_KEYBINDINGS = {
|
|
1110
|
+
jumpUp: "Ctrl+ArrowUp",
|
|
1111
|
+
jumpDown: "Ctrl+ArrowDown",
|
|
1112
|
+
copyRef: "Ctrl+L",
|
|
1113
|
+
openSearch: "Ctrl+F",
|
|
1114
|
+
searchNext: "F3",
|
|
1115
|
+
searchPrev: "Shift+F3",
|
|
1116
|
+
matchCase: "Alt+C",
|
|
1117
|
+
matchWholeWord: "Alt+W",
|
|
1118
|
+
undo: "Ctrl+Z",
|
|
1119
|
+
redo: "Ctrl+Shift+Z",
|
|
1120
|
+
cycleNext: "Ctrl+Tab",
|
|
1121
|
+
cyclePrev: "Ctrl+Shift+Tab",
|
|
1122
|
+
coverLeft: "Ctrl+Shift+ArrowLeft",
|
|
1123
|
+
coverTop: "Ctrl+Shift+ArrowUp",
|
|
1124
|
+
coverRight: "Ctrl+Shift+ArrowRight",
|
|
1125
|
+
coverComposer: "Ctrl+Shift+ArrowDown"
|
|
1126
|
+
};
|
|
1127
|
+
/** The currently configured chord for one action; falls back to its default. */
|
|
1128
|
+
function keybindingOf(action) {
|
|
1129
|
+
return localStorage.getItem(`${KEY_PREFIX}${action}`) ?? DEFAULT_KEYBINDINGS[action] ?? "";
|
|
1130
|
+
}
|
|
1131
|
+
/** Persist one action's chord. */
|
|
1132
|
+
function setKeybinding(action, chord) {
|
|
1133
|
+
localStorage.setItem(`${KEY_PREFIX}${action}`, chord);
|
|
1134
|
+
}
|
|
1135
|
+
/**
|
|
1136
|
+
* Whether a keyboard event matches a chord string like `Ctrl+D`. Modifier
|
|
1137
|
+
* names are matched case-insensitively (`Ctrl`/`Control`, `Alt`/`Option`,
|
|
1138
|
+
* `Shift`, `Meta`/`Cmd`/`Command`/`Win`); the final part is the key. Exact
|
|
1139
|
+
* modifier set is required (extra modifiers do not match).
|
|
1140
|
+
* @param event - the keydown event.
|
|
1141
|
+
* @param shortcut - the chord string.
|
|
1142
|
+
* @returns whether the event matches.
|
|
1143
|
+
*/
|
|
1144
|
+
function matchesShortcut(event, shortcut) {
|
|
1145
|
+
const parts = shortcut.split("+").map((part) => part.trim().toLowerCase());
|
|
1146
|
+
const key = parts.pop();
|
|
1147
|
+
if (key === void 0 || key === "") return false;
|
|
1148
|
+
const mods = new Set(parts);
|
|
1149
|
+
const ctrl = mods.has("ctrl") || mods.has("control");
|
|
1150
|
+
const alt = mods.has("alt") || mods.has("option");
|
|
1151
|
+
const shift = mods.has("shift");
|
|
1152
|
+
const meta = mods.has("meta") || mods.has("cmd") || mods.has("command") || mods.has("win");
|
|
1153
|
+
return event.key.toLowerCase() === key && event.ctrlKey === ctrl && event.altKey === alt && event.shiftKey === shift && event.metaKey === meta;
|
|
1154
|
+
}
|
|
1155
|
+
//#endregion
|
|
1156
|
+
//#region lib/types/client/chords.js
|
|
1157
|
+
/**
|
|
1158
|
+
* How a keybinding is spelled in a tooltip.
|
|
1159
|
+
*
|
|
1160
|
+
* Every hint reads the binding at render time rather than baking in a default, so
|
|
1161
|
+
* a rebind in Settings shows up everywhere the chord is advertised. That is also
|
|
1162
|
+
* why the strings live here: the panel's header, its diff toolbars, and the
|
|
1163
|
+
* coverage popover all advertise chords, and they must spell them the same way.
|
|
1164
|
+
*
|
|
1165
|
+
* @module dsh-diff-approval/client/chords
|
|
1166
|
+
*/
|
|
1167
|
+
/** Arrow keys render as arrows in a hint (`Ctrl+↑`, not `Ctrl+ArrowUp`). */
|
|
1168
|
+
const CHORD_KEY_GLYPHS = {
|
|
1169
|
+
ArrowUp: "↑",
|
|
1170
|
+
ArrowDown: "↓",
|
|
1171
|
+
ArrowLeft: "←",
|
|
1172
|
+
ArrowRight: "→"
|
|
1173
|
+
};
|
|
1174
|
+
/**
|
|
1175
|
+
* One stored chord as a hint renders it: modifiers as written, arrow keys as
|
|
1176
|
+
* glyphs (`Ctrl+ArrowUp` → `Ctrl+↑`).
|
|
1177
|
+
* @param chord - the stored chord.
|
|
1178
|
+
* @returns the hint text; `''` for an unbound action.
|
|
1179
|
+
*/
|
|
1180
|
+
function chordHint(chord) {
|
|
1181
|
+
return chord.split("+").map((part) => CHORD_KEY_GLYPHS[part] ?? part).join("+");
|
|
1182
|
+
}
|
|
1183
|
+
/**
|
|
1184
|
+
* One action's chord as a hint.
|
|
1185
|
+
* @param action - the keybinding action id (see `DEFAULT_KEYBINDINGS`).
|
|
1186
|
+
* @returns the hint text; `''` when the action has no chord.
|
|
1187
|
+
*/
|
|
1188
|
+
function chordLabel$1(action) {
|
|
1189
|
+
return chordHint(keybindingOf(action));
|
|
1190
|
+
}
|
|
1191
|
+
/**
|
|
1192
|
+
* A tooltip label with the action's configured chord appended. The hint states
|
|
1193
|
+
* the binding the user actually has — including one rebound in Settings, or none
|
|
1194
|
+
* at all, which simply adds nothing.
|
|
1195
|
+
* @param label - the translated action label.
|
|
1196
|
+
* @param action - the keybinding action id (see `DEFAULT_KEYBINDINGS`).
|
|
1197
|
+
* @returns the label, with ` (chord)` appended when one is configured.
|
|
1198
|
+
*/
|
|
1199
|
+
function withChord(label, action) {
|
|
1200
|
+
const chord = chordLabel$1(action);
|
|
1201
|
+
return chord === "" ? label : `${label} (${chord})`;
|
|
1202
|
+
}
|
|
1203
|
+
/**
|
|
1204
|
+
* How the panel's close button names its two ways out: Escape, and the
|
|
1205
|
+
* quick-summon chord when the user still has one bound. Unbinding the chord
|
|
1206
|
+
* leaves Escape as the only answer, so the hint then says just that.
|
|
1207
|
+
* @param t - the panel's translator.
|
|
1208
|
+
* @returns the tooltip label.
|
|
1209
|
+
*/
|
|
1210
|
+
function closeHint(t) {
|
|
1211
|
+
const hint = chordHint(quickSummonKey());
|
|
1212
|
+
return hint === "" ? t("action.closeHintEsc") : t("action.closeHint", { chord: hint });
|
|
1213
|
+
}
|
|
1214
|
+
/**
|
|
1215
|
+
* How an entry button names what it opens: the pending-changes panel, plus the
|
|
1216
|
+
* chord that does the same. With the chord unbound the plain label is the whole
|
|
1217
|
+
* hint — never an empty parenthetical.
|
|
1218
|
+
* @param t - the panel's translator.
|
|
1219
|
+
* @returns the tooltip label.
|
|
1220
|
+
*/
|
|
1221
|
+
function summonHint(t) {
|
|
1222
|
+
const hint = chordHint(quickSummonKey());
|
|
1223
|
+
return hint === "" ? t("panel.aria") : t("action.summonHint", { chord: hint });
|
|
1224
|
+
}
|
|
1225
|
+
//#endregion
|
|
1226
|
+
//#region lib/types/client/coverage-control.js
|
|
1227
|
+
/**
|
|
1228
|
+
* The floating panel's coverage control: one header button, and a popover of
|
|
1229
|
+
* three glyph buttons — cover the left sidebar, the composer below, the right
|
|
1230
|
+
* sidebar. All three on is what the panel used to call "fullscreen".
|
|
1231
|
+
*
|
|
1232
|
+
* Each glyph is the same rounded rectangle with one band highlighted, drawn to
|
|
1233
|
+
* the product's icon conventions (16px grid, ~1.4px ink, `currentColor`): the
|
|
1234
|
+
* band is filled while that side is covered and hollow while it is not, so the
|
|
1235
|
+
* shape says *which* edge and the fill says whether it is on. The popover is a
|
|
1236
|
+
* row rather than the kit's vertical menu because these are three independent
|
|
1237
|
+
* switches, not a list of choices.
|
|
1238
|
+
*
|
|
1239
|
+
* @module dsh-diff-approval/client/coverage-control
|
|
1240
|
+
*/
|
|
1241
|
+
/** The switches in the popover, in the order the panel names them: left, top,
|
|
1242
|
+
* right, bottom. */
|
|
1243
|
+
const COVER_ROWS = [
|
|
1244
|
+
{
|
|
1245
|
+
key: "left",
|
|
1246
|
+
action: "coverLeft",
|
|
1247
|
+
label: "cover.left"
|
|
1248
|
+
},
|
|
1249
|
+
{
|
|
1250
|
+
key: "top",
|
|
1251
|
+
action: "coverTop",
|
|
1252
|
+
label: "cover.top"
|
|
1253
|
+
},
|
|
1254
|
+
{
|
|
1255
|
+
key: "right",
|
|
1256
|
+
action: "coverRight",
|
|
1257
|
+
label: "cover.right"
|
|
1258
|
+
},
|
|
1259
|
+
{
|
|
1260
|
+
key: "composer",
|
|
1261
|
+
action: "coverComposer",
|
|
1262
|
+
label: "cover.composer"
|
|
1263
|
+
}
|
|
1264
|
+
];
|
|
1265
|
+
/** The rectangle every coverage glyph is built on: a wide panel in the 16 grid. */
|
|
1266
|
+
const FRAME = {
|
|
1267
|
+
x: 1.4,
|
|
1268
|
+
y: 3,
|
|
1269
|
+
w: 13.2,
|
|
1270
|
+
h: 10,
|
|
1271
|
+
r: 1.6
|
|
1272
|
+
};
|
|
1273
|
+
/** How thick the highlighted band is, along the edge it marks. */
|
|
1274
|
+
const BAND = 3.4;
|
|
1275
|
+
/**
|
|
1276
|
+
* One coverage glyph: the frame, with the edge named by `side` marked — filled
|
|
1277
|
+
* while that side is covered, hollow while it is not.
|
|
1278
|
+
* @param props - which edge, whether it is on, and the drawn size in px.
|
|
1279
|
+
* @returns the glyph.
|
|
1280
|
+
*/
|
|
1281
|
+
function CoverEdgeIcon({ side, on, size }) {
|
|
1282
|
+
const { x, y, w, h } = FRAME;
|
|
1283
|
+
const band = side === "left" ? {
|
|
1284
|
+
x,
|
|
1285
|
+
y,
|
|
1286
|
+
width: BAND,
|
|
1287
|
+
height: h
|
|
1288
|
+
} : side === "right" ? {
|
|
1289
|
+
x: x + w - BAND,
|
|
1290
|
+
y,
|
|
1291
|
+
width: BAND,
|
|
1292
|
+
height: h
|
|
1293
|
+
} : side === "top" ? {
|
|
1294
|
+
x,
|
|
1295
|
+
y,
|
|
1296
|
+
width: w,
|
|
1297
|
+
height: BAND
|
|
1298
|
+
} : {
|
|
1299
|
+
x,
|
|
1300
|
+
y: y + h - BAND,
|
|
1301
|
+
width: w,
|
|
1302
|
+
height: BAND
|
|
1303
|
+
};
|
|
1304
|
+
return (0, react_jsx_runtime.jsxs)("svg", {
|
|
1305
|
+
width: size,
|
|
1306
|
+
height: size,
|
|
1307
|
+
viewBox: "0 0 16 16",
|
|
1308
|
+
fill: "none",
|
|
1309
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
1310
|
+
"aria-hidden": true,
|
|
1311
|
+
children: [(0, react_jsx_runtime.jsx)("rect", {
|
|
1312
|
+
x,
|
|
1313
|
+
y,
|
|
1314
|
+
width: w,
|
|
1315
|
+
height: h,
|
|
1316
|
+
rx: FRAME.r,
|
|
1317
|
+
stroke: "currentColor",
|
|
1318
|
+
strokeWidth: "1.4"
|
|
1319
|
+
}), (0, react_jsx_runtime.jsx)("rect", {
|
|
1320
|
+
x: band.x,
|
|
1321
|
+
y: band.y,
|
|
1322
|
+
width: band.width,
|
|
1323
|
+
height: band.height,
|
|
1324
|
+
rx: side === "composer" || side === "top" ? .8 : FRAME.r - .4,
|
|
1325
|
+
...on ? { fill: "currentColor" } : {
|
|
1326
|
+
stroke: "currentColor",
|
|
1327
|
+
strokeWidth: "1.4"
|
|
1328
|
+
}
|
|
1329
|
+
})]
|
|
1330
|
+
});
|
|
1331
|
+
}
|
|
1332
|
+
/**
|
|
1333
|
+
* The coverage control's own mark: a frame with its four corners drawn, reading
|
|
1334
|
+
* as "how much of the frame the panel takes" — distinct from the two-arrow
|
|
1335
|
+
* fullscreen glyph the product ships.
|
|
1336
|
+
* @param props - the drawn size in px.
|
|
1337
|
+
* @returns the glyph.
|
|
1338
|
+
*/
|
|
1339
|
+
function CoverFrameIcon({ size }) {
|
|
1340
|
+
return (0, react_jsx_runtime.jsx)("svg", {
|
|
1341
|
+
width: size,
|
|
1342
|
+
height: size,
|
|
1343
|
+
viewBox: "0 0 16 16",
|
|
1344
|
+
fill: "none",
|
|
1345
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
1346
|
+
"aria-hidden": true,
|
|
1347
|
+
children: [
|
|
1348
|
+
`M2.2 6.4V3.2h3.2`,
|
|
1349
|
+
`M9.600000000000001 3.2h3.2v3.2`,
|
|
1350
|
+
`M12.8 9.600000000000001h-3.2v-3.2`,
|
|
1351
|
+
`M6.4 12.8H3.2v-3.2`
|
|
1352
|
+
].map((d) => (0, react_jsx_runtime.jsx)("path", {
|
|
1353
|
+
d,
|
|
1354
|
+
stroke: "currentColor",
|
|
1355
|
+
strokeWidth: "1.4",
|
|
1356
|
+
strokeLinecap: "round",
|
|
1357
|
+
strokeLinejoin: "round"
|
|
1358
|
+
}, d))
|
|
1359
|
+
});
|
|
1360
|
+
}
|
|
1361
|
+
/** How long the chord's on-screen echo stays up, matching its CSS animation. */
|
|
1362
|
+
const COVER_NOTICE_MS = 1250;
|
|
1363
|
+
/**
|
|
1364
|
+
* The chord's echo: the same row of glyphs the popover shows, centred on screen,
|
|
1365
|
+
* shown the moment a coverage chord flips a switch and fading out after a second.
|
|
1366
|
+
* It is a report, not a control — nothing in it is pressable — so a keyboard flip
|
|
1367
|
+
* is as visible as a click, without a pointer target appearing under the mouse.
|
|
1368
|
+
* @param props - copy, the coverage after the flip, and which edge just changed.
|
|
1369
|
+
* @returns the notice.
|
|
1370
|
+
*/
|
|
1371
|
+
function CoverageNotice({ t, cover, changed }) {
|
|
1372
|
+
const label = COVER_ROWS.find((row) => row.key === changed);
|
|
1373
|
+
return (0, react_dom.createPortal)((0, react_jsx_runtime.jsxs)("div", {
|
|
1374
|
+
className: PendingPanel_module_css_default.coverNotice,
|
|
1375
|
+
"data-diff-approval-cover-notice": true,
|
|
1376
|
+
role: "status",
|
|
1377
|
+
"aria-live": "polite",
|
|
1378
|
+
children: [(0, react_jsx_runtime.jsx)("div", {
|
|
1379
|
+
className: `${PendingPanel_module_css_default.coverCard} ${PendingPanel_module_css_default.coverNoticeCard}`,
|
|
1380
|
+
"aria-hidden": true,
|
|
1381
|
+
children: COVER_ROWS.map(({ key }) => (0, react_jsx_runtime.jsx)("span", {
|
|
1382
|
+
className: PendingPanel_module_css_default.coverNoticeGlyph,
|
|
1383
|
+
"data-on": cover[key] ? "" : void 0,
|
|
1384
|
+
"data-changed": key === changed ? "" : void 0,
|
|
1385
|
+
"data-diff-approval-cover-notice-glyph": key,
|
|
1386
|
+
children: (0, react_jsx_runtime.jsx)(CoverEdgeIcon, {
|
|
1387
|
+
side: key,
|
|
1388
|
+
on: cover[key],
|
|
1389
|
+
size: 28
|
|
1390
|
+
})
|
|
1391
|
+
}, key))
|
|
1392
|
+
}), (0, react_jsx_runtime.jsx)("span", {
|
|
1393
|
+
className: PendingPanel_module_css_default.coverNoticeText,
|
|
1394
|
+
children: t("cover.notice", {
|
|
1395
|
+
label: t(label?.label ?? "cover.left"),
|
|
1396
|
+
state: t(cover[changed] ? "cover.on" : "cover.off")
|
|
1397
|
+
})
|
|
1398
|
+
})]
|
|
1399
|
+
}), document.body);
|
|
1400
|
+
}
|
|
1401
|
+
/**
|
|
1402
|
+
* The trigger and its popover. Marked with `data-diff-approval-cover`, and each
|
|
1403
|
+
* switch with `data-diff-approval-cover-<key>`, so the two halves are addressable
|
|
1404
|
+
* apart.
|
|
1405
|
+
* @param props - copy, the current coverage, and what a switch does.
|
|
1406
|
+
* @returns the header control.
|
|
1407
|
+
*/
|
|
1408
|
+
function CoverageControl({ t, cover, onToggle }) {
|
|
1409
|
+
const [open, setOpen] = (0, react.useState)(false);
|
|
1410
|
+
const hostRef = (0, react.useRef)(null);
|
|
1411
|
+
(0, react.useEffect)(() => {
|
|
1412
|
+
if (!open) return;
|
|
1413
|
+
const onPointerDown = (event) => {
|
|
1414
|
+
if (event.target instanceof Node && hostRef.current?.contains(event.target) === true) return;
|
|
1415
|
+
setOpen(false);
|
|
1416
|
+
};
|
|
1417
|
+
const onKeyDown = (event) => {
|
|
1418
|
+
if (event.key === "Escape") setOpen(false);
|
|
1419
|
+
};
|
|
1420
|
+
document.addEventListener("pointerdown", onPointerDown, true);
|
|
1421
|
+
document.addEventListener("keydown", onKeyDown);
|
|
1422
|
+
return () => {
|
|
1423
|
+
document.removeEventListener("pointerdown", onPointerDown, true);
|
|
1424
|
+
document.removeEventListener("keydown", onKeyDown);
|
|
1425
|
+
};
|
|
1426
|
+
}, [open]);
|
|
1427
|
+
return (0, react_jsx_runtime.jsxs)("span", {
|
|
1428
|
+
className: PendingPanel_module_css_default.coverHost,
|
|
1429
|
+
ref: hostRef,
|
|
1430
|
+
children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
1431
|
+
label: t("action.cover"),
|
|
1432
|
+
side: "bottom",
|
|
1433
|
+
delayMs: 500,
|
|
1434
|
+
children: (0, react_jsx_runtime.jsx)("button", {
|
|
1435
|
+
type: "button",
|
|
1436
|
+
className: PendingPanel_module_css_default.expand,
|
|
1437
|
+
"data-diff-approval-cover": true,
|
|
1438
|
+
"aria-label": t("action.cover"),
|
|
1439
|
+
"aria-haspopup": "true",
|
|
1440
|
+
"aria-expanded": open,
|
|
1441
|
+
onClick: () => {
|
|
1442
|
+
setOpen((value) => !value);
|
|
1443
|
+
},
|
|
1444
|
+
children: (0, react_jsx_runtime.jsx)(CoverFrameIcon, { size: 14 })
|
|
1445
|
+
})
|
|
1446
|
+
}), open && (0, react_jsx_runtime.jsx)("div", {
|
|
1447
|
+
className: `${PendingPanel_module_css_default.coverCard} ${PendingPanel_module_css_default.coverPopover}`,
|
|
1448
|
+
"data-diff-approval-cover-popover": true,
|
|
1449
|
+
role: "group",
|
|
1450
|
+
"aria-label": t("action.cover"),
|
|
1451
|
+
children: COVER_ROWS.map(({ key, action, label }) => (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
1452
|
+
label: withChord(t(label), action),
|
|
1453
|
+
side: "bottom",
|
|
1454
|
+
delayMs: 0,
|
|
1455
|
+
children: (0, react_jsx_runtime.jsx)("button", {
|
|
1456
|
+
type: "button",
|
|
1457
|
+
className: PendingPanel_module_css_default.coverButton,
|
|
1458
|
+
"data-diff-approval-cover-switch": key,
|
|
1459
|
+
"data-on": cover[key] ? "" : void 0,
|
|
1460
|
+
"aria-label": withChord(t(label), action),
|
|
1461
|
+
"aria-pressed": cover[key],
|
|
1462
|
+
onClick: () => {
|
|
1463
|
+
onToggle(key);
|
|
1464
|
+
},
|
|
1465
|
+
children: (0, react_jsx_runtime.jsx)(CoverEdgeIcon, {
|
|
1466
|
+
side: key,
|
|
1467
|
+
on: cover[key],
|
|
1468
|
+
size: 20
|
|
1469
|
+
})
|
|
1470
|
+
})
|
|
1471
|
+
}, key))
|
|
1472
|
+
})]
|
|
1473
|
+
});
|
|
1474
|
+
}
|
|
1475
|
+
//#endregion
|
|
1476
|
+
//#region node_modules/.pnpm/diff@9.0.0/node_modules/diff/libesm/diff/base.js
|
|
1477
|
+
var Diff = class {
|
|
1478
|
+
diff(oldStr, newStr, options = {}) {
|
|
1479
|
+
let callback;
|
|
1480
|
+
if (typeof options === "function") {
|
|
1481
|
+
callback = options;
|
|
1482
|
+
options = {};
|
|
1483
|
+
} else if ("callback" in options) callback = options.callback;
|
|
1484
|
+
const oldString = this.castInput(oldStr, options);
|
|
1485
|
+
const newString = this.castInput(newStr, options);
|
|
1486
|
+
const oldTokens = this.removeEmpty(this.tokenize(oldString, options));
|
|
1487
|
+
const newTokens = this.removeEmpty(this.tokenize(newString, options));
|
|
1488
|
+
return this.diffWithOptionsObj(oldTokens, newTokens, options, callback);
|
|
1489
|
+
}
|
|
1490
|
+
diffWithOptionsObj(oldTokens, newTokens, options, callback) {
|
|
1491
|
+
var _a;
|
|
1492
|
+
const done = (value) => {
|
|
1493
|
+
value = this.postProcess(value, options);
|
|
1494
|
+
if (callback) {
|
|
1495
|
+
setTimeout(function() {
|
|
1496
|
+
callback(value);
|
|
1497
|
+
}, 0);
|
|
1498
|
+
return;
|
|
1499
|
+
} else return value;
|
|
1500
|
+
};
|
|
1501
|
+
const newLen = newTokens.length, oldLen = oldTokens.length;
|
|
1502
|
+
let editLength = 1;
|
|
1503
|
+
let maxEditLength = newLen + oldLen;
|
|
1504
|
+
if (options.maxEditLength != null) maxEditLength = Math.min(maxEditLength, options.maxEditLength);
|
|
1505
|
+
const maxExecutionTime = (_a = options.timeout) !== null && _a !== void 0 ? _a : Infinity;
|
|
1506
|
+
const abortAfterTimestamp = Date.now() + maxExecutionTime;
|
|
1507
|
+
const bestPath = [{
|
|
1508
|
+
oldPos: -1,
|
|
1509
|
+
lastComponent: void 0
|
|
1510
|
+
}];
|
|
1511
|
+
let newPos = this.extractCommon(bestPath[0], newTokens, oldTokens, 0, options);
|
|
1512
|
+
if (bestPath[0].oldPos + 1 >= oldLen && newPos + 1 >= newLen) return done(this.buildValues(bestPath[0].lastComponent, newTokens, oldTokens));
|
|
1513
|
+
let minDiagonalToConsider = -Infinity, maxDiagonalToConsider = Infinity;
|
|
1514
|
+
const execEditLength = () => {
|
|
1515
|
+
for (let diagonalPath = Math.max(minDiagonalToConsider, -editLength); diagonalPath <= Math.min(maxDiagonalToConsider, editLength); diagonalPath += 2) {
|
|
1516
|
+
let basePath;
|
|
1517
|
+
const removePath = bestPath[diagonalPath - 1], addPath = bestPath[diagonalPath + 1];
|
|
1518
|
+
if (removePath) bestPath[diagonalPath - 1] = void 0;
|
|
1519
|
+
let canAdd = false;
|
|
1520
|
+
if (addPath) {
|
|
1521
|
+
const addPathNewPos = addPath.oldPos - diagonalPath;
|
|
1522
|
+
canAdd = addPath && 0 <= addPathNewPos && addPathNewPos < newLen;
|
|
1523
|
+
}
|
|
1524
|
+
const canRemove = removePath && removePath.oldPos + 1 < oldLen;
|
|
1525
|
+
if (!canAdd && !canRemove) {
|
|
1526
|
+
bestPath[diagonalPath] = void 0;
|
|
1527
|
+
continue;
|
|
1528
|
+
}
|
|
1529
|
+
if (!canRemove || canAdd && removePath.oldPos < addPath.oldPos) basePath = this.addToPath(addPath, true, false, 0, options);
|
|
1530
|
+
else basePath = this.addToPath(removePath, false, true, 1, options);
|
|
1531
|
+
newPos = this.extractCommon(basePath, newTokens, oldTokens, diagonalPath, options);
|
|
1532
|
+
if (basePath.oldPos + 1 >= oldLen && newPos + 1 >= newLen) return done(this.buildValues(basePath.lastComponent, newTokens, oldTokens)) || true;
|
|
1533
|
+
else {
|
|
1534
|
+
bestPath[diagonalPath] = basePath;
|
|
1535
|
+
if (basePath.oldPos + 1 >= oldLen) maxDiagonalToConsider = Math.min(maxDiagonalToConsider, diagonalPath - 1);
|
|
1536
|
+
if (newPos + 1 >= newLen) minDiagonalToConsider = Math.max(minDiagonalToConsider, diagonalPath + 1);
|
|
1537
|
+
}
|
|
1538
|
+
}
|
|
1539
|
+
editLength++;
|
|
1540
|
+
};
|
|
1541
|
+
if (callback) (function exec() {
|
|
1542
|
+
setTimeout(function() {
|
|
1543
|
+
if (editLength > maxEditLength || Date.now() > abortAfterTimestamp) return callback(void 0);
|
|
1544
|
+
if (!execEditLength()) exec();
|
|
1545
|
+
}, 0);
|
|
1546
|
+
})();
|
|
1547
|
+
else while (editLength <= maxEditLength && Date.now() <= abortAfterTimestamp) {
|
|
1548
|
+
const ret = execEditLength();
|
|
1549
|
+
if (ret) return ret;
|
|
1550
|
+
}
|
|
1551
|
+
}
|
|
1552
|
+
addToPath(path, added, removed, oldPosInc, options) {
|
|
1553
|
+
const last = path.lastComponent;
|
|
1554
|
+
if (last && !options.oneChangePerToken && last.added === added && last.removed === removed) return {
|
|
1555
|
+
oldPos: path.oldPos + oldPosInc,
|
|
1556
|
+
lastComponent: {
|
|
1557
|
+
count: last.count + 1,
|
|
1558
|
+
added,
|
|
1559
|
+
removed,
|
|
1560
|
+
previousComponent: last.previousComponent
|
|
1561
|
+
}
|
|
1562
|
+
};
|
|
1563
|
+
else return {
|
|
1564
|
+
oldPos: path.oldPos + oldPosInc,
|
|
1565
|
+
lastComponent: {
|
|
1566
|
+
count: 1,
|
|
1567
|
+
added,
|
|
1568
|
+
removed,
|
|
1569
|
+
previousComponent: last
|
|
1570
|
+
}
|
|
1571
|
+
};
|
|
1572
|
+
}
|
|
1573
|
+
extractCommon(basePath, newTokens, oldTokens, diagonalPath, options) {
|
|
1574
|
+
const newLen = newTokens.length, oldLen = oldTokens.length;
|
|
1575
|
+
let oldPos = basePath.oldPos, newPos = oldPos - diagonalPath, commonCount = 0;
|
|
1576
|
+
while (newPos + 1 < newLen && oldPos + 1 < oldLen && this.equals(oldTokens[oldPos + 1], newTokens[newPos + 1], options)) {
|
|
1577
|
+
newPos++;
|
|
1578
|
+
oldPos++;
|
|
1579
|
+
commonCount++;
|
|
1580
|
+
if (options.oneChangePerToken) basePath.lastComponent = {
|
|
1581
|
+
count: 1,
|
|
1582
|
+
previousComponent: basePath.lastComponent,
|
|
1583
|
+
added: false,
|
|
1584
|
+
removed: false
|
|
1585
|
+
};
|
|
1586
|
+
}
|
|
1587
|
+
if (commonCount && !options.oneChangePerToken) basePath.lastComponent = {
|
|
1588
|
+
count: commonCount,
|
|
1589
|
+
previousComponent: basePath.lastComponent,
|
|
1590
|
+
added: false,
|
|
1591
|
+
removed: false
|
|
1592
|
+
};
|
|
1593
|
+
basePath.oldPos = oldPos;
|
|
1594
|
+
return newPos;
|
|
1595
|
+
}
|
|
1596
|
+
equals(left, right, options) {
|
|
1597
|
+
if (options.comparator) return options.comparator(left, right);
|
|
1598
|
+
else return left === right || !!options.ignoreCase && left.toLowerCase() === right.toLowerCase();
|
|
1599
|
+
}
|
|
1600
|
+
removeEmpty(array) {
|
|
1601
|
+
const ret = [];
|
|
1602
|
+
for (let i = 0; i < array.length; i++) if (array[i]) ret.push(array[i]);
|
|
1603
|
+
return ret;
|
|
1604
|
+
}
|
|
1605
|
+
castInput(value, options) {
|
|
1606
|
+
return value;
|
|
1607
|
+
}
|
|
1608
|
+
tokenize(value, options) {
|
|
1609
|
+
return Array.from(value);
|
|
1610
|
+
}
|
|
1611
|
+
join(chars) {
|
|
1612
|
+
return chars.join("");
|
|
1613
|
+
}
|
|
1614
|
+
postProcess(changeObjects, options) {
|
|
1615
|
+
return changeObjects;
|
|
1616
|
+
}
|
|
1617
|
+
get useLongestToken() {
|
|
1618
|
+
return false;
|
|
1619
|
+
}
|
|
1620
|
+
buildValues(lastComponent, newTokens, oldTokens) {
|
|
1621
|
+
const components = [];
|
|
1622
|
+
let nextComponent;
|
|
1623
|
+
while (lastComponent) {
|
|
1624
|
+
components.push(lastComponent);
|
|
1625
|
+
nextComponent = lastComponent.previousComponent;
|
|
1626
|
+
delete lastComponent.previousComponent;
|
|
1627
|
+
lastComponent = nextComponent;
|
|
1628
|
+
}
|
|
1629
|
+
components.reverse();
|
|
1630
|
+
const componentLen = components.length;
|
|
1631
|
+
let componentPos = 0, newPos = 0, oldPos = 0;
|
|
1632
|
+
for (; componentPos < componentLen; componentPos++) {
|
|
1633
|
+
const component = components[componentPos];
|
|
1634
|
+
if (!component.removed) {
|
|
1635
|
+
if (!component.added && this.useLongestToken) {
|
|
1636
|
+
let value = newTokens.slice(newPos, newPos + component.count);
|
|
1637
|
+
value = value.map(function(value, i) {
|
|
1638
|
+
const oldValue = oldTokens[oldPos + i];
|
|
1639
|
+
return oldValue.length > value.length ? oldValue : value;
|
|
1640
|
+
});
|
|
1641
|
+
component.value = this.join(value);
|
|
1642
|
+
} else component.value = this.join(newTokens.slice(newPos, newPos + component.count));
|
|
1643
|
+
newPos += component.count;
|
|
1644
|
+
if (!component.added) oldPos += component.count;
|
|
1645
|
+
} else {
|
|
1646
|
+
component.value = this.join(oldTokens.slice(oldPos, oldPos + component.count));
|
|
1647
|
+
oldPos += component.count;
|
|
1648
|
+
}
|
|
1649
|
+
}
|
|
1650
|
+
return components;
|
|
1651
|
+
}
|
|
1652
|
+
};
|
|
1653
|
+
//#endregion
|
|
1654
|
+
//#region node_modules/.pnpm/diff@9.0.0/node_modules/diff/libesm/diff/character.js
|
|
1655
|
+
var CharacterDiff = class extends Diff {};
|
|
1656
|
+
new CharacterDiff();
|
|
1657
|
+
//#endregion
|
|
1658
|
+
//#region node_modules/.pnpm/diff@9.0.0/node_modules/diff/libesm/util/string.js
|
|
1659
|
+
function longestCommonPrefix(str1, str2) {
|
|
1660
|
+
let i;
|
|
1661
|
+
for (i = 0; i < str1.length && i < str2.length; i++) if (str1[i] != str2[i]) return str1.slice(0, i);
|
|
1662
|
+
return str1.slice(0, i);
|
|
1663
|
+
}
|
|
1664
|
+
function longestCommonSuffix(str1, str2) {
|
|
1665
|
+
let i;
|
|
1666
|
+
if (!str1 || !str2 || str1[str1.length - 1] != str2[str2.length - 1]) return "";
|
|
1667
|
+
for (i = 0; i < str1.length && i < str2.length; i++) if (str1[str1.length - (i + 1)] != str2[str2.length - (i + 1)]) return str1.slice(-i);
|
|
1668
|
+
return str1.slice(-i);
|
|
1669
|
+
}
|
|
1670
|
+
function replacePrefix(string, oldPrefix, newPrefix) {
|
|
751
1671
|
if (string.slice(0, oldPrefix.length) != oldPrefix) throw Error(`string ${JSON.stringify(string)} doesn't start with prefix ${JSON.stringify(oldPrefix)}; this is a bug`);
|
|
752
1672
|
return newPrefix + string.slice(oldPrefix.length);
|
|
753
1673
|
}
|
|
@@ -17618,342 +18538,460 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
17618
18538
|
});
|
|
17619
18539
|
const onResize = () => inject();
|
|
17620
18540
|
window.addEventListener("resize", onResize);
|
|
17621
|
-
return () => {
|
|
17622
|
-
observer.disconnect();
|
|
17623
|
-
window.removeEventListener("resize", onResize);
|
|
17624
|
-
document.querySelectorAll(`[${BUTTON_ATTR}]`).forEach((el) => el.remove());
|
|
17625
|
-
if (ownedStyleEl !== null) ownedStyleEl.remove();
|
|
17626
|
-
};
|
|
17627
|
-
}
|
|
17628
|
-
//#endregion
|
|
17629
|
-
//#region lib/types/client/settings.js
|
|
17630
|
-
/** Client preferences for the review panel, persisted in localStorage. */
|
|
17631
|
-
const PASTE_ON_COPY_KEY = "diff-approval:paste-on-copy";
|
|
17632
|
-
const IMPORT_UNTRACKED_KEY = "diff-approval:import-untracked";
|
|
17633
|
-
const CONFIRM_FILE_REMOVE_KEY = "diff-approval:confirm-file-remove";
|
|
17634
|
-
const SEARCH_CASE_KEY = "diff-approval:search-case";
|
|
17635
|
-
const SEARCH_WORD_KEY = "diff-approval:search-word";
|
|
17636
|
-
const TAB_WIDTH_KEY = "diff-approval:tab-size";
|
|
17637
|
-
const SPLIT_MODE_KEY = "diff-approval:split-mode";
|
|
17638
|
-
const MD_PREVIEW_KEY = "diff-approval:md-preview";
|
|
17639
|
-
const MD_MAX_WIDTH_KEY = "diff-approval:md-max-width";
|
|
17640
|
-
const NAV_LEAD_KEY = "diff-approval:nav-lead-rows";
|
|
17641
|
-
const DIFF_FONT_SCALE_KEY = "diff-approval:diff-font-scale";
|
|
17642
|
-
const DIFF_LINE_HEIGHT_KEY = "diff-approval:diff-line-height";
|
|
17643
|
-
const DIFF_ADD_COLOR_KEY = "diff-approval:diff-add-color";
|
|
17644
|
-
const DIFF_DEL_COLOR_KEY = "diff-approval:diff-del-color";
|
|
17645
|
-
const LANG_BY_SUFFIX_KEY = "diff-approval:lang-by-suffix";
|
|
17646
|
-
const WRAP_PREFIX = "diff-approval:wrap:";
|
|
17647
|
-
const MD_MAX_WIDTH_MAX = 1600;
|
|
17648
|
-
/**
|
|
17649
|
-
* Whether copying a reference should also paste it into the chat input and
|
|
17650
|
-
* focus it. Defaults to on; only an explicit `'0'` disables it.
|
|
17651
|
-
* @returns whether auto-paste is enabled.
|
|
17652
|
-
*/
|
|
17653
|
-
function pasteOnCopyEnabled() {
|
|
17654
|
-
return localStorage.getItem(PASTE_ON_COPY_KEY) !== "0";
|
|
17655
|
-
}
|
|
17656
|
-
/** Persist the auto-paste preference. */
|
|
17657
|
-
function setPasteOnCopyEnabled(value) {
|
|
17658
|
-
localStorage.setItem(PASTE_ON_COPY_KEY, value ? "1" : "0");
|
|
17659
|
-
}
|
|
17660
|
-
/**
|
|
17661
|
-
* Whether importing workspace VCS changes includes new/untracked files (git
|
|
17662
|
-
* `??`, svn `?`, p4 unversioned). Defaults to off: collecting them scans the
|
|
17663
|
-
* whole workspace, which can be slow on large trees, so the preference is
|
|
17664
|
-
* opt-in. Only an explicit `'1'` enables it.
|
|
17665
|
-
* @returns whether untracked files are imported.
|
|
17666
|
-
*/
|
|
17667
|
-
function includeUntrackedEnabled() {
|
|
17668
|
-
return localStorage.getItem(IMPORT_UNTRACKED_KEY) === "1";
|
|
17669
|
-
}
|
|
17670
|
-
/** Persist the import-untracked preference. */
|
|
17671
|
-
function setIncludeUntrackedEnabled(value) {
|
|
17672
|
-
localStorage.setItem(IMPORT_UNTRACKED_KEY, value ? "1" : "0");
|
|
17673
|
-
}
|
|
17674
|
-
/**
|
|
17675
|
-
* Whether a whole-file keep/revert asks before dropping the resolved file from
|
|
17676
|
-
* the list. Defaults to on; only an explicit `'0'` disables it, which then
|
|
17677
|
-
* removes the file straight away.
|
|
17678
|
-
* @returns whether the remove prompt is enabled.
|
|
17679
|
-
*/
|
|
17680
|
-
function confirmFileRemoveEnabled() {
|
|
17681
|
-
return localStorage.getItem(CONFIRM_FILE_REMOVE_KEY) !== "0";
|
|
17682
|
-
}
|
|
17683
|
-
/** Persist the whole-file remove-prompt preference. */
|
|
17684
|
-
function setConfirmFileRemoveEnabled(value) {
|
|
17685
|
-
localStorage.setItem(CONFIRM_FILE_REMOVE_KEY, value ? "1" : "0");
|
|
17686
|
-
}
|
|
17687
|
-
/**
|
|
17688
|
-
* Whether the in-file search matches letter case exactly. Defaults to off, so a
|
|
17689
|
-
* plain query keeps matching either case; only an explicit `'1'` turns it on.
|
|
17690
|
-
* @returns whether the search is case-sensitive.
|
|
17691
|
-
*/
|
|
17692
|
-
function searchCaseSensitive() {
|
|
17693
|
-
return localStorage.getItem(SEARCH_CASE_KEY) === "1";
|
|
17694
|
-
}
|
|
17695
|
-
/** Persist the search case-sensitivity preference. */
|
|
17696
|
-
function setSearchCaseSensitive(value) {
|
|
17697
|
-
localStorage.setItem(SEARCH_CASE_KEY, value ? "1" : "0");
|
|
17698
|
-
}
|
|
17699
|
-
/**
|
|
17700
|
-
* Whether the in-file search only matches whole words. Defaults to off; only an
|
|
17701
|
-
* explicit `'1'` enables it.
|
|
17702
|
-
* @returns whether the search matches whole words only.
|
|
17703
|
-
*/
|
|
17704
|
-
function searchWholeWord() {
|
|
17705
|
-
return localStorage.getItem(SEARCH_WORD_KEY) === "1";
|
|
17706
|
-
}
|
|
17707
|
-
/** Persist the search whole-word preference. */
|
|
17708
|
-
function setSearchWholeWord(value) {
|
|
17709
|
-
localStorage.setItem(SEARCH_WORD_KEY, value ? "1" : "0");
|
|
17710
|
-
}
|
|
17711
|
-
/**
|
|
17712
|
-
* Whether lines wrap (auto-wrap) in the diff for one highlight language.
|
|
17713
|
-
* Defaults to off; only an explicit `'1'` enables it. Stored per language, so
|
|
17714
|
-
* a language's preference never leaks into another's.
|
|
17715
|
-
* @param lang - the highlight language (or `''` for the auto/default bucket).
|
|
17716
|
-
* @returns whether lines wrap.
|
|
17717
|
-
*/
|
|
17718
|
-
function wrapEnabled(lang) {
|
|
17719
|
-
return localStorage.getItem(`${WRAP_PREFIX}${lang}`) === "1";
|
|
17720
|
-
}
|
|
17721
|
-
/** Persist the per-language auto-wrap preference. */
|
|
17722
|
-
function setWrapEnabled(lang, value) {
|
|
17723
|
-
localStorage.setItem(`${WRAP_PREFIX}${lang}`, value ? "1" : "0");
|
|
17724
|
-
}
|
|
17725
|
-
/**
|
|
17726
|
-
* The diff's tab width in spaces. Defaults to 4; the settings UI offers 2/4/8,
|
|
17727
|
-
* but any positive integer is accepted. This drives both the rendered
|
|
17728
|
-
* `tab-size` and the wrapped-line tab measurement, so they always agree.
|
|
17729
|
-
* @returns the number of spaces one tab advances.
|
|
17730
|
-
*/
|
|
17731
|
-
function tabWidth() {
|
|
17732
|
-
const value = Number.parseInt(localStorage.getItem(TAB_WIDTH_KEY) ?? "", 10);
|
|
17733
|
-
return Number.isInteger(value) && value > 0 ? value : 4;
|
|
17734
|
-
}
|
|
17735
|
-
/** Persist the diff's tab width (in spaces). */
|
|
17736
|
-
function setTabWidth(value) {
|
|
17737
|
-
localStorage.setItem(TAB_WIDTH_KEY, String(value));
|
|
18541
|
+
return () => {
|
|
18542
|
+
observer.disconnect();
|
|
18543
|
+
window.removeEventListener("resize", onResize);
|
|
18544
|
+
document.querySelectorAll(`[${BUTTON_ATTR}]`).forEach((el) => el.remove());
|
|
18545
|
+
if (ownedStyleEl !== null) ownedStyleEl.remove();
|
|
18546
|
+
};
|
|
17738
18547
|
}
|
|
18548
|
+
//#endregion
|
|
18549
|
+
//#region lib/types/client/boundary.js
|
|
17739
18550
|
/**
|
|
17740
|
-
*
|
|
17741
|
-
*
|
|
17742
|
-
*
|
|
18551
|
+
* A crash inside the panel must not take the review away for the rest of the page.
|
|
18552
|
+
*
|
|
18553
|
+
* The slot renderer wraps every seat in its own boundary, but a crash there
|
|
18554
|
+
* *abdicates* the entry: the footer row, the header button and the docked tab
|
|
18555
|
+
* disappear for the life of the page, with nothing on screen to say why and no way
|
|
18556
|
+
* back but a reload. This boundary keeps a failure inside our own tree instead —
|
|
18557
|
+
* the entry stays registered, it says what happened, and one press retries the
|
|
18558
|
+
* render — so a broken panel can never present as "the panel is gone" or as a
|
|
18559
|
+
* blank surface with no explanation.
|
|
18560
|
+
*
|
|
18561
|
+
* @module dsh-diff-approval/client/boundary
|
|
17743
18562
|
*/
|
|
17744
|
-
function diffFontScale() {
|
|
17745
|
-
const raw = Number.parseInt(localStorage.getItem(DIFF_FONT_SCALE_KEY) ?? "", 10);
|
|
17746
|
-
if (!Number.isFinite(raw)) return 100;
|
|
17747
|
-
return Math.max(50, Math.min(200, raw));
|
|
17748
|
-
}
|
|
17749
|
-
/** Persist the diff's code font-size scale (a percentage). */
|
|
17750
|
-
function setDiffFontScale(value) {
|
|
17751
|
-
localStorage.setItem(DIFF_FONT_SCALE_KEY, String(Math.max(50, Math.min(200, value))));
|
|
17752
|
-
}
|
|
17753
18563
|
/**
|
|
17754
|
-
*
|
|
17755
|
-
*
|
|
17756
|
-
* @returns the line height in px.
|
|
18564
|
+
* Keep one panel mount's crash inside its own slot entry, with a visible note and
|
|
18565
|
+
* a retry that re-renders it.
|
|
17757
18566
|
*/
|
|
17758
|
-
|
|
17759
|
-
|
|
17760
|
-
|
|
17761
|
-
|
|
17762
|
-
}
|
|
17763
|
-
/** Persist the diff's code line height (px). */
|
|
17764
|
-
function setDiffLineHeight(value) {
|
|
17765
|
-
localStorage.setItem(DIFF_LINE_HEIGHT_KEY, String(Math.max(10, Math.min(36, value))));
|
|
17766
|
-
}
|
|
17767
|
-
/** Read a theme CSS custom property, with a fallback when it is unavailable. */
|
|
17768
|
-
function themeColor(name, fallback) {
|
|
17769
|
-
if (typeof document !== "undefined") {
|
|
17770
|
-
const v = getComputedStyle(document.documentElement).getPropertyValue(name).trim();
|
|
17771
|
-
if (/^#[0-9a-f]{6}$/i.test(v)) return v;
|
|
18567
|
+
var PanelBoundary = class extends react.Component {
|
|
18568
|
+
state = { failed: false };
|
|
18569
|
+
static getDerivedStateFromError() {
|
|
18570
|
+
return { failed: true };
|
|
17772
18571
|
}
|
|
17773
|
-
|
|
17774
|
-
|
|
17775
|
-
|
|
17776
|
-
|
|
17777
|
-
|
|
17778
|
-
|
|
17779
|
-
|
|
17780
|
-
|
|
17781
|
-
|
|
17782
|
-
|
|
17783
|
-
|
|
17784
|
-
|
|
17785
|
-
|
|
17786
|
-
|
|
17787
|
-
|
|
17788
|
-
|
|
17789
|
-
|
|
17790
|
-
|
|
17791
|
-
|
|
17792
|
-
|
|
17793
|
-
|
|
17794
|
-
|
|
17795
|
-
|
|
17796
|
-
|
|
17797
|
-
|
|
17798
|
-
|
|
17799
|
-
|
|
17800
|
-
}
|
|
18572
|
+
componentDidCatch(error, info) {
|
|
18573
|
+
console.error("diff-approval panel crashed:", error, info.componentStack);
|
|
18574
|
+
}
|
|
18575
|
+
render() {
|
|
18576
|
+
if (!this.state.failed) return this.props.children;
|
|
18577
|
+
const { t } = this.props;
|
|
18578
|
+
return (0, react_jsx_runtime.jsxs)("div", {
|
|
18579
|
+
className: PendingPanel_module_css_default.panelFailure,
|
|
18580
|
+
role: "alert",
|
|
18581
|
+
"data-diff-approval-failed": true,
|
|
18582
|
+
children: [(0, react_jsx_runtime.jsx)("span", {
|
|
18583
|
+
className: PendingPanel_module_css_default.panelFailureText,
|
|
18584
|
+
children: t("panel.crashed")
|
|
18585
|
+
}), (0, react_jsx_runtime.jsx)("button", {
|
|
18586
|
+
type: "button",
|
|
18587
|
+
className: PendingPanel_module_css_default.panelFailureRetry,
|
|
18588
|
+
"data-diff-approval-failed-retry": true,
|
|
18589
|
+
onClick: () => {
|
|
18590
|
+
this.setState({ failed: false });
|
|
18591
|
+
},
|
|
18592
|
+
children: t("action.retry")
|
|
18593
|
+
})]
|
|
18594
|
+
});
|
|
18595
|
+
}
|
|
18596
|
+
};
|
|
18597
|
+
//#endregion
|
|
18598
|
+
//#region lib/types/client/dock.js
|
|
17801
18599
|
/**
|
|
17802
|
-
* The
|
|
17803
|
-
*
|
|
17804
|
-
*
|
|
17805
|
-
*
|
|
17806
|
-
*
|
|
17807
|
-
*
|
|
18600
|
+
* The app's right sidebar (the docking surface) as a host for the review panel.
|
|
18601
|
+
*
|
|
18602
|
+
* The sidebar is an extensible tab system: a tab type registers its static face
|
|
18603
|
+
* with the `sidebarRightTabs` service, its body in the keyed `sidebar.right.pane.tab`
|
|
18604
|
+
* seat under the same `id`, and is opened by `kind` through `sidebarRight`. The
|
|
18605
|
+
* harness documents this as the path a *package outside the product* takes (its
|
|
18606
|
+
* own document preview ships that way), so nothing here imports the sidebar's
|
|
18607
|
+
* package as a runtime value: the shapes below are the structural minimum, which
|
|
18608
|
+
* is exactly what lets this plugin keep loading on releases that have no right
|
|
18609
|
+
* sidebar at all — the type is simply never registered there.
|
|
18610
|
+
*
|
|
18611
|
+
* @module dsh-diff-approval/client/dock
|
|
17808
18612
|
*/
|
|
17809
|
-
|
|
17810
|
-
|
|
17811
|
-
|
|
18613
|
+
/** The tab type's `kind`: what `openTab` names. */
|
|
18614
|
+
const DIFF_DOCK_KIND = "diff-approval";
|
|
18615
|
+
/** The registration's `id`: the key its body and title register under. The
|
|
18616
|
+
* registry wants a name unique across implementations, and the package name is
|
|
18617
|
+
* the natural value. */
|
|
18618
|
+
const DIFF_DOCK_ID = "dsh-diff-approval";
|
|
18619
|
+
/** Window event a docked panel dispatches to move the panel back to the overlay.
|
|
18620
|
+
* The docked tab and the footer entry are separate mounts, so the ask travels as
|
|
18621
|
+
* an event — the same bridge the produced-file buttons use. */
|
|
18622
|
+
const SHOW_PANEL_EVENT = "diff-approval:show-panel";
|
|
18623
|
+
/** Ask the panel to toggle: what the header entry's button does. It runs the same
|
|
18624
|
+
* action as the footer badge, so the two entries can never disagree about what a
|
|
18625
|
+
* press means — including for a docked panel, whose tab the chip still closes. */
|
|
18626
|
+
const TOGGLE_PANEL_EVENT = "diff-approval:toggle-panel";
|
|
18627
|
+
/** The floating panel's own visibility, published on every change so a second
|
|
18628
|
+
* entry can light up while the panel is open (`detail.open`). */
|
|
18629
|
+
const PANEL_STATE_EVENT = "diff-approval:panel-state";
|
|
18630
|
+
/** Name a file for the panel to show, from a mount that is not the panel itself
|
|
18631
|
+
* (the produced-file chip): every mounted panel instance switches to it and lands
|
|
18632
|
+
* on its first change — the ask is "show me this diff", not "put me back where I
|
|
18633
|
+
* was". The file is also recorded as the last one, so an instance that only
|
|
18634
|
+
* appears afterwards (the docked tab) opens the same file the same way. */
|
|
18635
|
+
const OPEN_PANEL_FILE_EVENT = "diff-approval:panel-file";
|
|
18636
|
+
/** How often the optional sidebar services are looked up, and for how long. The
|
|
18637
|
+
* sidebar's plugin may attach after this one; ~15s of patience covers a slow
|
|
18638
|
+
* boot, and finding them stops the timer. */
|
|
18639
|
+
const DOCK_LOOKUP_INTERVAL_MS = 150;
|
|
18640
|
+
const DOCK_LOOKUP_ATTEMPTS = 100;
|
|
18641
|
+
/** A service this plugin already depends on, probed through the same lookup the
|
|
18642
|
+
* sidebar is found with. If it reads "ok" while the sidebar's do not, the lookup
|
|
18643
|
+
* itself works and the sidebar's services are simply not reachable from this
|
|
18644
|
+
* plugin's context — a different bug from "not loaded yet". */
|
|
18645
|
+
const PROBE_SERVICES = ["slots"];
|
|
18646
|
+
/** How one lookup result reads in the diagnostic string. Never throws: a probe
|
|
18647
|
+
* that fails says so instead of taking the whole dock wiring down with it. */
|
|
18648
|
+
function shape(ctx, name) {
|
|
18649
|
+
try {
|
|
18650
|
+
const value = ctx.get(name);
|
|
18651
|
+
if (value === void 0) return "missing";
|
|
18652
|
+
return typeof value === "object" || typeof value === "function" ? "ok" : typeof value;
|
|
18653
|
+
} catch (error) {
|
|
18654
|
+
return `threw(${error instanceof Error ? error.message : String(error)})`;
|
|
18655
|
+
}
|
|
17812
18656
|
}
|
|
17813
18657
|
/**
|
|
17814
|
-
*
|
|
17815
|
-
*
|
|
17816
|
-
*
|
|
17817
|
-
*
|
|
18658
|
+
* What the lookup can see, for the "why is there no dock" message: the sidebar's
|
|
18659
|
+
* two services first (the decisive fact), then the services this plugin already
|
|
18660
|
+
* uses, then whether the tab seat exists at all — a seat without services means
|
|
18661
|
+
* the sidebar's plugin is loaded but its services are not reachable from here.
|
|
18662
|
+
* @param ctx - the client context (structurally, see {@link DockHostContext}).
|
|
18663
|
+
* @returns a one-line description, safe to show and to report.
|
|
17818
18664
|
*/
|
|
17819
|
-
function
|
|
17820
|
-
const
|
|
17821
|
-
|
|
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 {};
|
|
18665
|
+
function diagnose(ctx) {
|
|
18666
|
+
const probed = PROBE_SERVICES.map((name) => `${name}=${shape(ctx, name)}`).join(" ");
|
|
18667
|
+
let seat = "unknown";
|
|
17834
18668
|
try {
|
|
17835
|
-
|
|
17836
|
-
|
|
17837
|
-
|
|
17838
|
-
for (const [suffix, language] of Object.entries(parsed)) if (typeof language === "string" && language !== "") cleaned[suffix] = language;
|
|
17839
|
-
return cleaned;
|
|
17840
|
-
} catch {
|
|
17841
|
-
return {};
|
|
18669
|
+
seat = ctx.get("slots")?.spec?.("sidebar.right.pane.tab") === void 0 ? "missing" : "ok";
|
|
18670
|
+
} catch (error) {
|
|
18671
|
+
seat = `probe threw: ${error instanceof Error ? error.message : String(error)}`;
|
|
17842
18672
|
}
|
|
18673
|
+
return `sidebarRight=${shape(ctx, "sidebarRight")} sidebarRightTabs=${shape(ctx, "sidebarRightTabs")}, ${probed}, seat(sidebar.right.pane.tab)=${seat}`;
|
|
17843
18674
|
}
|
|
18675
|
+
/** Where the guide page lists the panel, relative to the product's own entries. */
|
|
18676
|
+
const DIFF_DOCK_GUIDE_ORDER = 40;
|
|
17844
18677
|
/**
|
|
17845
|
-
*
|
|
17846
|
-
*
|
|
17847
|
-
*
|
|
17848
|
-
*
|
|
18678
|
+
* The dock's live state, created at apply time rather than when the sidebar's
|
|
18679
|
+
* services appear. The footer entry is seated once, so a face captured before the
|
|
18680
|
+
* sidebar attaches would freeze on "no dock" and the panel would never offer the
|
|
18681
|
+
* switch — the state below is therefore always present, and `attach` swaps the
|
|
18682
|
+
* live sidebar underneath it while `open` resolves the current one per call.
|
|
18683
|
+
* @returns the state; `attach` reports availability and the tab's own visibility.
|
|
17849
18684
|
*/
|
|
17850
|
-
function
|
|
17851
|
-
|
|
17852
|
-
|
|
17853
|
-
|
|
17854
|
-
|
|
17855
|
-
|
|
18685
|
+
function createDockState() {
|
|
18686
|
+
let sidebar;
|
|
18687
|
+
let reason;
|
|
18688
|
+
/** Reported by the docked tab body: see {@link DockFace.setShowing}. */
|
|
18689
|
+
let showing = false;
|
|
18690
|
+
/** Published by the docked tab's chip: see {@link DockFace.setClose}. */
|
|
18691
|
+
let closeTab;
|
|
18692
|
+
let snapshot = {
|
|
18693
|
+
available: false,
|
|
18694
|
+
open: false,
|
|
18695
|
+
reason: void 0
|
|
18696
|
+
};
|
|
18697
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
18698
|
+
const read = () => sidebar === void 0 ? {
|
|
18699
|
+
available: false,
|
|
18700
|
+
open: false,
|
|
18701
|
+
reason
|
|
18702
|
+
} : {
|
|
18703
|
+
available: true,
|
|
18704
|
+
open: showing,
|
|
18705
|
+
reason: void 0
|
|
18706
|
+
};
|
|
18707
|
+
const publish = () => {
|
|
18708
|
+
const next = read();
|
|
18709
|
+
if (next.available === snapshot.available && next.open === snapshot.open && next.reason === snapshot.reason) return;
|
|
18710
|
+
snapshot = next;
|
|
18711
|
+
for (const listener of [...listeners]) listener();
|
|
18712
|
+
};
|
|
18713
|
+
return {
|
|
18714
|
+
face: {
|
|
18715
|
+
hooks: { dock: {
|
|
18716
|
+
getSnapshot: () => snapshot,
|
|
18717
|
+
subscribe(listener) {
|
|
18718
|
+
listeners.add(listener);
|
|
18719
|
+
return () => {
|
|
18720
|
+
listeners.delete(listener);
|
|
18721
|
+
};
|
|
18722
|
+
}
|
|
18723
|
+
} },
|
|
18724
|
+
open: () => {
|
|
18725
|
+
sidebar?.openTab(DIFF_DOCK_KIND, { revealIfOpened: true });
|
|
18726
|
+
},
|
|
18727
|
+
setShowing: (next) => {
|
|
18728
|
+
if (showing === next) return;
|
|
18729
|
+
showing = next;
|
|
18730
|
+
publish();
|
|
18731
|
+
},
|
|
18732
|
+
setClose: (close) => {
|
|
18733
|
+
closeTab = close;
|
|
18734
|
+
},
|
|
18735
|
+
close: () => {
|
|
18736
|
+
closeTab?.();
|
|
18737
|
+
}
|
|
18738
|
+
},
|
|
18739
|
+
attach(next, why) {
|
|
18740
|
+
reason = why;
|
|
18741
|
+
sidebar = next;
|
|
18742
|
+
if (sidebar === void 0) showing = false;
|
|
18743
|
+
publish();
|
|
18744
|
+
return () => {
|
|
18745
|
+
sidebar = void 0;
|
|
18746
|
+
showing = false;
|
|
18747
|
+
publish();
|
|
18748
|
+
};
|
|
18749
|
+
}
|
|
18750
|
+
};
|
|
17856
18751
|
}
|
|
17857
18752
|
/**
|
|
17858
|
-
*
|
|
17859
|
-
*
|
|
17860
|
-
*
|
|
17861
|
-
* @
|
|
18753
|
+
* The tab body: the review panel, docked. The panel's content is one portaled
|
|
18754
|
+
* block, so instead of restructuring it this hands it a host element of the
|
|
18755
|
+
* sidebar's own making to portal into — the tab body owns the element it fills.
|
|
18756
|
+
* @param props - the seat's runtime props, this plugin's face, and `useTabInfo`.
|
|
18757
|
+
* @returns the host element and, once it exists, the docked panel inside it.
|
|
17862
18758
|
*/
|
|
17863
|
-
|
|
17864
|
-
|
|
17865
|
-
|
|
17866
|
-
|
|
17867
|
-
|
|
17868
|
-
|
|
18759
|
+
/** Keeps a failure inside the tab from taking the app's tree down with it, and
|
|
18760
|
+
* from leaving the pane blank: {@link PanelBoundary} says what happened and can
|
|
18761
|
+
* re-render the panel without a reload. */
|
|
18762
|
+
function DiffDockBody(props) {
|
|
18763
|
+
const { t, useTabInfo, onDockShowing, ...panelProps } = props;
|
|
18764
|
+
const [host, setHost] = (0, react.useState)(null);
|
|
18765
|
+
const visible = (useTabInfo?.())?.tab.visible === true;
|
|
18766
|
+
(0, react.useEffect)(() => {
|
|
18767
|
+
onDockShowing?.(visible);
|
|
18768
|
+
return () => {
|
|
18769
|
+
onDockShowing?.(false);
|
|
18770
|
+
};
|
|
18771
|
+
}, [visible, onDockShowing]);
|
|
18772
|
+
return (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)("div", {
|
|
18773
|
+
ref: setHost,
|
|
18774
|
+
className: PendingPanel_module_css_default.dockHost,
|
|
18775
|
+
"data-diff-approval-dock": true
|
|
18776
|
+
}), host !== null && (0, react_jsx_runtime.jsx)(PanelBoundary, {
|
|
18777
|
+
t,
|
|
18778
|
+
children: (0, react_jsx_runtime.jsx)(PendingPanel, {
|
|
18779
|
+
...panelProps,
|
|
18780
|
+
t,
|
|
18781
|
+
docked: true,
|
|
18782
|
+
dockHost: host
|
|
18783
|
+
})
|
|
18784
|
+
})] });
|
|
17869
18785
|
}
|
|
17870
18786
|
/**
|
|
17871
|
-
* The
|
|
17872
|
-
*
|
|
17873
|
-
*
|
|
17874
|
-
*
|
|
18787
|
+
* The tab chip: the mode switch, then the panel's title with the pending count —
|
|
18788
|
+
* read live from the store so the chip tracks the list without re-registering the
|
|
18789
|
+
* tab. The glyph leads because that is this seat's convention (the document tab
|
|
18790
|
+
* puts its file-type icon there), and because the chip's tail is masked for the
|
|
18791
|
+
* kit's own close button.
|
|
18792
|
+
*
|
|
18793
|
+
* A docked panel draws no header of its own, so this chip is where the panel is
|
|
18794
|
+
* named *and* where it is told to leave: the mode switch offers the two overlay
|
|
18795
|
+
* states, and its menu footer keeps this plugin's Settings one click away, which
|
|
18796
|
+
* the docked panel has no gear for.
|
|
18797
|
+
* @param props - the same face the body gets; the count and the switch are drawn.
|
|
18798
|
+
* @returns the chip content.
|
|
17875
18799
|
*/
|
|
17876
|
-
function
|
|
17877
|
-
const
|
|
17878
|
-
|
|
17879
|
-
|
|
17880
|
-
|
|
17881
|
-
|
|
17882
|
-
|
|
17883
|
-
|
|
18800
|
+
function DiffDockTitle(props) {
|
|
18801
|
+
const { t, usePending, useTabInfo, onDockClose } = props;
|
|
18802
|
+
let count = 0;
|
|
18803
|
+
try {
|
|
18804
|
+
count = usePending?.((snapshot) => snapshot.files.length) ?? 0;
|
|
18805
|
+
} catch {
|
|
18806
|
+
count = 0;
|
|
18807
|
+
}
|
|
18808
|
+
const tab = useTabInfo?.();
|
|
18809
|
+
const tabRef = (0, react.useRef)(tab);
|
|
18810
|
+
tabRef.current = tab;
|
|
18811
|
+
(0, react.useEffect)(() => {
|
|
18812
|
+
onDockClose?.(() => {
|
|
18813
|
+
tabRef.current?.tab.actions.close();
|
|
18814
|
+
});
|
|
18815
|
+
return () => {
|
|
18816
|
+
onDockClose?.(void 0);
|
|
18817
|
+
};
|
|
18818
|
+
}, [onDockClose]);
|
|
18819
|
+
/** Leave the dock for an overlay: remember it, close this tab, and let the
|
|
18820
|
+
* footer entry (a separate mount) show the panel in the overlay. */
|
|
18821
|
+
const leaveDock = (next) => {
|
|
18822
|
+
if (next === "dock") return;
|
|
18823
|
+
setPanelPresentation(next);
|
|
18824
|
+
tabRef.current?.tab.actions.close();
|
|
18825
|
+
window.dispatchEvent(new CustomEvent(SHOW_PANEL_EVENT));
|
|
18826
|
+
};
|
|
18827
|
+
return (0, react_jsx_runtime.jsxs)("span", {
|
|
18828
|
+
className: PendingPanel_module_css_default.chip,
|
|
18829
|
+
"data-diff-approval-chip": true,
|
|
18830
|
+
children: [(0, react_jsx_runtime.jsx)("span", {
|
|
18831
|
+
className: PendingPanel_module_css_default.chipLabel,
|
|
18832
|
+
children: count > 0 ? `${t("panel.title")} · ${count}` : t("panel.title")
|
|
18833
|
+
}), (0, react_jsx_runtime.jsx)(PresentationMenu, {
|
|
18834
|
+
t,
|
|
18835
|
+
current: "dock",
|
|
18836
|
+
onChoose: leaveDock,
|
|
18837
|
+
compact: true
|
|
18838
|
+
})]
|
|
18839
|
+
});
|
|
17884
18840
|
}
|
|
17885
18841
|
/**
|
|
17886
|
-
*
|
|
17887
|
-
*
|
|
17888
|
-
*
|
|
17889
|
-
*
|
|
18842
|
+
* Find the sidebar's services and, once they are there, attach the panel to them:
|
|
18843
|
+
* register the tab type (with a guide entry, so the panel is discoverable from the
|
|
18844
|
+
* sidebar's own guide page) and hand the live sidebar to `attach`. Where they are
|
|
18845
|
+
* never provided — an older app, or a composition without the right sidebar —
|
|
18846
|
+
* nothing is registered and nothing attaches, which is what makes the whole
|
|
18847
|
+
* feature optional rather than required.
|
|
18848
|
+
*
|
|
18849
|
+
* Discovery is a timed optional lookup rather than cordis's dynamic `inject`,
|
|
18850
|
+
* because the plugin sandbox forwards `effect`/`on`/timers but not `inject`, and
|
|
18851
|
+
* the sidebar may attach after this plugin does. Timers are the window's own (the
|
|
18852
|
+
* sandbox's ctx timer verbs want a `timer` declaration, and a host that withholds
|
|
18853
|
+
* the verb must not take the panel down with it).
|
|
18854
|
+
* @param ctx - the client context (structurally, see {@link DockHostContext}).
|
|
18855
|
+
* @param copy - the tab's copy, read per call so a language change needs no
|
|
18856
|
+
* re-registration.
|
|
18857
|
+
* @param attach - takes the live sidebar; returns its detach.
|
|
17890
18858
|
*/
|
|
17891
|
-
function
|
|
17892
|
-
|
|
17893
|
-
|
|
17894
|
-
|
|
17895
|
-
|
|
17896
|
-
|
|
17897
|
-
|
|
17898
|
-
|
|
18859
|
+
function attachDiffDock(ctx, copy, attach) {
|
|
18860
|
+
let timer;
|
|
18861
|
+
let attached = false;
|
|
18862
|
+
/** The last lookup's shape, kept so "no dock" can say which half was missing. */
|
|
18863
|
+
let detail = "not looked up yet";
|
|
18864
|
+
/** The shape already reported to `attach`, so the poll reports once per result
|
|
18865
|
+
* rather than forty times. */
|
|
18866
|
+
let reported = "";
|
|
18867
|
+
const stop = () => {
|
|
18868
|
+
if (timer === void 0) return;
|
|
18869
|
+
window.clearTimeout(timer);
|
|
18870
|
+
timer = void 0;
|
|
18871
|
+
};
|
|
18872
|
+
/** Hand "no sidebar, and here is what the lookup saw" to the state — once per
|
|
18873
|
+
* distinct shape, so a 100-tick poll reports once and then only on a change.
|
|
18874
|
+
* The panel surfaces it when the user asks for the dock. */
|
|
18875
|
+
const report = () => {
|
|
18876
|
+
if (detail === reported) return;
|
|
18877
|
+
reported = detail;
|
|
18878
|
+
attach(void 0, detail);
|
|
18879
|
+
};
|
|
18880
|
+
const look = () => {
|
|
18881
|
+
if (attached) return true;
|
|
18882
|
+
try {
|
|
18883
|
+
let sidebar;
|
|
18884
|
+
let registry;
|
|
18885
|
+
try {
|
|
18886
|
+
sidebar = ctx.get("sidebarRight");
|
|
18887
|
+
registry = ctx.get("sidebarRightTabs");
|
|
18888
|
+
} catch (error) {
|
|
18889
|
+
detail = `ctx.get threw: ${error instanceof Error ? error.message : String(error)}`;
|
|
18890
|
+
report();
|
|
18891
|
+
return false;
|
|
18892
|
+
}
|
|
18893
|
+
detail = diagnose(ctx);
|
|
18894
|
+
if (sidebar === void 0 || registry === void 0) {
|
|
18895
|
+
report();
|
|
18896
|
+
return false;
|
|
18897
|
+
}
|
|
18898
|
+
attached = true;
|
|
18899
|
+
ctx.effect(() => attach(sidebar), "diff-approval: dock sidebar");
|
|
18900
|
+
ctx.effect(() => registry.register({
|
|
18901
|
+
id: DIFF_DOCK_ID,
|
|
18902
|
+
kind: DIFF_DOCK_KIND,
|
|
18903
|
+
title: () => copy.title(),
|
|
18904
|
+
guide: [{
|
|
18905
|
+
order: DIFF_DOCK_GUIDE_ORDER,
|
|
18906
|
+
title: () => copy.title(),
|
|
18907
|
+
description: () => copy.guideDescription()
|
|
18908
|
+
}]
|
|
18909
|
+
}), "diff-approval: right-sidebar tab type");
|
|
18910
|
+
return true;
|
|
18911
|
+
} catch (error) {
|
|
18912
|
+
detail = `dock wiring threw: ${error instanceof Error ? error.message : String(error)}`;
|
|
18913
|
+
report();
|
|
18914
|
+
return false;
|
|
18915
|
+
}
|
|
18916
|
+
};
|
|
18917
|
+
ctx.effect(() => stop, "diff-approval: dock lookup");
|
|
18918
|
+
if (look()) return;
|
|
18919
|
+
let attempts = 0;
|
|
18920
|
+
const tick = () => {
|
|
18921
|
+
timer = void 0;
|
|
18922
|
+
if (look()) {
|
|
18923
|
+
stop();
|
|
18924
|
+
return;
|
|
18925
|
+
}
|
|
18926
|
+
if (++attempts >= DOCK_LOOKUP_ATTEMPTS) {
|
|
18927
|
+
stop();
|
|
18928
|
+
attach(void 0, `no right sidebar after ${DOCK_LOOKUP_ATTEMPTS} looks: ${detail}`);
|
|
18929
|
+
return;
|
|
18930
|
+
}
|
|
18931
|
+
timer = window.setTimeout(tick, DOCK_LOOKUP_INTERVAL_MS);
|
|
18932
|
+
};
|
|
18933
|
+
timer = window.setTimeout(tick, DOCK_LOOKUP_INTERVAL_MS);
|
|
17899
18934
|
}
|
|
17900
|
-
|
|
18935
|
+
//#endregion
|
|
18936
|
+
//#region lib/types/client/panel-memory.js
|
|
17901
18937
|
/**
|
|
17902
|
-
*
|
|
17903
|
-
*
|
|
17904
|
-
*
|
|
18938
|
+
* Where the review panel was left, for this page's lifetime.
|
|
18939
|
+
*
|
|
18940
|
+
* Closing the panel is not "done reviewing": reopening it should put the reader
|
|
18941
|
+
* back on the file they were in, at the offset they were at. That is a fact about
|
|
18942
|
+
* this visit rather than a stored preference, so it lives in module state — which
|
|
18943
|
+
* also gives the two mounts that show the panel (the floating overlay and the
|
|
18944
|
+
* docked tab) one shared memory, so switching presentation does not lose the
|
|
18945
|
+
* place, and a reload starts clean.
|
|
18946
|
+
*
|
|
18947
|
+
* Keyed by session: two sessions have their own pending lists, and one session's
|
|
18948
|
+
* panel must not resume another's file.
|
|
18949
|
+
*
|
|
18950
|
+
* @module dsh-diff-approval/client/panel-memory
|
|
17905
18951
|
*/
|
|
17906
|
-
|
|
17907
|
-
|
|
17908
|
-
|
|
17909
|
-
|
|
17910
|
-
|
|
17911
|
-
|
|
17912
|
-
|
|
17913
|
-
|
|
17914
|
-
|
|
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"
|
|
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] ?? "";
|
|
18952
|
+
const views = /* @__PURE__ */ new Map();
|
|
18953
|
+
/** This session's record, created on first use. */
|
|
18954
|
+
function viewOf(sessionId) {
|
|
18955
|
+
let view = views.get(sessionId);
|
|
18956
|
+
if (view === void 0) {
|
|
18957
|
+
view = { offsets: /* @__PURE__ */ new Map() };
|
|
18958
|
+
views.set(sessionId, view);
|
|
18959
|
+
}
|
|
18960
|
+
return view;
|
|
17933
18961
|
}
|
|
17934
|
-
/**
|
|
17935
|
-
|
|
17936
|
-
|
|
18962
|
+
/**
|
|
18963
|
+
* Remember that a session's panel was left showing one file, and where in it.
|
|
18964
|
+
* @param sessionId - the session the panel was reviewing; nothing is recorded
|
|
18965
|
+
* without one.
|
|
18966
|
+
* @param view - the pending entry that was open, and the code view's scrollTop.
|
|
18967
|
+
* An absent offset *forgets* any remembered place for that file, which is what
|
|
18968
|
+
* a caller that is about to open it at its first change wants: a mount that only
|
|
18969
|
+
* appears afterwards then lands there too, instead of on a stale offset.
|
|
18970
|
+
*/
|
|
18971
|
+
function rememberPanelView(sessionId, view) {
|
|
18972
|
+
if (sessionId === void 0) return;
|
|
18973
|
+
const record = viewOf(sessionId);
|
|
18974
|
+
record.last = view.fileId;
|
|
18975
|
+
if (view.scrollTop === void 0) record.offsets.delete(view.fileId);
|
|
18976
|
+
else record.offsets.set(view.fileId, view.scrollTop);
|
|
17937
18977
|
}
|
|
17938
18978
|
/**
|
|
17939
|
-
*
|
|
17940
|
-
*
|
|
17941
|
-
*
|
|
17942
|
-
*
|
|
17943
|
-
* @param event - the keydown event.
|
|
17944
|
-
* @param shortcut - the chord string.
|
|
17945
|
-
* @returns whether the event matches.
|
|
18979
|
+
* The file a session's panel was last showing.
|
|
18980
|
+
* @param sessionId - the session, if any.
|
|
18981
|
+
* @returns the pending entry id, or undefined when that session's panel has never
|
|
18982
|
+
* closed on a file.
|
|
17946
18983
|
*/
|
|
17947
|
-
function
|
|
17948
|
-
|
|
17949
|
-
|
|
17950
|
-
|
|
17951
|
-
|
|
17952
|
-
|
|
17953
|
-
|
|
17954
|
-
|
|
17955
|
-
|
|
17956
|
-
|
|
18984
|
+
function lastPanelFile(sessionId) {
|
|
18985
|
+
return sessionId === void 0 ? void 0 : views.get(sessionId)?.last;
|
|
18986
|
+
}
|
|
18987
|
+
/**
|
|
18988
|
+
* How far down one file was left, when it has been left before.
|
|
18989
|
+
* @param sessionId - the session, if any.
|
|
18990
|
+
* @param fileId - the pending entry id.
|
|
18991
|
+
* @returns the remembered scrollTop, or undefined when there is none.
|
|
18992
|
+
*/
|
|
18993
|
+
function panelFileOffset(sessionId, fileId) {
|
|
18994
|
+
return sessionId === void 0 ? void 0 : views.get(sessionId)?.offsets.get(fileId);
|
|
17957
18995
|
}
|
|
17958
18996
|
//#endregion
|
|
17959
18997
|
//#region lib/types/client/PendingPanel.js
|
|
@@ -17969,20 +19007,75 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
17969
19007
|
const FALLBACK_BOTTOM_PX = 128;
|
|
17970
19008
|
/** Gap kept between the composer's top edge and the panel bottom, in px. */
|
|
17971
19009
|
const COMPOSER_GAP_PX = 12;
|
|
17972
|
-
/**
|
|
19010
|
+
/** The coverage switch each chord toggles, in the order the popover lists them. */
|
|
19011
|
+
const COVER_ACTIONS = [
|
|
19012
|
+
["coverLeft", "left"],
|
|
19013
|
+
["coverTop", "top"],
|
|
19014
|
+
["coverRight", "right"],
|
|
19015
|
+
["coverComposer", "composer"]
|
|
19016
|
+
];
|
|
19017
|
+
/** Fixed window-edge inset the floating panel keeps on every side, mirroring
|
|
19018
|
+
* `.panel`'s own left/right/top. */
|
|
17973
19019
|
const PANEL_INSET_PX = 8;
|
|
19020
|
+
/** The smallest floating panel worth drawing. A window that cannot hold the
|
|
19021
|
+
* uncovered edges *and* a panel this big covers them instead — the edges are a
|
|
19022
|
+
* preference, an invisible panel is a bug, and a phone-sized window is exactly
|
|
19023
|
+
* the case where leaving a sidebar visible can squeeze the panel to nothing. */
|
|
19024
|
+
const MIN_PANEL_WIDTH_PX = 240;
|
|
19025
|
+
const MIN_PANEL_HEIGHT_PX = 200;
|
|
19026
|
+
/**
|
|
19027
|
+
* The floating panel's four insets for the current coverage. An edge the panel
|
|
19028
|
+
* covers keeps the small window inset; one it does not cover starts past what the
|
|
19029
|
+
* app draws there. If the result would leave the panel smaller than
|
|
19030
|
+
* {@link MIN_PANEL_WIDTH_PX} / {@link MIN_PANEL_HEIGHT_PX}, the sides are covered
|
|
19031
|
+
* again rather than drawn as an invisible sliver.
|
|
19032
|
+
* @param cover - which edges the panel is covering.
|
|
19033
|
+
* @param sideInset - what the app occupies on each side (see {@link frameInsets}).
|
|
19034
|
+
* @param bottomPx - the composer offset used when the composer is not covered.
|
|
19035
|
+
* @param viewport - the window's size in px.
|
|
19036
|
+
* @returns the insets for the panel's style.
|
|
19037
|
+
*/
|
|
19038
|
+
function panelInsets(cover, sideInset, bottomPx, viewport) {
|
|
19039
|
+
const side = (covered, value) => covered ? PANEL_INSET_PX : value + PANEL_INSET_PX;
|
|
19040
|
+
let left = side(cover.left, sideInset.left);
|
|
19041
|
+
let right = side(cover.right, sideInset.right);
|
|
19042
|
+
if (viewport.width - left - right < MIN_PANEL_WIDTH_PX) {
|
|
19043
|
+
left = PANEL_INSET_PX;
|
|
19044
|
+
right = PANEL_INSET_PX;
|
|
19045
|
+
}
|
|
19046
|
+
let top = cover.top ? PANEL_INSET_PX : sideInset.top;
|
|
19047
|
+
let bottom = cover.composer ? PANEL_INSET_PX : bottomPx;
|
|
19048
|
+
if (viewport.height - top - bottom < MIN_PANEL_HEIGHT_PX) {
|
|
19049
|
+
top = PANEL_INSET_PX;
|
|
19050
|
+
bottom = PANEL_INSET_PX;
|
|
19051
|
+
}
|
|
19052
|
+
return {
|
|
19053
|
+
top,
|
|
19054
|
+
left,
|
|
19055
|
+
right,
|
|
19056
|
+
bottom
|
|
19057
|
+
};
|
|
19058
|
+
}
|
|
19059
|
+
/** Narrowest docked panel that still fits the file list beside the detail. Below
|
|
19060
|
+
* it the list folds into the floating card, exactly as it does in a narrow
|
|
19061
|
+
* floating panel — a right-sidebar column is narrow even on a wide window, so the
|
|
19062
|
+
* viewport width says nothing about it. */
|
|
19063
|
+
const DOCK_TWO_COLUMN_MIN_PX = 520;
|
|
17974
19064
|
/** The harness composer seat (conversation scroll body + seat div). */
|
|
17975
19065
|
const SCROLL_SELECTOR = "[data-conversation-scroll]";
|
|
17976
19066
|
const SEAT_SELECTOR = "[data-composer-seat]";
|
|
19067
|
+
/** The chat composer's own editable box, where a closed panel hands the caret. */
|
|
19068
|
+
const COMPOSER_INPUT_SELECTOR = "[data-composer-input]";
|
|
19069
|
+
/**
|
|
19070
|
+
* Hand the caret back to the chat composer. Closing the review panel is a "done
|
|
19071
|
+
* reviewing, back to typing" move; a close the user made by clicking elsewhere is
|
|
19072
|
+
* the exception, and that path does not call this.
|
|
19073
|
+
*/
|
|
19074
|
+
function focusComposer() {
|
|
19075
|
+
document.querySelector(COMPOSER_INPUT_SELECTOR)?.focus();
|
|
19076
|
+
}
|
|
17977
19077
|
/** Seat height ui-conversation publishes for floating controls (its own seat observer). */
|
|
17978
19078
|
const COMPOSER_HEIGHT_VAR = "--dsh-composer-height";
|
|
17979
|
-
/** Interactive composer/approval cards; clicking these keeps the panel open.
|
|
17980
|
-
Deliberately the cards themselves, not the seat: the approval frame's wide
|
|
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\"]";
|
|
17986
19079
|
/** Seat counts as docked when its bottom is this close to the window bottom. */
|
|
17987
19080
|
const DOCKED_TOLERANCE_PX = 48;
|
|
17988
19081
|
/** File-list pane width bounds for the manual split drag, in px. */
|
|
@@ -18090,10 +19183,11 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
18090
19183
|
/** Total width of the two line-number gutters, subtracted from the code width
|
|
18091
19184
|
* when measuring wrapped line heights. */
|
|
18092
19185
|
const WRAP_GUTTERS_PX = 88;
|
|
18093
|
-
/**
|
|
18094
|
-
*
|
|
18095
|
-
*
|
|
18096
|
-
|
|
19186
|
+
/** How long a requested dock may take to actually show the panel before the
|
|
19187
|
+
* overlay steps in. The tab body mounts a frame or two after the ask, so the
|
|
19188
|
+
* wait is short — but a sidebar that accepts the ask and never brings the tab up
|
|
19189
|
+
* must not swallow the click. */
|
|
19190
|
+
const DOCK_REVEAL_GRACE_MS = 400;
|
|
18097
19191
|
/** A shared canvas for measuring wrapped line heights (CPU-only, no DOM reflow). */
|
|
18098
19192
|
let measureCanvas;
|
|
18099
19193
|
/**
|
|
@@ -18313,6 +19407,78 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
18313
19407
|
})
|
|
18314
19408
|
});
|
|
18315
19409
|
}
|
|
19410
|
+
/**
|
|
19411
|
+
* The app's view tabs above the conversation (对话 / 轨迹 and the like). A panel
|
|
19412
|
+
* that leaves the header visible still covers these: the session's title row is
|
|
19413
|
+
* the part worth keeping, and the tab strip sits directly under it, above the
|
|
19414
|
+
* conversation body — a sibling of the scroller's own parent. Nothing else's
|
|
19415
|
+
* tablists count: the right sidebar's own tab strip, and any inside this panel,
|
|
19416
|
+
* are excluded.
|
|
19417
|
+
* @param scroller - the conversation scroll body.
|
|
19418
|
+
* @returns the strip's top edge in viewport px, or undefined when there is none.
|
|
19419
|
+
*/
|
|
19420
|
+
function viewTabsTop(scroller) {
|
|
19421
|
+
for (const strip of document.querySelectorAll("[role=\"tablist\"]")) {
|
|
19422
|
+
if (strip.closest("[data-sidebar-right-panel]") !== null) continue;
|
|
19423
|
+
if (strip.closest("[data-diff-approval-panel]") !== null) continue;
|
|
19424
|
+
if ((strip.compareDocumentPosition(scroller) & Node.DOCUMENT_POSITION_FOLLOWING) === 0) continue;
|
|
19425
|
+
const rect = strip.getBoundingClientRect();
|
|
19426
|
+
if (rect.height > 0) return rect.top;
|
|
19427
|
+
}
|
|
19428
|
+
}
|
|
19429
|
+
/**
|
|
19430
|
+
* How much of the window the app itself occupies on each side of the conversation
|
|
19431
|
+
* — how far the floating panel has to start from an edge to leave that part
|
|
19432
|
+
* visible.
|
|
19433
|
+
*
|
|
19434
|
+
* The centre column's own box is the direct answer: the header above it, an
|
|
19435
|
+
* expanded sidebar, a collapsed rail, and a right panel hanging over the centre
|
|
19436
|
+
* all sit outside it. The frame's grid tracks fill in the rest — they name the
|
|
19437
|
+
* sidebar columns even before a conversation is mounted, and a right sidebar shown
|
|
19438
|
+
* without a track of its own draws over the centre, so its own panel is measured
|
|
19439
|
+
* instead. Relying on the frame's *resizers* alone was a bug: a collapsed sidebar
|
|
19440
|
+
* renders no resizer, so a collapsed rail read as "nothing on that side".
|
|
19441
|
+
* @returns each side's occupied size, 0 when that side shows nothing.
|
|
19442
|
+
*/
|
|
19443
|
+
function frameInsets() {
|
|
19444
|
+
if (typeof document === "undefined") return {
|
|
19445
|
+
top: 0,
|
|
19446
|
+
bottom: 0,
|
|
19447
|
+
left: 0,
|
|
19448
|
+
right: 0
|
|
19449
|
+
};
|
|
19450
|
+
const viewport = window.innerWidth;
|
|
19451
|
+
const viewportHeight = window.innerHeight;
|
|
19452
|
+
let top = 0;
|
|
19453
|
+
let bottom = 0;
|
|
19454
|
+
let left = 0;
|
|
19455
|
+
let right = 0;
|
|
19456
|
+
const scroller = document.querySelector("[data-conversation-scroll]");
|
|
19457
|
+
const centre = scroller?.getBoundingClientRect();
|
|
19458
|
+
if (scroller != null && centre !== void 0 && centre.width > 0 && centre.height > 0) {
|
|
19459
|
+
top = Math.max(top, viewTabsTop(scroller) ?? centre.top);
|
|
19460
|
+
bottom = Math.max(bottom, viewportHeight - centre.bottom);
|
|
19461
|
+
left = Math.max(left, centre.left);
|
|
19462
|
+
right = Math.max(right, viewport - centre.right);
|
|
19463
|
+
}
|
|
19464
|
+
const frame = document.querySelector("[data-side=\"sidebar\"],[data-side=\"rightbar\"]")?.parentElement ?? document.querySelector("[data-sidebar-collapsed],[data-rightbar-collapsed],[data-rightbar-fullscreen]");
|
|
19465
|
+
const frameRect = frame?.getBoundingClientRect();
|
|
19466
|
+
if (frame != null && frameRect !== void 0) {
|
|
19467
|
+
const tracks = getComputedStyle(frame).gridTemplateColumns.split(" ").map((part) => Number.parseFloat(part)).filter(Number.isFinite);
|
|
19468
|
+
if (tracks.length > 0) {
|
|
19469
|
+
left = Math.max(left, frameRect.left + (tracks[0] ?? 0));
|
|
19470
|
+
right = Math.max(right, tracks.length > 1 ? tracks[tracks.length - 1] ?? 0 : 0);
|
|
19471
|
+
}
|
|
19472
|
+
}
|
|
19473
|
+
const rightPanel = document.querySelector("[data-sidebar-right-panel]")?.getBoundingClientRect();
|
|
19474
|
+
if (rightPanel !== void 0 && rightPanel.width > 0) right = Math.max(right, viewport - rightPanel.left);
|
|
19475
|
+
return {
|
|
19476
|
+
top,
|
|
19477
|
+
bottom,
|
|
19478
|
+
left,
|
|
19479
|
+
right
|
|
19480
|
+
};
|
|
19481
|
+
}
|
|
18316
19482
|
/** The diff body's row class per line kind. */
|
|
18317
19483
|
const ROW_CLASS = {
|
|
18318
19484
|
context: PendingPanel_module_css_default.context,
|
|
@@ -18424,26 +19590,6 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
18424
19590
|
if (cursor < segEnd) push(cursor, segEnd, false);
|
|
18425
19591
|
return nodes;
|
|
18426
19592
|
}
|
|
18427
|
-
/** Arrow keys render as arrows in a tooltip hint (`Ctrl+↑`, not `Ctrl+ArrowUp`). */
|
|
18428
|
-
const CHORD_KEY_GLYPHS = {
|
|
18429
|
-
ArrowUp: "↑",
|
|
18430
|
-
ArrowDown: "↓",
|
|
18431
|
-
ArrowLeft: "←",
|
|
18432
|
-
ArrowRight: "→"
|
|
18433
|
-
};
|
|
18434
|
-
/**
|
|
18435
|
-
* A tooltip label with the action's configured chord appended. The hint states
|
|
18436
|
-
* the binding the user actually has — including one rebound in Settings — rather
|
|
18437
|
-
* than a default baked into the label.
|
|
18438
|
-
* @param label - the translated action label.
|
|
18439
|
-
* @param action - the keybinding action id (see `DEFAULT_KEYBINDINGS`).
|
|
18440
|
-
* @returns the label, with ` (chord)` appended when one is configured.
|
|
18441
|
-
*/
|
|
18442
|
-
function withChord(label, action) {
|
|
18443
|
-
const chord = keybindingOf(action);
|
|
18444
|
-
if (chord === "") return label;
|
|
18445
|
-
return `${label} (${chord.split("+").map((part) => CHORD_KEY_GLYPHS[part] ?? part).join("+")})`;
|
|
18446
|
-
}
|
|
18447
19593
|
/**
|
|
18448
19594
|
* Whether a key event's target is one of the panel's search query boxes. The
|
|
18449
19595
|
* bar's own chords (step, narrow) are scoped this way: the caret has to be in
|
|
@@ -18481,6 +19627,18 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
18481
19627
|
return target instanceof Element && target.closest("input, textarea, [contenteditable=\"true\"]") !== null;
|
|
18482
19628
|
}
|
|
18483
19629
|
/**
|
|
19630
|
+
* Whether a key event came from the chat composer. The coverage chords fire
|
|
19631
|
+
* there — the user is usually typing when the panel's edges need rearranging —
|
|
19632
|
+
* while every other text field (this panel's own search box, the add-path
|
|
19633
|
+
* dialog's input) keeps its Ctrl+Shift+Arrow for word-wise selection.
|
|
19634
|
+
* @param event - the keydown event.
|
|
19635
|
+
* @returns whether the event came from the composer.
|
|
19636
|
+
*/
|
|
19637
|
+
function isComposerEvent(event) {
|
|
19638
|
+
const target = event.target;
|
|
19639
|
+
return target instanceof Element && target.closest("[data-composer-input], [data-composer-card]") !== null;
|
|
19640
|
+
}
|
|
19641
|
+
/**
|
|
18484
19642
|
* The in-file search's narrowing toggles. The persisted preference is the
|
|
18485
19643
|
* source of truth, so the choice survives a reopen and the two views cannot
|
|
18486
19644
|
* disagree: each bar owns one of these, and it re-reads the stored flags with
|
|
@@ -19324,13 +20482,18 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
19324
20482
|
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, { size: 14 })
|
|
19325
20483
|
})
|
|
19326
20484
|
}),
|
|
19327
|
-
(0, react_jsx_runtime.jsx)(
|
|
19328
|
-
|
|
19329
|
-
|
|
19330
|
-
|
|
19331
|
-
|
|
19332
|
-
|
|
19333
|
-
|
|
20485
|
+
(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
20486
|
+
label: t("action.closeHintEsc"),
|
|
20487
|
+
side: "bottom",
|
|
20488
|
+
delayMs: 500,
|
|
20489
|
+
children: (0, react_jsx_runtime.jsx)("button", {
|
|
20490
|
+
type: "button",
|
|
20491
|
+
className: `${PendingPanel_module_css_default.action} ${PendingPanel_module_css_default.iconAction}`,
|
|
20492
|
+
"data-diff-search-close": true,
|
|
20493
|
+
"aria-label": t("action.close"),
|
|
20494
|
+
onClick: closeSearch,
|
|
20495
|
+
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCloseOutline16, { size: 14 })
|
|
20496
|
+
})
|
|
19334
20497
|
})
|
|
19335
20498
|
]
|
|
19336
20499
|
}),
|
|
@@ -19633,7 +20796,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
19633
20796
|
});
|
|
19634
20797
|
}
|
|
19635
20798
|
/** The selected file's diff, actions, jump controls, and copy toolbar. */
|
|
19636
|
-
function PendingDiff({ file, busy, workspacePath, jumpSignal, undoFlash, failedMessage, onPasteReference, onToast, t, onKeep, onRevert, onRefreshVcs, onBlockKeep, onBlockRevert, onOpen, onPreviewImage
|
|
20799
|
+
function PendingDiff({ file, busy, workspacePath, jumpSignal, undoFlash, landingTop, landingTick, failedMessage, onPasteReference, onToast, t, onKeep, onRevert, onRefreshVcs, onBlockKeep, onBlockRevert, onOpen, onPreviewImage }) {
|
|
19637
20800
|
const [langOverride, setLangOverride] = (0, react.useState)(void 0);
|
|
19638
20801
|
const [langMenuOpen, setLangMenuOpen] = (0, react.useState)(false);
|
|
19639
20802
|
const langMenuItems = (0, react.useMemo)(() => [{
|
|
@@ -19798,6 +20961,9 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
19798
20961
|
const [previewFlash, setPreviewFlash] = (0, react.useState)(void 0);
|
|
19799
20962
|
const previewFlashRef = (0, react.useRef)(null);
|
|
19800
20963
|
const previewFlashPendingRef = (0, react.useRef)(false);
|
|
20964
|
+
/** The offset the file now showing was asked to open at, held until the landing
|
|
20965
|
+
* effect below spends it (that effect runs a render later than the switch). */
|
|
20966
|
+
const landingTopRef = (0, react.useRef)(void 0);
|
|
19801
20967
|
const selectionTextRef = (0, react.useRef)("");
|
|
19802
20968
|
const [copied, setCopied] = (0, react.useState)(false);
|
|
19803
20969
|
const [searchOpen, setSearchOpen] = (0, react.useState)(false);
|
|
@@ -19813,10 +20979,13 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
19813
20979
|
previewFlashPendingRef.current = true;
|
|
19814
20980
|
};
|
|
19815
20981
|
(0, react.useEffect)(() => {
|
|
19816
|
-
|
|
20982
|
+
landingTopRef.current = landingTop;
|
|
20983
|
+
if (landingTop === void 0) {
|
|
20984
|
+
setFocus(0);
|
|
20985
|
+
bumpFlash(false);
|
|
20986
|
+
} else setFocus(blockIndexAtOffset(landingTop));
|
|
19817
20987
|
setScrollTick((tick) => tick + 1);
|
|
19818
20988
|
bodyRef.current?.focus();
|
|
19819
|
-
bumpFlash(false);
|
|
19820
20989
|
setHoveredBlock(void 0);
|
|
19821
20990
|
setSelection(void 0);
|
|
19822
20991
|
setLangOverride(void 0);
|
|
@@ -19825,7 +20994,11 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
19825
20994
|
setSearchOpen(false);
|
|
19826
20995
|
setSearchQuery("");
|
|
19827
20996
|
setSearchIndex(0);
|
|
19828
|
-
}, [
|
|
20997
|
+
}, [
|
|
20998
|
+
file.id,
|
|
20999
|
+
landingTop,
|
|
21000
|
+
landingTick
|
|
21001
|
+
]);
|
|
19829
21002
|
(0, react.useEffect)(() => {
|
|
19830
21003
|
if (undoFlash === 0) return;
|
|
19831
21004
|
setFocus(0);
|
|
@@ -20426,6 +21599,19 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
20426
21599
|
]);
|
|
20427
21600
|
(0, react.useLayoutEffect)(() => {
|
|
20428
21601
|
if (rowCount === 0) return;
|
|
21602
|
+
const resumed = landingTopRef.current;
|
|
21603
|
+
if (resumed !== void 0) {
|
|
21604
|
+
landingTopRef.current = void 0;
|
|
21605
|
+
if (!previewActive) {
|
|
21606
|
+
const resumeBody = bodyRef.current;
|
|
21607
|
+
if (resumeBody !== null) {
|
|
21608
|
+
const clamped = Math.max(0, Math.min(resumed, resumeBody.scrollHeight - resumeBody.clientHeight));
|
|
21609
|
+
if (resumeBody.scrollTop !== clamped) resumeBody.scrollTop = clamped;
|
|
21610
|
+
setScrollTop(clamped);
|
|
21611
|
+
}
|
|
21612
|
+
}
|
|
21613
|
+
return;
|
|
21614
|
+
}
|
|
20429
21615
|
const block = model.blocks[focus];
|
|
20430
21616
|
if (block === void 0) return;
|
|
20431
21617
|
if (previewActive) {
|
|
@@ -20527,6 +21713,23 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
20527
21713
|
if (jumpSignal === 0) return;
|
|
20528
21714
|
jumpBlock(1, true, false);
|
|
20529
21715
|
}, [jumpSignal]);
|
|
21716
|
+
/**
|
|
21717
|
+
* The change block the view is in at one scroll offset: the last block at or
|
|
21718
|
+
* above the offset, i.e. the same anchor `onScroll` re-focuses from. Used when
|
|
21719
|
+
* a resumed view starts away from the first change, so prev/next walk from the
|
|
21720
|
+
* block the reader was actually in.
|
|
21721
|
+
* @param offset - a code view scrollTop.
|
|
21722
|
+
* @returns the block index; 0 when the offset is above every change.
|
|
21723
|
+
*/
|
|
21724
|
+
const blockIndexAtOffset = (offset) => {
|
|
21725
|
+
const anchor = offset + leadRows * ROW_HEIGHT_PX;
|
|
21726
|
+
let found = 0;
|
|
21727
|
+
for (let index = 0; index < model.blocks.length; index++) {
|
|
21728
|
+
const block = model.blocks[index];
|
|
21729
|
+
if (block !== void 0 && offsetOf(block.start) <= anchor + NAV_ANCHOR_TOLERANCE_PX) found = index;
|
|
21730
|
+
}
|
|
21731
|
+
return found;
|
|
21732
|
+
};
|
|
20530
21733
|
const onScroll = () => {
|
|
20531
21734
|
const body = bodyRef.current;
|
|
20532
21735
|
if (body === null) return;
|
|
@@ -20872,13 +22075,18 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
20872
22075
|
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, { size: 14 })
|
|
20873
22076
|
})
|
|
20874
22077
|
}),
|
|
20875
|
-
(0, react_jsx_runtime.jsx)(
|
|
20876
|
-
|
|
20877
|
-
|
|
20878
|
-
|
|
20879
|
-
|
|
20880
|
-
|
|
20881
|
-
|
|
22078
|
+
(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
22079
|
+
label: t("action.closeHintEsc"),
|
|
22080
|
+
side: "bottom",
|
|
22081
|
+
delayMs: 500,
|
|
22082
|
+
children: (0, react_jsx_runtime.jsx)("button", {
|
|
22083
|
+
type: "button",
|
|
22084
|
+
className: `${PendingPanel_module_css_default.action} ${PendingPanel_module_css_default.iconAction}`,
|
|
22085
|
+
"data-diff-search-close": true,
|
|
22086
|
+
"aria-label": t("action.close"),
|
|
22087
|
+
onClick: closeSearch,
|
|
22088
|
+
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCloseOutline16, { size: 14 })
|
|
22089
|
+
})
|
|
20882
22090
|
})
|
|
20883
22091
|
]
|
|
20884
22092
|
}) : null;
|
|
@@ -20889,6 +22097,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
20889
22097
|
children: [
|
|
20890
22098
|
(0, react_jsx_runtime.jsxs)("div", {
|
|
20891
22099
|
className: PendingPanel_module_css_default.diffHeader,
|
|
22100
|
+
"data-diff-toolbar": true,
|
|
20892
22101
|
children: [
|
|
20893
22102
|
(0, react_jsx_runtime.jsx)("span", {
|
|
20894
22103
|
className: PendingPanel_module_css_default.diffPath,
|
|
@@ -20929,19 +22138,6 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
20929
22138
|
(0, react_jsx_runtime.jsxs)("div", {
|
|
20930
22139
|
className: PendingPanel_module_css_default.diffActions,
|
|
20931
22140
|
children: [
|
|
20932
|
-
floatMode && (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
20933
|
-
label: t(floatOpen ? "action.hideFileList" : "action.showFileList"),
|
|
20934
|
-
side: "bottom",
|
|
20935
|
-
delayMs: 500,
|
|
20936
|
-
children: (0, react_jsx_runtime.jsx)("button", {
|
|
20937
|
-
type: "button",
|
|
20938
|
-
className: `${PendingPanel_module_css_default.action} ${PendingPanel_module_css_default.iconAction}`,
|
|
20939
|
-
"data-diff-file-list-toggle": true,
|
|
20940
|
-
"aria-label": t(floatOpen ? "action.hideFileList" : "action.showFileList"),
|
|
20941
|
-
onClick: onToggleFileList,
|
|
20942
|
-
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconListPenOutline16, { size: 14 })
|
|
20943
|
-
})
|
|
20944
|
-
}),
|
|
20945
22141
|
(model.diff.added !== 0 || model.diff.removed !== 0) && (0, react_jsx_runtime.jsx)("span", {
|
|
20946
22142
|
className: PendingPanel_module_css_default.diffStats,
|
|
20947
22143
|
children: t("panel.stats", {
|
|
@@ -21477,16 +22673,75 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
21477
22673
|
});
|
|
21478
22674
|
}
|
|
21479
22675
|
/** Render the pending-edit review panel and its unified footer action. */
|
|
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 }) {
|
|
22676
|
+
function PendingPanel({ wide, useSessions, usePending, onRefresh, onKeep, onRevert, onBlockKeep, onBlockRevert, onOpen, onPreviewImage, onPasteReference, onUndo, onRedo, onImportVcs, onRefreshVcs, onBrowse, onAddPath, onKeepAll, onRevertAll, onAckRedoCleared, collapseSidebar, t, docked = false, dockHost, onOpenDock, closeDock, useDock }) {
|
|
21481
22677
|
const current = useSessions((state) => state.current);
|
|
21482
22678
|
const currentBlank = useSessions((state) => {
|
|
21483
22679
|
const id = state.current;
|
|
21484
22680
|
return id === void 0 ? false : state.byId[id]?.blank ?? false;
|
|
21485
22681
|
});
|
|
21486
22682
|
const noSession = current === void 0 || currentBlank;
|
|
22683
|
+
const dockShowing = useDock?.((state) => state.open) === true;
|
|
22684
|
+
const dockAvailable = useDock?.((state) => state.available) === true;
|
|
22685
|
+
/** Why the dock is unavailable, when it is (shown once, if asked for). */
|
|
22686
|
+
const dockReason = useDock?.((state) => state.reason);
|
|
22687
|
+
/** The dock's visibility as the latest render has it, for the reveal fallback's
|
|
22688
|
+
* timer (which runs outside this render's closure). */
|
|
22689
|
+
const dockShowingRef = (0, react.useRef)(false);
|
|
22690
|
+
dockShowingRef.current = dockShowing;
|
|
22691
|
+
/** The pending "did the dock actually come up" check, if any. */
|
|
22692
|
+
const revealTimerRef = (0, react.useRef)(void 0);
|
|
22693
|
+
(0, react.useEffect)(() => () => {
|
|
22694
|
+
if (revealTimerRef.current !== void 0) window.clearTimeout(revealTimerRef.current);
|
|
22695
|
+
}, []);
|
|
22696
|
+
const revealRef = (0, react.useRef)(() => {});
|
|
22697
|
+
/** The header entry's toggle, deferred to the latest render (see `toggleOpen`):
|
|
22698
|
+
* this instance owns the overlay's open state, and the entry is a second mount
|
|
22699
|
+
* that can only ask. */
|
|
22700
|
+
const toggleRef = (0, react.useRef)(() => {});
|
|
22701
|
+
(0, react.useEffect)(() => {
|
|
22702
|
+
if (docked) return;
|
|
22703
|
+
const onShow = () => {
|
|
22704
|
+
revealRef.current();
|
|
22705
|
+
};
|
|
22706
|
+
const onToggle = () => {
|
|
22707
|
+
toggleRef.current();
|
|
22708
|
+
};
|
|
22709
|
+
window.addEventListener(SHOW_PANEL_EVENT, onShow);
|
|
22710
|
+
window.addEventListener(TOGGLE_PANEL_EVENT, onToggle);
|
|
22711
|
+
return () => {
|
|
22712
|
+
window.removeEventListener(SHOW_PANEL_EVENT, onShow);
|
|
22713
|
+
window.removeEventListener(TOGGLE_PANEL_EVENT, onToggle);
|
|
22714
|
+
};
|
|
22715
|
+
}, [docked]);
|
|
22716
|
+
(0, react.useEffect)(() => {
|
|
22717
|
+
if (docked) setPanelPresentation("dock");
|
|
22718
|
+
}, [docked]);
|
|
21487
22719
|
const snapshot = usePending((snapshot) => snapshot);
|
|
21488
22720
|
const [open, setOpen] = (0, react.useState)(false);
|
|
21489
22721
|
const [selected, setSelected] = (0, react.useState)("");
|
|
22722
|
+
/**
|
|
22723
|
+
* Where the diff should land for the file it is about to show, and a nonce so a
|
|
22724
|
+
* repeated request for the *same* file lands again. `top` carries the offset the
|
|
22725
|
+
* reader was left at when the panel resumes a view (see `panel-memory`); it is
|
|
22726
|
+
* absent when the request means "show me this diff" — a file picked from the
|
|
22727
|
+
* list, the advance a decision leaves behind, or the produced-file chip — and
|
|
22728
|
+
* that lands on the file's first change.
|
|
22729
|
+
*/
|
|
22730
|
+
const [landing, setLanding] = (0, react.useState)(void 0);
|
|
22731
|
+
/** Ask the diff to land on one file: its first change unless `top` says where.
|
|
22732
|
+
* The nonce makes the request an event rather than a value, so re-clicking the
|
|
22733
|
+
* chip for the file already open lands on its first change again. */
|
|
22734
|
+
const landOn = (fileId, top) => {
|
|
22735
|
+
setLanding((prev) => ({
|
|
22736
|
+
fileId,
|
|
22737
|
+
top,
|
|
22738
|
+
n: (prev?.n ?? 0) + 1
|
|
22739
|
+
}));
|
|
22740
|
+
};
|
|
22741
|
+
/** The selection as the latest render has it, for the closers that run from a
|
|
22742
|
+
* cleanup (the docked tab unmounting) rather than from a handler. */
|
|
22743
|
+
const selectedRef = (0, react.useRef)(selected);
|
|
22744
|
+
selectedRef.current = selected;
|
|
21490
22745
|
/** Bumped when the already-open file is clicked again, to jump to the next
|
|
21491
22746
|
* diff block in the open file's detail pane. */
|
|
21492
22747
|
const [jumpSignal, setJumpSignal] = (0, react.useState)(0);
|
|
@@ -21520,8 +22775,9 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
21520
22775
|
showCopyToast(t("panel.fileNotPending"));
|
|
21521
22776
|
return;
|
|
21522
22777
|
}
|
|
21523
|
-
|
|
21524
|
-
|
|
22778
|
+
revealPanel();
|
|
22779
|
+
rememberPanelView(current, { fileId: entry.id });
|
|
22780
|
+
window.dispatchEvent(new CustomEvent(OPEN_PANEL_FILE_EVENT, { detail: { fileId: entry.id } }));
|
|
21525
22781
|
};
|
|
21526
22782
|
(0, react.useEffect)(() => {
|
|
21527
22783
|
const onOpenFile = (event) => {
|
|
@@ -21534,6 +22790,18 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
21534
22790
|
window.removeEventListener(OPEN_FILE_EVENT, onOpenFile);
|
|
21535
22791
|
};
|
|
21536
22792
|
}, []);
|
|
22793
|
+
(0, react.useEffect)(() => {
|
|
22794
|
+
const onPanelFile = (event) => {
|
|
22795
|
+
const detail = event.detail;
|
|
22796
|
+
if (detail === void 0 || typeof detail.fileId !== "string") return;
|
|
22797
|
+
setSelected(detail.fileId);
|
|
22798
|
+
landOn(detail.fileId);
|
|
22799
|
+
};
|
|
22800
|
+
window.addEventListener(OPEN_PANEL_FILE_EVENT, onPanelFile);
|
|
22801
|
+
return () => {
|
|
22802
|
+
window.removeEventListener(OPEN_PANEL_FILE_EVENT, onPanelFile);
|
|
22803
|
+
};
|
|
22804
|
+
}, []);
|
|
21537
22805
|
/** Whether the redo-cleared notice is showing (bottom-right, OK to dismiss). */
|
|
21538
22806
|
const [redoClearedNotice, setRedoClearedNotice] = (0, react.useState)(false);
|
|
21539
22807
|
/** A last-block keep/revert awaiting the user's remove-or-keep choice; the
|
|
@@ -21545,17 +22813,34 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
21545
22813
|
const [addOpen, setAddOpen] = (0, react.useState)(false);
|
|
21546
22814
|
/** Bottom offset tracking the chat composer's top edge so the input stays visible. */
|
|
21547
22815
|
const [bottomPx, setBottomPx] = (0, react.useState)(FALLBACK_BOTTOM_PX);
|
|
21548
|
-
/**
|
|
21549
|
-
|
|
22816
|
+
/** The app frame's sidebar columns, in px: how far the floating panel has to
|
|
22817
|
+
* start from each window edge to leave that sidebar visible. */
|
|
22818
|
+
/** How much of the window the app occupies on each side of the conversation:
|
|
22819
|
+
* how far the floating panel starts from an edge it does not cover. */
|
|
22820
|
+
const [sideInset, setSideInset] = (0, react.useState)({
|
|
22821
|
+
top: 0,
|
|
22822
|
+
bottom: 0,
|
|
22823
|
+
left: 0,
|
|
22824
|
+
right: 0
|
|
22825
|
+
});
|
|
22826
|
+
/** What the floating panel covers: the app's two sidebars and the composer. */
|
|
22827
|
+
const [cover, setCover] = (0, react.useState)(() => panelCover());
|
|
22828
|
+
/** The chord's on-screen echo: the edge just flipped, and a nonce so a repeat
|
|
22829
|
+
* restarts the notice instead of re-rendering the same one. */
|
|
22830
|
+
const [coverNotice, setCoverNotice] = (0, react.useState)(null);
|
|
21550
22831
|
/** File-list pane width, adjustable by dragging the divider. */
|
|
21551
22832
|
const [listWidth, setListWidth] = (0, react.useState)(240);
|
|
21552
22833
|
const resizeDrag = (0, react.useRef)(null);
|
|
21553
|
-
/** Whether the floating (collapsed) file list is currently
|
|
22834
|
+
/** Whether the floating (collapsed) file list is currently shown. */
|
|
21554
22835
|
const [floatOpen, setFloatOpen] = (0, react.useState)(false);
|
|
22836
|
+
/** Whether the file list is always folded, whatever the width allows. */
|
|
22837
|
+
const [forceFloat, setForceFloat] = (0, react.useState)(() => fileListFloat());
|
|
21555
22838
|
/** The review panel's width, measured so the file list can collapse when it
|
|
21556
22839
|
* would take more than a third of it (browser zoom / window resize). */
|
|
21557
22840
|
const [panelWidth, setPanelWidth] = (0, react.useState)(0);
|
|
21558
22841
|
const [viewportWidth, setViewportWidth] = (0, react.useState)(() => window.innerWidth);
|
|
22842
|
+
/** The window's height, for the panel's minimum-size floor (see {@link panelInsets}). */
|
|
22843
|
+
const [viewportHeight, setViewportHeight] = (0, react.useState)(() => window.innerHeight);
|
|
21559
22844
|
const panelRef = (0, react.useRef)(null);
|
|
21560
22845
|
const splitRef = (0, react.useRef)(null);
|
|
21561
22846
|
/** The code scroll box's bounds within the split, so the floating card is
|
|
@@ -21582,14 +22867,22 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
21582
22867
|
return () => {
|
|
21583
22868
|
observer?.disconnect();
|
|
21584
22869
|
};
|
|
21585
|
-
}, [open]);
|
|
21586
|
-
const floatMode = viewportWidth <
|
|
22870
|
+
}, [open, docked]);
|
|
22871
|
+
const floatMode = forceFloat || (docked ? panelWidth > 0 && panelWidth < DOCK_TWO_COLUMN_MIN_PX : viewportWidth < 1024);
|
|
21587
22872
|
const toggleFileList = () => {
|
|
21588
22873
|
setFloatOpen((value) => !value);
|
|
21589
22874
|
};
|
|
22875
|
+
/** Flip the always-fold preference, remembered for the next open. */
|
|
22876
|
+
const toggleForceFloat = () => {
|
|
22877
|
+
const next = !forceFloat;
|
|
22878
|
+
setForceFloat(next);
|
|
22879
|
+
setFileListFloat(next);
|
|
22880
|
+
if (next) setFloatOpen(false);
|
|
22881
|
+
};
|
|
21590
22882
|
(0, react.useEffect)(() => {
|
|
21591
22883
|
const onResize = () => {
|
|
21592
22884
|
setViewportWidth(window.innerWidth);
|
|
22885
|
+
setViewportHeight(window.innerHeight);
|
|
21593
22886
|
};
|
|
21594
22887
|
window.addEventListener("resize", onResize);
|
|
21595
22888
|
return () => {
|
|
@@ -21610,23 +22903,50 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
21610
22903
|
el.removeEventListener("pointerdown", onPointerDown, true);
|
|
21611
22904
|
};
|
|
21612
22905
|
}, [floatMode, floatOpen]);
|
|
21613
|
-
|
|
21614
|
-
if (!floatMode
|
|
22906
|
+
const measureFloatBox = () => {
|
|
22907
|
+
if (!floatMode) return;
|
|
22908
|
+
const panel = panelRef.current;
|
|
21615
22909
|
const split = splitRef.current;
|
|
21616
|
-
|
|
21617
|
-
|
|
22910
|
+
if (panel === null || split === null) return;
|
|
22911
|
+
const body = panel.querySelector("[data-diff-body],[data-diff-md-preview-body]") ?? panel.querySelector("[data-diff-detail]");
|
|
22912
|
+
if (body === null) return;
|
|
21618
22913
|
const s = split.getBoundingClientRect();
|
|
21619
22914
|
const b = body.getBoundingClientRect();
|
|
21620
|
-
setFloatBox({
|
|
22915
|
+
setFloatBox((current) => current !== null && current.left === b.left - s.left && current.top === b.top - s.top && current.width === b.width && current.height === b.height ? current : {
|
|
21621
22916
|
left: b.left - s.left,
|
|
21622
22917
|
top: b.top - s.top,
|
|
21623
22918
|
width: b.width,
|
|
21624
22919
|
height: b.height
|
|
21625
22920
|
});
|
|
22921
|
+
};
|
|
22922
|
+
const measureFloatBoxRef = (0, react.useRef)(measureFloatBox);
|
|
22923
|
+
measureFloatBoxRef.current = measureFloatBox;
|
|
22924
|
+
(0, react.useLayoutEffect)(() => {
|
|
22925
|
+
measureFloatBoxRef.current();
|
|
22926
|
+
});
|
|
22927
|
+
(0, react.useEffect)(() => {
|
|
22928
|
+
if (!floatMode) return;
|
|
22929
|
+
const split = splitRef.current;
|
|
22930
|
+
if (split === null) return;
|
|
22931
|
+
const measure = () => {
|
|
22932
|
+
measureFloatBoxRef.current();
|
|
22933
|
+
};
|
|
22934
|
+
const resize = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(measure);
|
|
22935
|
+
resize?.observe(split);
|
|
22936
|
+
const mutations = typeof MutationObserver === "undefined" ? null : new MutationObserver(measure);
|
|
22937
|
+
mutations?.observe(split, {
|
|
22938
|
+
childList: true,
|
|
22939
|
+
subtree: true
|
|
22940
|
+
});
|
|
22941
|
+
return () => {
|
|
22942
|
+
resize?.disconnect();
|
|
22943
|
+
mutations?.disconnect();
|
|
22944
|
+
};
|
|
21626
22945
|
}, [
|
|
21627
22946
|
floatMode,
|
|
21628
22947
|
floatOpen,
|
|
21629
|
-
panelWidth
|
|
22948
|
+
panelWidth,
|
|
22949
|
+
selected
|
|
21630
22950
|
]);
|
|
21631
22951
|
(0, react.useEffect)(() => {
|
|
21632
22952
|
if (!open || !snapshot.redoCleared) return;
|
|
@@ -21641,6 +22961,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
21641
22961
|
if (!open) return;
|
|
21642
22962
|
const MEASURE_INTERVAL_MS = 400;
|
|
21643
22963
|
const measure = () => {
|
|
22964
|
+
setSideInset(frameInsets());
|
|
21644
22965
|
const scrollers = document.querySelectorAll(SCROLL_SELECTOR);
|
|
21645
22966
|
for (const scroller of scrollers) {
|
|
21646
22967
|
const seat = scroller.querySelector(SEAT_SELECTOR);
|
|
@@ -21666,20 +22987,6 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
21666
22987
|
window.removeEventListener("resize", measure);
|
|
21667
22988
|
};
|
|
21668
22989
|
}, [open]);
|
|
21669
|
-
(0, react.useEffect)(() => {
|
|
21670
|
-
if (!open) return;
|
|
21671
|
-
const onPointerDown = (event) => {
|
|
21672
|
-
const target = event.target;
|
|
21673
|
-
if (target === null) return;
|
|
21674
|
-
const element = target instanceof Element ? target : target.parentElement;
|
|
21675
|
-
if (element !== null && (element.closest("[data-diff-approval-panel]") !== null || element.closest("[data-diff-approval-badge]") !== null || element.closest(KEEP_OPEN_SELECTOR) !== null)) return;
|
|
21676
|
-
setOpen(false);
|
|
21677
|
-
};
|
|
21678
|
-
document.addEventListener("pointerdown", onPointerDown, true);
|
|
21679
|
-
return () => {
|
|
21680
|
-
document.removeEventListener("pointerdown", onPointerDown, true);
|
|
21681
|
-
};
|
|
21682
|
-
}, [open]);
|
|
21683
22990
|
const files = snapshot.files.filter((file) => current !== void 0 && (file.sessionIds ?? [file.sessionId]).includes(current)).sort((left, right) => compareFileNames(left.path, right.path));
|
|
21684
22991
|
const blockKeepWithPrompt = (sessionId, id, block, removeWhenResolved) => {
|
|
21685
22992
|
const file = files.find((entry) => entry.id === id);
|
|
@@ -21745,13 +23052,23 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
21745
23052
|
if (fresh.length > 0) setActionToast(fresh[0][1]);
|
|
21746
23053
|
failedRef.current = current;
|
|
21747
23054
|
}, [snapshot.failed]);
|
|
21748
|
-
(0, react.
|
|
21749
|
-
|
|
21750
|
-
|
|
21751
|
-
const
|
|
21752
|
-
|
|
23055
|
+
const wasShowingRef = (0, react.useRef)(false);
|
|
23056
|
+
(0, react.useLayoutEffect)(() => {
|
|
23057
|
+
const showing = open || docked;
|
|
23058
|
+
const started = showing && !wasShowingRef.current;
|
|
23059
|
+
wasShowingRef.current = showing;
|
|
23060
|
+
if (!showing) return;
|
|
23061
|
+
const pending = (id) => files.some((file) => file.id === id);
|
|
23062
|
+
const keep = selected !== "" && pending(selected) ? selected : void 0;
|
|
23063
|
+
const remembered = keep === void 0 && started ? lastPanelFile(current) : void 0;
|
|
23064
|
+
const resumed = remembered !== void 0 && pending(remembered) ? remembered : void 0;
|
|
23065
|
+
const pick = keep ?? resumed ?? files[0]?.id;
|
|
23066
|
+
if (pick === void 0) return;
|
|
23067
|
+
if (pick !== selected) setSelected(pick);
|
|
23068
|
+
if (started) landOn(pick, panelFileOffset(current, pick));
|
|
21753
23069
|
}, [
|
|
21754
23070
|
open,
|
|
23071
|
+
docked,
|
|
21755
23072
|
current,
|
|
21756
23073
|
files,
|
|
21757
23074
|
selected
|
|
@@ -21774,10 +23091,166 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
21774
23091
|
setImportBusy(false);
|
|
21775
23092
|
}
|
|
21776
23093
|
};
|
|
23094
|
+
/**
|
|
23095
|
+
* Show the panel the way the user last had it. `dock` hands off to the app's
|
|
23096
|
+
* right sidebar, where the panel lives in its own tab; `float` opens this
|
|
23097
|
+
* floating panel, covering whatever {@link panelCover} says.
|
|
23098
|
+
*
|
|
23099
|
+
* A remembered dock is only taken when the dock can actually show the panel.
|
|
23100
|
+
* "dock" is remembered from a page where the right sidebar existed, and the ask
|
|
23101
|
+
* itself is not proof: `onOpenDock` is always wired, and a sidebar whose
|
|
23102
|
+
* services never came up (or a controller that has since gone stale) accepts the
|
|
23103
|
+
* call and shows nothing. That used to swallow the click whole — the badge, the
|
|
23104
|
+
* header entry, the chord and the produced-file chips all did nothing, and the
|
|
23105
|
+
* only switch back to floating lives *inside* the panel, so the page had to be
|
|
23106
|
+
* reloaded to reach the review again. Now the overlay steps in whenever the tab
|
|
23107
|
+
* does not come up.
|
|
23108
|
+
*/
|
|
23109
|
+
const revealPanel = () => {
|
|
23110
|
+
if (panelPresentation() === "dock" && onOpenDock !== void 0 && dockAvailable) {
|
|
23111
|
+
try {
|
|
23112
|
+
onOpenDock();
|
|
23113
|
+
} catch {
|
|
23114
|
+
collapseSidebar();
|
|
23115
|
+
setOpen(true);
|
|
23116
|
+
return;
|
|
23117
|
+
}
|
|
23118
|
+
if (revealTimerRef.current !== void 0) window.clearTimeout(revealTimerRef.current);
|
|
23119
|
+
revealTimerRef.current = window.setTimeout(() => {
|
|
23120
|
+
revealTimerRef.current = void 0;
|
|
23121
|
+
if (dockShowingRef.current) return;
|
|
23122
|
+
collapseSidebar();
|
|
23123
|
+
setOpen(true);
|
|
23124
|
+
}, DOCK_REVEAL_GRACE_MS);
|
|
23125
|
+
return;
|
|
23126
|
+
}
|
|
23127
|
+
collapseSidebar();
|
|
23128
|
+
setOpen(true);
|
|
23129
|
+
};
|
|
23130
|
+
revealRef.current = revealPanel;
|
|
23131
|
+
const wasDockShowingRef = (0, react.useRef)(false);
|
|
23132
|
+
(0, react.useEffect)(() => {
|
|
23133
|
+
const was = wasDockShowingRef.current;
|
|
23134
|
+
wasDockShowingRef.current = dockShowing;
|
|
23135
|
+
if (docked) return;
|
|
23136
|
+
if (!was && dockShowing && open) setOpen(false);
|
|
23137
|
+
}, [
|
|
23138
|
+
docked,
|
|
23139
|
+
dockShowing,
|
|
23140
|
+
open
|
|
23141
|
+
]);
|
|
23142
|
+
/** Move the panel into the right sidebar's tab: the tab is opened and this
|
|
23143
|
+
* overlay steps aside, with the presentation remembered for the entry. The
|
|
23144
|
+
* sidebar's controller throws when it is mounted but cannot act on a session
|
|
23145
|
+
* yet (no seat bound), so a failure is said out loud rather than swallowed. */
|
|
23146
|
+
const dockPanel = () => {
|
|
23147
|
+
if (onOpenDock === void 0) return;
|
|
23148
|
+
try {
|
|
23149
|
+
onOpenDock();
|
|
23150
|
+
} catch (error) {
|
|
23151
|
+
showCopyToast(`${t("panel.dockFailed")} (${error instanceof Error ? error.message : String(error)})`);
|
|
23152
|
+
return;
|
|
23153
|
+
}
|
|
23154
|
+
setPanelPresentation("dock");
|
|
23155
|
+
rememberView();
|
|
23156
|
+
setOpen(false);
|
|
23157
|
+
};
|
|
23158
|
+
/** Where the panel is showing right now: the mode switch's checked row. */
|
|
23159
|
+
const presentation = docked ? "dock" : "float";
|
|
23160
|
+
/**
|
|
23161
|
+
* Move the panel to the chosen presentation. Leaving the dock is not this
|
|
23162
|
+
* component's to do: a docked panel draws no header (its tab chip carries the
|
|
23163
|
+
* switch), and the chip closes the tab and hands the panel back to the footer
|
|
23164
|
+
* entry itself.
|
|
23165
|
+
*/
|
|
23166
|
+
const choosePresentation = (next) => {
|
|
23167
|
+
if (next === "dock") {
|
|
23168
|
+
if (docked) return;
|
|
23169
|
+
if (!dockAvailable) {
|
|
23170
|
+
showCopyToast(`${t("panel.dockUnavailable")}${dockReason === void 0 ? "" : ` (${dockReason})`}`);
|
|
23171
|
+
return;
|
|
23172
|
+
}
|
|
23173
|
+
dockPanel();
|
|
23174
|
+
return;
|
|
23175
|
+
}
|
|
23176
|
+
if (docked) return;
|
|
23177
|
+
setPanelPresentation("float");
|
|
23178
|
+
};
|
|
23179
|
+
/** Flip one coverage switch, remembered for the next open. */
|
|
23180
|
+
const toggleCover = (key) => {
|
|
23181
|
+
const next = {
|
|
23182
|
+
...cover,
|
|
23183
|
+
[key]: !cover[key]
|
|
23184
|
+
};
|
|
23185
|
+
setCover(next);
|
|
23186
|
+
setPanelCover(next);
|
|
23187
|
+
};
|
|
23188
|
+
(0, react.useEffect)(() => {
|
|
23189
|
+
if (coverNotice === null) return;
|
|
23190
|
+
const timer = window.setTimeout(() => {
|
|
23191
|
+
setCoverNotice(null);
|
|
23192
|
+
}, COVER_NOTICE_MS);
|
|
23193
|
+
return () => {
|
|
23194
|
+
window.clearTimeout(timer);
|
|
23195
|
+
};
|
|
23196
|
+
}, [coverNotice]);
|
|
23197
|
+
(0, react.useEffect)(() => {
|
|
23198
|
+
const onCover = () => {
|
|
23199
|
+
setCover(panelCover());
|
|
23200
|
+
};
|
|
23201
|
+
window.addEventListener(COVER_CHANGED_EVENT, onCover);
|
|
23202
|
+
return () => {
|
|
23203
|
+
window.removeEventListener(COVER_CHANGED_EVENT, onCover);
|
|
23204
|
+
};
|
|
23205
|
+
}, []);
|
|
23206
|
+
/**
|
|
23207
|
+
* Remember what this panel is showing, so the next open resumes it: the file
|
|
23208
|
+
* that is open and how far down it the code view is. Called on every way out —
|
|
23209
|
+
* the overlay's close, the hand-off to the dock, and the docked tab unmounting.
|
|
23210
|
+
* With no code view (the Markdown preview is showing) the file's offset is kept
|
|
23211
|
+
* as it was: the file is what the reader comes back to.
|
|
23212
|
+
*/
|
|
23213
|
+
const rememberView = () => {
|
|
23214
|
+
const id = selectedRef.current;
|
|
23215
|
+
if (id === "" || current === void 0) return;
|
|
23216
|
+
const body = panelRef.current?.querySelector("[data-diff-body]");
|
|
23217
|
+
const previous = panelFileOffset(current, id);
|
|
23218
|
+
rememberPanelView(current, {
|
|
23219
|
+
fileId: id,
|
|
23220
|
+
scrollTop: body?.scrollTop ?? previous ?? 0
|
|
23221
|
+
});
|
|
23222
|
+
};
|
|
23223
|
+
/** The latest `rememberView`, for the docked instance's unmount cleanup: that
|
|
23224
|
+
* closer runs outside this render's closure. */
|
|
23225
|
+
const rememberViewRef = (0, react.useRef)(rememberView);
|
|
23226
|
+
rememberViewRef.current = rememberView;
|
|
23227
|
+
(0, react.useLayoutEffect)(() => {
|
|
23228
|
+
if (!docked) return;
|
|
23229
|
+
return () => {
|
|
23230
|
+
rememberViewRef.current();
|
|
23231
|
+
};
|
|
23232
|
+
}, [docked]);
|
|
23233
|
+
/**
|
|
23234
|
+
* Close the floating panel and hand the caret back to the chat composer:
|
|
23235
|
+
* closing the review is a "done reviewing, back to typing" move. A close the
|
|
23236
|
+
* user made by clicking somewhere else is the exception — that press is an
|
|
23237
|
+
* instruction to put the caret where they clicked, so the outside-click path
|
|
23238
|
+
* closes without touching focus.
|
|
23239
|
+
*/
|
|
23240
|
+
const closePanel = () => {
|
|
23241
|
+
rememberView();
|
|
23242
|
+
setOpen(false);
|
|
23243
|
+
focusComposer();
|
|
23244
|
+
};
|
|
21777
23245
|
const toggleOpen = () => {
|
|
21778
|
-
if (!open)
|
|
21779
|
-
|
|
23246
|
+
if (!open) revealPanel();
|
|
23247
|
+
else closePanel();
|
|
21780
23248
|
};
|
|
23249
|
+
toggleRef.current = toggleOpen;
|
|
23250
|
+
(0, react.useEffect)(() => {
|
|
23251
|
+
if (docked) return;
|
|
23252
|
+
window.dispatchEvent(new CustomEvent(PANEL_STATE_EVENT, { detail: { open } }));
|
|
23253
|
+
}, [docked, open]);
|
|
21781
23254
|
(0, react.useEffect)(() => {
|
|
21782
23255
|
if (noSession) setOpen(false);
|
|
21783
23256
|
}, [noSession]);
|
|
@@ -21839,7 +23312,10 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
21839
23312
|
t,
|
|
21840
23313
|
onSelect: (id) => {
|
|
21841
23314
|
if (id === selected) setJumpSignal((signal) => signal + 1);
|
|
21842
|
-
else
|
|
23315
|
+
else {
|
|
23316
|
+
setSelected(id);
|
|
23317
|
+
landOn(id);
|
|
23318
|
+
}
|
|
21843
23319
|
}
|
|
21844
23320
|
}, entry.id);
|
|
21845
23321
|
const selectedFile = files.find((file) => file.id === selected);
|
|
@@ -21870,6 +23346,21 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
21870
23346
|
},
|
|
21871
23347
|
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutline16, { size: 12 })
|
|
21872
23348
|
})
|
|
23349
|
+
}),
|
|
23350
|
+
(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
23351
|
+
label: t(forceFloat ? "action.fileListFloatOff" : "action.fileListFloatOn"),
|
|
23352
|
+
side: "bottom",
|
|
23353
|
+
delayMs: 500,
|
|
23354
|
+
children: (0, react_jsx_runtime.jsx)("button", {
|
|
23355
|
+
type: "button",
|
|
23356
|
+
className: `${PendingPanel_module_css_default.action} ${PendingPanel_module_css_default.addButton}`,
|
|
23357
|
+
"data-diff-file-list-float": true,
|
|
23358
|
+
"data-active": forceFloat ? "" : void 0,
|
|
23359
|
+
"aria-label": t(forceFloat ? "action.fileListFloatOff" : "action.fileListFloatOn"),
|
|
23360
|
+
"aria-pressed": forceFloat,
|
|
23361
|
+
onClick: toggleForceFloat,
|
|
23362
|
+
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPanelLeftOutline16, { size: 12 })
|
|
23363
|
+
})
|
|
21873
23364
|
})
|
|
21874
23365
|
]
|
|
21875
23366
|
}),
|
|
@@ -21908,13 +23399,19 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
21908
23399
|
const id = await onUndo(sessionId);
|
|
21909
23400
|
if (id === void 0) return;
|
|
21910
23401
|
if (id === selected) setUndoFlash((signal) => signal + 1);
|
|
21911
|
-
else
|
|
23402
|
+
else {
|
|
23403
|
+
setSelected(id);
|
|
23404
|
+
landOn(id);
|
|
23405
|
+
}
|
|
21912
23406
|
};
|
|
21913
23407
|
const handleRedo = async (sessionId) => {
|
|
21914
23408
|
const id = await onRedo(sessionId);
|
|
21915
23409
|
if (id === void 0) return;
|
|
21916
23410
|
if (id === selected) setUndoFlash((signal) => signal + 1);
|
|
21917
|
-
else
|
|
23411
|
+
else {
|
|
23412
|
+
setSelected(id);
|
|
23413
|
+
landOn(id);
|
|
23414
|
+
}
|
|
21918
23415
|
};
|
|
21919
23416
|
(0, react.useEffect)(() => {
|
|
21920
23417
|
if (!open || current === void 0) return;
|
|
@@ -21943,6 +23440,31 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
21943
23440
|
handleUndo,
|
|
21944
23441
|
handleRedo
|
|
21945
23442
|
]);
|
|
23443
|
+
(0, react.useEffect)(() => {
|
|
23444
|
+
if (!open || docked) return;
|
|
23445
|
+
const onKeyDown = (event) => {
|
|
23446
|
+
const action = COVER_ACTIONS.find(([name]) => matchesShortcut(event, keybindingOf(name)));
|
|
23447
|
+
if (action === void 0) return;
|
|
23448
|
+
if (pathPickerOpen()) return;
|
|
23449
|
+
if (isTextFieldEvent(event) && !isComposerEvent(event)) return;
|
|
23450
|
+
event.preventDefault();
|
|
23451
|
+
const edge = action[1];
|
|
23452
|
+
toggleCover(edge);
|
|
23453
|
+
setCoverNotice({
|
|
23454
|
+
edge,
|
|
23455
|
+
n: (coverNotice?.n ?? 0) + 1
|
|
23456
|
+
});
|
|
23457
|
+
};
|
|
23458
|
+
window.addEventListener("keydown", onKeyDown, true);
|
|
23459
|
+
return () => {
|
|
23460
|
+
window.removeEventListener("keydown", onKeyDown, true);
|
|
23461
|
+
};
|
|
23462
|
+
}, [
|
|
23463
|
+
open,
|
|
23464
|
+
docked,
|
|
23465
|
+
cover,
|
|
23466
|
+
coverNotice
|
|
23467
|
+
]);
|
|
21946
23468
|
(0, react.useEffect)(() => {
|
|
21947
23469
|
if (!open || current === void 0) return;
|
|
21948
23470
|
const onKeyDown = (event) => {
|
|
@@ -21957,7 +23479,10 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
21957
23479
|
event.preventDefault();
|
|
21958
23480
|
const index = files.findIndex((file) => file.id === selected);
|
|
21959
23481
|
const next = files[(index + direction + files.length) % files.length];
|
|
21960
|
-
if (next !== void 0)
|
|
23482
|
+
if (next !== void 0) {
|
|
23483
|
+
setSelected(next.id);
|
|
23484
|
+
landOn(next.id);
|
|
23485
|
+
}
|
|
21961
23486
|
};
|
|
21962
23487
|
window.addEventListener("keydown", onKeyDown, true);
|
|
21963
23488
|
return () => {
|
|
@@ -21970,37 +23495,51 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
21970
23495
|
selected
|
|
21971
23496
|
]);
|
|
21972
23497
|
(0, react.useEffect)(() => {
|
|
23498
|
+
if (docked) return;
|
|
21973
23499
|
const onKeyDown = (event) => {
|
|
21974
23500
|
if (!matchesShortcut(event, quickSummonKey())) return;
|
|
21975
23501
|
if (pathPickerOpen()) return;
|
|
21976
23502
|
event.preventDefault();
|
|
21977
|
-
if (
|
|
21978
|
-
|
|
23503
|
+
if (dockShowing) {
|
|
23504
|
+
closeDock?.();
|
|
23505
|
+
focusComposer();
|
|
23506
|
+
return;
|
|
23507
|
+
}
|
|
23508
|
+
if (open) closePanel();
|
|
23509
|
+
else revealPanel();
|
|
21979
23510
|
};
|
|
21980
23511
|
window.addEventListener("keydown", onKeyDown, true);
|
|
21981
23512
|
return () => {
|
|
21982
23513
|
window.removeEventListener("keydown", onKeyDown, true);
|
|
21983
23514
|
};
|
|
21984
|
-
}, [
|
|
23515
|
+
}, [
|
|
23516
|
+
docked,
|
|
23517
|
+
open,
|
|
23518
|
+
dockShowing,
|
|
23519
|
+
closeDock,
|
|
23520
|
+
collapseSidebar
|
|
23521
|
+
]);
|
|
21985
23522
|
(0, react.useEffect)(() => {
|
|
21986
|
-
if (!open) return;
|
|
23523
|
+
if (!open || docked) return;
|
|
21987
23524
|
const onKeyDown = (event) => {
|
|
21988
23525
|
if (event.key !== "Escape") return;
|
|
21989
23526
|
if (pathPickerOpen()) return;
|
|
23527
|
+
if (document.querySelector("[data-diff-approval-cover-popover]") !== null) return;
|
|
21990
23528
|
const target = event.target;
|
|
21991
23529
|
if (target instanceof Node && panelRef.current?.contains(target) === true && document.querySelector("[data-diff-searchbar]") !== null) return;
|
|
21992
23530
|
event.preventDefault();
|
|
21993
|
-
|
|
23531
|
+
closePanel();
|
|
21994
23532
|
};
|
|
21995
23533
|
window.addEventListener("keydown", onKeyDown, true);
|
|
21996
23534
|
return () => {
|
|
21997
23535
|
window.removeEventListener("keydown", onKeyDown, true);
|
|
21998
23536
|
};
|
|
21999
|
-
}, [open]);
|
|
23537
|
+
}, [open, docked]);
|
|
22000
23538
|
/** Drag the list/detail divider; width follows the pointer within its bounds. */
|
|
22001
23539
|
const startResize = (event) => {
|
|
22002
23540
|
if (event.button !== 0) return;
|
|
22003
23541
|
event.preventDefault();
|
|
23542
|
+
const cap = floatMode && floatBox !== null ? Math.max(MIN_LIST_WIDTH_PX, floatBox.width - 24) : MAX_LIST_WIDTH_PX;
|
|
22004
23543
|
resizeDrag.current = {
|
|
22005
23544
|
startX: event.clientX,
|
|
22006
23545
|
startWidth: listWidth
|
|
@@ -22011,7 +23550,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
22011
23550
|
const start = resizeDrag.current;
|
|
22012
23551
|
if (start === null) return;
|
|
22013
23552
|
const next = start.startWidth + (move.clientX - start.startX);
|
|
22014
|
-
setListWidth(Math.min(Math.max(next, MIN_LIST_WIDTH_PX),
|
|
23553
|
+
setListWidth(Math.min(Math.max(next, MIN_LIST_WIDTH_PX), cap));
|
|
22015
23554
|
};
|
|
22016
23555
|
const onUp = () => {
|
|
22017
23556
|
resizeDrag.current = null;
|
|
@@ -22023,293 +23562,318 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
22023
23562
|
window.addEventListener("mousemove", onMove);
|
|
22024
23563
|
window.addEventListener("mouseup", onUp);
|
|
22025
23564
|
};
|
|
22026
|
-
|
|
22027
|
-
|
|
22028
|
-
|
|
22029
|
-
|
|
22030
|
-
|
|
22031
|
-
|
|
22032
|
-
|
|
22033
|
-
|
|
22034
|
-
|
|
22035
|
-
|
|
22036
|
-
|
|
22037
|
-
|
|
22038
|
-
|
|
22039
|
-
|
|
23565
|
+
const content = (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
23566
|
+
importToast !== null && (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Toast, {
|
|
23567
|
+
text: importToast,
|
|
23568
|
+
onDone: () => {
|
|
23569
|
+
setImportToast(null);
|
|
23570
|
+
}
|
|
23571
|
+
}),
|
|
23572
|
+
actionToast !== null && (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Toast, {
|
|
23573
|
+
text: actionToast,
|
|
23574
|
+
onDone: () => {
|
|
23575
|
+
setActionToast(null);
|
|
23576
|
+
}
|
|
23577
|
+
}),
|
|
23578
|
+
copyToast !== null && (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Toast, {
|
|
23579
|
+
text: copyToast.text,
|
|
23580
|
+
onDone: () => {
|
|
23581
|
+
setCopyToast(null);
|
|
23582
|
+
}
|
|
23583
|
+
}, copyToast.n),
|
|
23584
|
+
coverNotice !== null && (0, react_jsx_runtime.jsx)(CoverageNotice, {
|
|
23585
|
+
t,
|
|
23586
|
+
cover,
|
|
23587
|
+
changed: coverNotice.edge
|
|
23588
|
+
}, coverNotice.n),
|
|
23589
|
+
(docked || open) && (0, react_dom.createPortal)((0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [!docked && cover.top && cover.left && cover.right && cover.composer && (0, react_jsx_runtime.jsx)("div", {
|
|
23590
|
+
className: PendingPanel_module_css_default.coverBackdrop,
|
|
23591
|
+
"data-diff-cover-backdrop": true
|
|
23592
|
+
}), (0, react_jsx_runtime.jsxs)("section", {
|
|
23593
|
+
className: docked ? `${PendingPanel_module_css_default.panel} ${PendingPanel_module_css_default.panelDocked}` : PendingPanel_module_css_default.panel,
|
|
23594
|
+
ref: panelRef,
|
|
23595
|
+
style: docked ? void 0 : panelInsets(cover, sideInset, bottomPx, {
|
|
23596
|
+
width: viewportWidth,
|
|
23597
|
+
height: viewportHeight
|
|
22040
23598
|
}),
|
|
22041
|
-
|
|
22042
|
-
|
|
22043
|
-
|
|
22044
|
-
|
|
22045
|
-
|
|
22046
|
-
|
|
22047
|
-
|
|
22048
|
-
|
|
22049
|
-
|
|
22050
|
-
|
|
22051
|
-
|
|
22052
|
-
|
|
22053
|
-
|
|
22054
|
-
|
|
22055
|
-
|
|
22056
|
-
|
|
22057
|
-
|
|
22058
|
-
|
|
22059
|
-
|
|
22060
|
-
|
|
22061
|
-
|
|
22062
|
-
|
|
22063
|
-
|
|
23599
|
+
"data-diff-approval-panel": true,
|
|
23600
|
+
"data-diff-docked": docked ? "" : void 0,
|
|
23601
|
+
"aria-label": t("panel.title"),
|
|
23602
|
+
children: [
|
|
23603
|
+
!docked && (0, react_jsx_runtime.jsxs)("header", {
|
|
23604
|
+
className: PendingPanel_module_css_default.header,
|
|
23605
|
+
children: [(0, react_jsx_runtime.jsx)("span", {
|
|
23606
|
+
className: PendingPanel_module_css_default.title,
|
|
23607
|
+
children: t("panel.title")
|
|
23608
|
+
}), (0, react_jsx_runtime.jsxs)("div", {
|
|
23609
|
+
className: PendingPanel_module_css_default.headerActions,
|
|
23610
|
+
children: [
|
|
23611
|
+
(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
23612
|
+
label: t("action.settings"),
|
|
23613
|
+
side: "bottom",
|
|
23614
|
+
delayMs: 500,
|
|
23615
|
+
children: (0, react_jsx_runtime.jsx)("button", {
|
|
23616
|
+
type: "button",
|
|
23617
|
+
className: PendingPanel_module_css_default.expand,
|
|
23618
|
+
"data-diff-approval-settings": true,
|
|
23619
|
+
"aria-label": t("action.settings"),
|
|
23620
|
+
onClick: () => {
|
|
23621
|
+
if (!docked) setOpen(false);
|
|
23622
|
+
openSettingsSection(t("settings.tabLabel"));
|
|
23623
|
+
},
|
|
23624
|
+
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconSettingsOutline16, { size: 14 })
|
|
23625
|
+
})
|
|
23626
|
+
}),
|
|
23627
|
+
!docked && (0, react_jsx_runtime.jsx)(CoverageControl, {
|
|
23628
|
+
t,
|
|
23629
|
+
cover,
|
|
23630
|
+
onToggle: toggleCover
|
|
23631
|
+
}),
|
|
23632
|
+
(0, react_jsx_runtime.jsx)(PresentationMenu, {
|
|
23633
|
+
t,
|
|
23634
|
+
current: presentation,
|
|
23635
|
+
onChoose: choosePresentation
|
|
23636
|
+
}),
|
|
23637
|
+
(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
23638
|
+
label: closeHint(t),
|
|
23639
|
+
side: "bottom",
|
|
23640
|
+
delayMs: 500,
|
|
23641
|
+
children: (0, react_jsx_runtime.jsx)("button", {
|
|
23642
|
+
type: "button",
|
|
23643
|
+
className: PendingPanel_module_css_default.close,
|
|
23644
|
+
"data-diff-approval-close": true,
|
|
23645
|
+
"aria-label": t("action.close"),
|
|
23646
|
+
onClick: closePanel,
|
|
23647
|
+
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCloseOutline16, { size: 14 })
|
|
23648
|
+
})
|
|
23649
|
+
})
|
|
23650
|
+
]
|
|
23651
|
+
})]
|
|
23652
|
+
}),
|
|
23653
|
+
snapshot.error !== void 0 || !snapshot.read || files.length === 0 ? (0, react_jsx_runtime.jsxs)("div", {
|
|
23654
|
+
className: PendingPanel_module_css_default.states,
|
|
23655
|
+
children: [
|
|
23656
|
+
snapshot.error !== void 0 && (0, react_jsx_runtime.jsx)("p", {
|
|
23657
|
+
className: PendingPanel_module_css_default.readError,
|
|
23658
|
+
role: "alert",
|
|
23659
|
+
children: t("panel.readFailed", { message: snapshot.error })
|
|
23660
|
+
}),
|
|
23661
|
+
!snapshot.read && snapshot.error === void 0 && (0, react_jsx_runtime.jsx)("p", {
|
|
23662
|
+
className: PendingPanel_module_css_default.note,
|
|
23663
|
+
children: t("panel.loading")
|
|
23664
|
+
}),
|
|
23665
|
+
snapshot.read && snapshot.error === void 0 && files.length === 0 && (0, react_jsx_runtime.jsxs)("div", {
|
|
23666
|
+
className: PendingPanel_module_css_default.emptyState,
|
|
22064
23667
|
children: [
|
|
22065
|
-
(0, react_jsx_runtime.jsx)(
|
|
22066
|
-
|
|
22067
|
-
|
|
22068
|
-
delayMs: 500,
|
|
22069
|
-
children: (0, react_jsx_runtime.jsx)("button", {
|
|
22070
|
-
type: "button",
|
|
22071
|
-
className: PendingPanel_module_css_default.expand,
|
|
22072
|
-
"data-diff-approval-settings": true,
|
|
22073
|
-
"aria-label": t("action.settings"),
|
|
22074
|
-
onClick: () => {
|
|
22075
|
-
setOpen(false);
|
|
22076
|
-
openSettingsSection(t("settings.tabLabel"));
|
|
22077
|
-
},
|
|
22078
|
-
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconSettingsOutline16, { size: 14 })
|
|
22079
|
-
})
|
|
23668
|
+
(0, react_jsx_runtime.jsx)("p", {
|
|
23669
|
+
className: `${PendingPanel_module_css_default.note} ${PendingPanel_module_css_default.noteCentered}`,
|
|
23670
|
+
children: t("panel.empty")
|
|
22080
23671
|
}),
|
|
22081
|
-
(0, react_jsx_runtime.
|
|
22082
|
-
|
|
22083
|
-
|
|
22084
|
-
delayMs: 500,
|
|
22085
|
-
children: (0, react_jsx_runtime.jsx)("button", {
|
|
23672
|
+
(0, react_jsx_runtime.jsxs)("div", {
|
|
23673
|
+
className: PendingPanel_module_css_default.emptyActions,
|
|
23674
|
+
children: [(0, react_jsx_runtime.jsx)("button", {
|
|
22086
23675
|
type: "button",
|
|
22087
|
-
className:
|
|
22088
|
-
"data-diff-
|
|
22089
|
-
|
|
23676
|
+
className: PendingPanel_module_css_default.importButton,
|
|
23677
|
+
"data-diff-import-vcs": true,
|
|
23678
|
+
disabled: importBusy,
|
|
22090
23679
|
onClick: () => {
|
|
22091
|
-
|
|
22092
|
-
setExpanded((value) => !value);
|
|
23680
|
+
runImportVcs();
|
|
22093
23681
|
},
|
|
22094
|
-
children: (
|
|
22095
|
-
})
|
|
22096
|
-
}),
|
|
22097
|
-
(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
22098
|
-
label: t("action.close"),
|
|
22099
|
-
side: "bottom",
|
|
22100
|
-
delayMs: 500,
|
|
22101
|
-
children: (0, react_jsx_runtime.jsx)("button", {
|
|
23682
|
+
children: importBusy ? t("action.importVcsBusy") : t("action.importVcs")
|
|
23683
|
+
}), (0, react_jsx_runtime.jsx)("button", {
|
|
22102
23684
|
type: "button",
|
|
22103
|
-
className: PendingPanel_module_css_default.
|
|
22104
|
-
"data-diff-
|
|
22105
|
-
"aria-label": t("action.close"),
|
|
23685
|
+
className: PendingPanel_module_css_default.importButton,
|
|
23686
|
+
"data-diff-add": true,
|
|
22106
23687
|
onClick: () => {
|
|
22107
|
-
|
|
23688
|
+
setAddOpen(true);
|
|
22108
23689
|
},
|
|
22109
|
-
children: (
|
|
22110
|
-
})
|
|
23690
|
+
children: t("action.addPath")
|
|
23691
|
+
})]
|
|
23692
|
+
}),
|
|
23693
|
+
importNote !== void 0 && (0, react_jsx_runtime.jsx)("p", {
|
|
23694
|
+
className: PendingPanel_module_css_default.importNote,
|
|
23695
|
+
role: importFailed ? "alert" : void 0,
|
|
23696
|
+
children: importNote
|
|
22111
23697
|
})
|
|
22112
23698
|
]
|
|
22113
|
-
})
|
|
22114
|
-
|
|
22115
|
-
|
|
22116
|
-
|
|
22117
|
-
|
|
22118
|
-
|
|
22119
|
-
|
|
22120
|
-
|
|
22121
|
-
|
|
22122
|
-
|
|
22123
|
-
|
|
22124
|
-
|
|
22125
|
-
|
|
22126
|
-
}),
|
|
22127
|
-
snapshot.read && snapshot.error === void 0 && files.length === 0 && (0, react_jsx_runtime.jsxs)("div", {
|
|
22128
|
-
className: PendingPanel_module_css_default.emptyState,
|
|
22129
|
-
children: [
|
|
22130
|
-
(0, react_jsx_runtime.jsx)("p", {
|
|
22131
|
-
className: `${PendingPanel_module_css_default.note} ${PendingPanel_module_css_default.noteCentered}`,
|
|
22132
|
-
children: t("panel.empty")
|
|
22133
|
-
}),
|
|
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
|
-
})]
|
|
22154
|
-
}),
|
|
22155
|
-
importNote !== void 0 && (0, react_jsx_runtime.jsx)("p", {
|
|
22156
|
-
className: PendingPanel_module_css_default.importNote,
|
|
22157
|
-
role: importFailed ? "alert" : void 0,
|
|
22158
|
-
children: importNote
|
|
22159
|
-
})
|
|
22160
|
-
]
|
|
22161
|
-
})
|
|
22162
|
-
]
|
|
22163
|
-
}) : (0, react_jsx_runtime.jsxs)("div", {
|
|
22164
|
-
className: PendingPanel_module_css_default.split,
|
|
22165
|
-
ref: splitRef,
|
|
22166
|
-
children: [
|
|
22167
|
-
!floatMode && (0, react_jsx_runtime.jsx)("nav", {
|
|
22168
|
-
className: PendingPanel_module_css_default.fileList,
|
|
22169
|
-
style: { width: listWidth },
|
|
22170
|
-
"data-diff-approval-file-list": true,
|
|
22171
|
-
children: fileListBody
|
|
22172
|
-
}),
|
|
22173
|
-
!floatMode && (0, react_jsx_runtime.jsx)("div", {
|
|
22174
|
-
className: PendingPanel_module_css_default.resizeHandle,
|
|
22175
|
-
"data-diff-resize": true,
|
|
22176
|
-
onMouseDown: startResize
|
|
22177
|
-
}),
|
|
22178
|
-
(0, react_jsx_runtime.jsx)("div", {
|
|
22179
|
-
className: PendingPanel_module_css_default.detail,
|
|
22180
|
-
children: selectedFile === void 0 ? (0, react_jsx_runtime.jsx)("p", {
|
|
22181
|
-
className: PendingPanel_module_css_default.detailEmpty,
|
|
22182
|
-
children: t("panel.selectHint")
|
|
22183
|
-
}) : (0, react_jsx_runtime.jsx)(PendingDiff, {
|
|
22184
|
-
file: selectedFile,
|
|
22185
|
-
busy: snapshot.busy.has(selectedFile.id),
|
|
22186
|
-
workspacePath: snapshot.workspacePath,
|
|
22187
|
-
jumpSignal,
|
|
22188
|
-
undoFlash,
|
|
22189
|
-
failedMessage: failed.get(selectedFile.id),
|
|
22190
|
-
onPasteReference,
|
|
22191
|
-
onToast: showCopyToast,
|
|
22192
|
-
t,
|
|
22193
|
-
onKeep: keepWithPrompt,
|
|
22194
|
-
onRevert: revertWithPrompt,
|
|
22195
|
-
onRefreshVcs: (entry) => {
|
|
22196
|
-
runRefreshVcs(entry);
|
|
22197
|
-
},
|
|
22198
|
-
onBlockKeep: blockKeepWithPrompt,
|
|
22199
|
-
onBlockRevert: blockRevertWithPrompt,
|
|
22200
|
-
onOpen,
|
|
22201
|
-
onPreviewImage,
|
|
22202
|
-
floatMode,
|
|
22203
|
-
floatOpen,
|
|
22204
|
-
onToggleFileList: toggleFileList
|
|
22205
|
-
})
|
|
22206
|
-
}),
|
|
22207
|
-
floatMode && floatOpen && files.length > 0 && floatBox !== null && (0, react_jsx_runtime.jsx)("div", {
|
|
22208
|
-
className: PendingPanel_module_css_default.fileListFloat,
|
|
23699
|
+
})
|
|
23700
|
+
]
|
|
23701
|
+
}) : (0, react_jsx_runtime.jsxs)("div", {
|
|
23702
|
+
className: PendingPanel_module_css_default.split,
|
|
23703
|
+
ref: splitRef,
|
|
23704
|
+
children: [
|
|
23705
|
+
floatMode && (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
23706
|
+
label: t(floatOpen ? "action.hideFileList" : "action.showFileList"),
|
|
23707
|
+
side: "bottom",
|
|
23708
|
+
delayMs: 500,
|
|
23709
|
+
children: (0, react_jsx_runtime.jsx)("button", {
|
|
23710
|
+
type: "button",
|
|
23711
|
+
className: PendingPanel_module_css_default.fileListKnob,
|
|
22209
23712
|
style: {
|
|
22210
|
-
left: floatBox
|
|
22211
|
-
top: floatBox
|
|
22212
|
-
width: Math.min(listWidth, Math.max(0, floatBox.width - 24)),
|
|
22213
|
-
height: Math.max(0, floatBox.height - 24)
|
|
23713
|
+
left: (floatBox?.left ?? 0) + FLOAT_LIST_MARGIN_PX,
|
|
23714
|
+
top: (floatBox?.top ?? 0) + FLOAT_LIST_MARGIN_PX
|
|
22214
23715
|
},
|
|
22215
|
-
"data-diff-
|
|
22216
|
-
|
|
23716
|
+
"data-diff-file-list-toggle": true,
|
|
23717
|
+
"aria-label": t(floatOpen ? "action.hideFileList" : "action.showFileList"),
|
|
23718
|
+
"aria-expanded": floatOpen,
|
|
23719
|
+
onClick: toggleFileList,
|
|
23720
|
+
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconListPenOutline16, { size: 14 })
|
|
22217
23721
|
})
|
|
22218
|
-
|
|
22219
|
-
|
|
22220
|
-
|
|
22221
|
-
|
|
22222
|
-
|
|
22223
|
-
|
|
22224
|
-
|
|
22225
|
-
|
|
22226
|
-
|
|
22227
|
-
|
|
22228
|
-
|
|
22229
|
-
|
|
22230
|
-
|
|
22231
|
-
|
|
22232
|
-
|
|
22233
|
-
|
|
22234
|
-
|
|
22235
|
-
|
|
22236
|
-
|
|
22237
|
-
|
|
22238
|
-
|
|
22239
|
-
|
|
22240
|
-
|
|
22241
|
-
|
|
22242
|
-
|
|
22243
|
-
|
|
22244
|
-
|
|
22245
|
-
|
|
22246
|
-
|
|
22247
|
-
|
|
22248
|
-
|
|
22249
|
-
|
|
22250
|
-
|
|
22251
|
-
|
|
22252
|
-
}
|
|
23722
|
+
}),
|
|
23723
|
+
!floatMode && (0, react_jsx_runtime.jsx)("nav", {
|
|
23724
|
+
className: PendingPanel_module_css_default.fileList,
|
|
23725
|
+
style: { width: listWidth },
|
|
23726
|
+
"data-diff-approval-file-list": true,
|
|
23727
|
+
children: fileListBody
|
|
23728
|
+
}),
|
|
23729
|
+
!floatMode && (0, react_jsx_runtime.jsx)("div", {
|
|
23730
|
+
className: PendingPanel_module_css_default.resizeHandle,
|
|
23731
|
+
"data-diff-resize": true,
|
|
23732
|
+
onMouseDown: startResize
|
|
23733
|
+
}),
|
|
23734
|
+
(0, react_jsx_runtime.jsx)("div", {
|
|
23735
|
+
className: PendingPanel_module_css_default.detail,
|
|
23736
|
+
"data-diff-detail": true,
|
|
23737
|
+
children: selectedFile === void 0 ? (0, react_jsx_runtime.jsx)("p", {
|
|
23738
|
+
className: PendingPanel_module_css_default.detailEmpty,
|
|
23739
|
+
children: t("panel.selectHint")
|
|
23740
|
+
}) : (0, react_jsx_runtime.jsx)(PendingDiff, {
|
|
23741
|
+
file: selectedFile,
|
|
23742
|
+
busy: snapshot.busy.has(selectedFile.id),
|
|
23743
|
+
workspacePath: snapshot.workspacePath,
|
|
23744
|
+
jumpSignal,
|
|
23745
|
+
undoFlash,
|
|
23746
|
+
landingTop: landing !== void 0 && landing.fileId === selectedFile.id ? landing.top : void 0,
|
|
23747
|
+
landingTick: landing !== void 0 && landing.fileId === selectedFile.id ? landing.n : 0,
|
|
23748
|
+
failedMessage: failed.get(selectedFile.id),
|
|
23749
|
+
onPasteReference,
|
|
23750
|
+
onToast: showCopyToast,
|
|
23751
|
+
t,
|
|
23752
|
+
onKeep: keepWithPrompt,
|
|
23753
|
+
onRevert: revertWithPrompt,
|
|
23754
|
+
onRefreshVcs: (entry) => {
|
|
23755
|
+
runRefreshVcs(entry);
|
|
23756
|
+
},
|
|
23757
|
+
onBlockKeep: blockKeepWithPrompt,
|
|
23758
|
+
onBlockRevert: blockRevertWithPrompt,
|
|
23759
|
+
onOpen,
|
|
23760
|
+
onPreviewImage
|
|
23761
|
+
})
|
|
23762
|
+
}),
|
|
23763
|
+
floatMode && floatOpen && files.length > 0 && floatBox !== null && (0, react_jsx_runtime.jsxs)("div", {
|
|
23764
|
+
className: PendingPanel_module_css_default.fileListFloat,
|
|
23765
|
+
style: {
|
|
23766
|
+
left: floatBox.left + FLOAT_LIST_MARGIN_PX,
|
|
23767
|
+
top: floatBox.top + FLOAT_LIST_MARGIN_PX,
|
|
23768
|
+
width: Math.min(listWidth, Math.max(0, floatBox.width - 24)),
|
|
23769
|
+
height: Math.max(0, floatBox.height - 24)
|
|
23770
|
+
},
|
|
23771
|
+
"data-diff-floating-file-list": true,
|
|
23772
|
+
children: [fileListBody, (0, react_jsx_runtime.jsx)("div", {
|
|
23773
|
+
className: PendingPanel_module_css_default.floatResizeHandle,
|
|
23774
|
+
"data-diff-float-resize": true,
|
|
23775
|
+
onMouseDown: startResize
|
|
22253
23776
|
})]
|
|
22254
23777
|
})
|
|
22255
|
-
|
|
22256
|
-
|
|
22257
|
-
|
|
22258
|
-
|
|
22259
|
-
|
|
22260
|
-
|
|
22261
|
-
|
|
22262
|
-
|
|
22263
|
-
|
|
22264
|
-
|
|
22265
|
-
|
|
22266
|
-
|
|
22267
|
-
|
|
22268
|
-
|
|
22269
|
-
|
|
22270
|
-
|
|
22271
|
-
|
|
22272
|
-
|
|
22273
|
-
|
|
22274
|
-
|
|
22275
|
-
|
|
22276
|
-
|
|
22277
|
-
|
|
22278
|
-
|
|
22279
|
-
|
|
22280
|
-
|
|
22281
|
-
|
|
22282
|
-
|
|
22283
|
-
|
|
22284
|
-
|
|
22285
|
-
|
|
22286
|
-
|
|
22287
|
-
|
|
22288
|
-
|
|
23778
|
+
]
|
|
23779
|
+
}),
|
|
23780
|
+
blockPrompt !== null && promptFile !== void 0 && (0, react_jsx_runtime.jsx)("div", {
|
|
23781
|
+
className: PendingPanel_module_css_default.confirmBackdrop,
|
|
23782
|
+
"data-diff-confirm": true,
|
|
23783
|
+
children: (0, react_jsx_runtime.jsxs)("div", {
|
|
23784
|
+
className: PendingPanel_module_css_default.confirmCard,
|
|
23785
|
+
role: "dialog",
|
|
23786
|
+
"aria-modal": "true",
|
|
23787
|
+
children: [(0, react_jsx_runtime.jsx)("p", {
|
|
23788
|
+
className: PendingPanel_module_css_default.confirmText,
|
|
23789
|
+
children: t("panel.resolvedAsk", { file: basenameOf(promptFile.path) })
|
|
23790
|
+
}), (0, react_jsx_runtime.jsxs)("div", {
|
|
23791
|
+
className: PendingPanel_module_css_default.confirmActions,
|
|
23792
|
+
children: [(0, react_jsx_runtime.jsx)("button", {
|
|
23793
|
+
type: "button",
|
|
23794
|
+
className: `${PendingPanel_module_css_default.action} ${PendingPanel_module_css_default.actionPrimary}`,
|
|
23795
|
+
"data-diff-confirm-remove": true,
|
|
23796
|
+
onClick: () => {
|
|
23797
|
+
setBlockPrompt(null);
|
|
23798
|
+
const { action, sessionId, id, block } = blockPrompt;
|
|
23799
|
+
action === "keep" ? onBlockKeep(sessionId, id, block, true) : onBlockRevert(sessionId, id, block, true);
|
|
23800
|
+
},
|
|
23801
|
+
children: t("row.dismiss")
|
|
23802
|
+
}), (0, react_jsx_runtime.jsx)("button", {
|
|
23803
|
+
type: "button",
|
|
23804
|
+
className: PendingPanel_module_css_default.action,
|
|
23805
|
+
"data-diff-confirm-keep": true,
|
|
23806
|
+
onClick: () => {
|
|
23807
|
+
setBlockPrompt(null);
|
|
23808
|
+
const { action, sessionId, id, block } = blockPrompt;
|
|
23809
|
+
action === "keep" ? onBlockKeep(sessionId, id, block, false) : onBlockRevert(sessionId, id, block, false);
|
|
23810
|
+
},
|
|
23811
|
+
children: t("panel.keepInList")
|
|
22289
23812
|
})]
|
|
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
|
|
23813
|
+
})]
|
|
22301
23814
|
})
|
|
22302
|
-
|
|
22303
|
-
|
|
22304
|
-
|
|
22305
|
-
|
|
23815
|
+
}),
|
|
23816
|
+
filePrompt !== null && promptEntry !== void 0 && (0, react_jsx_runtime.jsx)("div", {
|
|
23817
|
+
className: PendingPanel_module_css_default.confirmBackdrop,
|
|
23818
|
+
"data-diff-confirm-file": true,
|
|
23819
|
+
children: (0, react_jsx_runtime.jsxs)("div", {
|
|
23820
|
+
className: PendingPanel_module_css_default.confirmCard,
|
|
23821
|
+
role: "dialog",
|
|
23822
|
+
"aria-modal": "true",
|
|
23823
|
+
children: [(0, react_jsx_runtime.jsx)("p", {
|
|
23824
|
+
className: PendingPanel_module_css_default.confirmText,
|
|
23825
|
+
children: t(filePrompt.action === "keep" ? "panel.fileKeptAsk" : "panel.fileRevertedAsk", { file: basenameOf(promptEntry.path) })
|
|
23826
|
+
}), (0, react_jsx_runtime.jsxs)("div", {
|
|
23827
|
+
className: PendingPanel_module_css_default.confirmActions,
|
|
23828
|
+
children: [(0, react_jsx_runtime.jsx)("button", {
|
|
23829
|
+
type: "button",
|
|
23830
|
+
className: `${PendingPanel_module_css_default.action} ${PendingPanel_module_css_default.actionPrimary}`,
|
|
23831
|
+
"data-diff-file-confirm-remove": true,
|
|
23832
|
+
onClick: () => {
|
|
23833
|
+
setFilePrompt(null);
|
|
23834
|
+
const { action, sessionId, id } = filePrompt;
|
|
23835
|
+
action === "keep" ? onKeep(sessionId, id, false) : onRevert(sessionId, id, false);
|
|
23836
|
+
},
|
|
23837
|
+
children: t("row.dismiss")
|
|
23838
|
+
}), (0, react_jsx_runtime.jsx)("button", {
|
|
23839
|
+
type: "button",
|
|
23840
|
+
className: PendingPanel_module_css_default.action,
|
|
23841
|
+
"data-diff-file-confirm-keep": true,
|
|
23842
|
+
onClick: () => {
|
|
23843
|
+
setFilePrompt(null);
|
|
23844
|
+
const { action, sessionId, id } = filePrompt;
|
|
23845
|
+
action === "keep" ? onKeep(sessionId, id, true) : onRevert(sessionId, id, true);
|
|
23846
|
+
},
|
|
23847
|
+
children: t("panel.keepInList")
|
|
23848
|
+
})]
|
|
23849
|
+
})]
|
|
23850
|
+
})
|
|
23851
|
+
}),
|
|
23852
|
+
addOpen && current !== void 0 && (0, react_jsx_runtime.jsx)(PathPicker, {
|
|
23853
|
+
rootPath: snapshot.workspacePath,
|
|
23854
|
+
onBrowse: (path) => onBrowse(current, path),
|
|
23855
|
+
onAdd: (path, includeUnchanged) => onAddPath(current, path, includeUnchanged),
|
|
23856
|
+
onToast: showCopyToast,
|
|
23857
|
+
onClose: () => {
|
|
23858
|
+
setAddOpen(false);
|
|
23859
|
+
},
|
|
23860
|
+
t
|
|
23861
|
+
})
|
|
23862
|
+
]
|
|
23863
|
+
})] }), docked && dockHost !== void 0 ? dockHost : document.body),
|
|
23864
|
+
!docked && (0, react_jsx_runtime.jsx)("div", {
|
|
23865
|
+
className: PendingPanel_module_css_default.footerButtons,
|
|
23866
|
+
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
23867
|
+
label: summonHint(t),
|
|
23868
|
+
side: "top",
|
|
23869
|
+
delayMs: 500,
|
|
22306
23870
|
children: (0, react_jsx_runtime.jsxs)("button", {
|
|
22307
23871
|
type: "button",
|
|
22308
23872
|
className: noSession ? `${PendingPanel_module_css_default.badge} ${PendingPanel_module_css_default.badgeDisabled}` : PendingPanel_module_css_default.badge,
|
|
22309
23873
|
"data-diff-approval-badge": files.length,
|
|
22310
|
-
"data-active": open ? "" : void 0,
|
|
23874
|
+
"data-active": open || dockShowing ? "" : void 0,
|
|
22311
23875
|
"aria-label": t("panel.aria"),
|
|
22312
|
-
"aria-expanded": open,
|
|
23876
|
+
"aria-expanded": open || dockShowing,
|
|
22313
23877
|
disabled: noSession,
|
|
22314
23878
|
onClick: toggleOpen,
|
|
22315
23879
|
children: [
|
|
@@ -22324,25 +23888,30 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
22324
23888
|
})
|
|
22325
23889
|
]
|
|
22326
23890
|
})
|
|
22327
|
-
}),
|
|
22328
|
-
redoClearedNotice && (0, react_jsx_runtime.jsxs)("div", {
|
|
22329
|
-
className: PendingPanel_module_css_default.notice,
|
|
22330
|
-
role: "status",
|
|
22331
|
-
"data-diff-approval-notice": true,
|
|
22332
|
-
children: [(0, react_jsx_runtime.jsx)("p", {
|
|
22333
|
-
className: PendingPanel_module_css_default.noticeText,
|
|
22334
|
-
children: t("panel.externalChanged")
|
|
22335
|
-
}), (0, react_jsx_runtime.jsx)("button", {
|
|
22336
|
-
type: "button",
|
|
22337
|
-
className: PendingPanel_module_css_default.noticeButton,
|
|
22338
|
-
"data-diff-notice-dismiss": true,
|
|
22339
|
-
onClick: () => {
|
|
22340
|
-
setRedoClearedNotice(false);
|
|
22341
|
-
},
|
|
22342
|
-
children: t("panel.dismiss")
|
|
22343
|
-
})]
|
|
22344
23891
|
})
|
|
22345
|
-
|
|
23892
|
+
}),
|
|
23893
|
+
redoClearedNotice && (0, react_jsx_runtime.jsxs)("div", {
|
|
23894
|
+
className: PendingPanel_module_css_default.notice,
|
|
23895
|
+
role: "status",
|
|
23896
|
+
"data-diff-approval-notice": true,
|
|
23897
|
+
children: [(0, react_jsx_runtime.jsx)("p", {
|
|
23898
|
+
className: PendingPanel_module_css_default.noticeText,
|
|
23899
|
+
children: t("panel.externalChanged")
|
|
23900
|
+
}), (0, react_jsx_runtime.jsx)("button", {
|
|
23901
|
+
type: "button",
|
|
23902
|
+
className: PendingPanel_module_css_default.noticeButton,
|
|
23903
|
+
"data-diff-notice-dismiss": true,
|
|
23904
|
+
onClick: () => {
|
|
23905
|
+
setRedoClearedNotice(false);
|
|
23906
|
+
},
|
|
23907
|
+
children: t("panel.dismiss")
|
|
23908
|
+
})]
|
|
23909
|
+
})
|
|
23910
|
+
] });
|
|
23911
|
+
return docked ? content : (0, react_jsx_runtime.jsx)("div", {
|
|
23912
|
+
className: wide ? PendingPanel_module_css_default.layer : `${PendingPanel_module_css_default.layer} ${PendingPanel_module_css_default.rail}`,
|
|
23913
|
+
"data-diff-approval-layer": true,
|
|
23914
|
+
children: content
|
|
22346
23915
|
});
|
|
22347
23916
|
}
|
|
22348
23917
|
//#endregion
|
|
@@ -22714,14 +24283,34 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
22714
24283
|
parts.push(key.length === 1 ? key.toUpperCase() : key);
|
|
22715
24284
|
return parts.join("+");
|
|
22716
24285
|
}
|
|
22717
|
-
/**
|
|
22718
|
-
|
|
24286
|
+
/**
|
|
24287
|
+
* A button that records the next key chord (modifiers + key) as the shortcut.
|
|
24288
|
+
* Clicking it starts recording, clicking it again stops; a press anywhere else
|
|
24289
|
+
* while recording means "no shortcut" — the row is the control, so a press
|
|
24290
|
+
* outside it is the same answer as pressing Delete would be.
|
|
24291
|
+
*/
|
|
24292
|
+
function ShortcutRecorder({ value, onChange, dataAttribute, placeholder, noneLabel }) {
|
|
22719
24293
|
const [recording, setRecording] = (0, react.useState)(false);
|
|
22720
|
-
|
|
24294
|
+
const ref = (0, react.useRef)(null);
|
|
24295
|
+
(0, react.useEffect)(() => {
|
|
24296
|
+
if (!recording) return;
|
|
24297
|
+
const onPointerDown = (event) => {
|
|
24298
|
+
if (event.target instanceof Node && ref.current?.contains(event.target) === true) return;
|
|
24299
|
+
onChange("");
|
|
24300
|
+
setRecording(false);
|
|
24301
|
+
};
|
|
24302
|
+
document.addEventListener("pointerdown", onPointerDown, true);
|
|
24303
|
+
return () => {
|
|
24304
|
+
document.removeEventListener("pointerdown", onPointerDown, true);
|
|
24305
|
+
};
|
|
24306
|
+
}, [recording, onChange]);
|
|
24307
|
+
return (0, react_jsx_runtime.jsx)("button", {
|
|
24308
|
+
ref,
|
|
22721
24309
|
type: "button",
|
|
22722
24310
|
className: PendingPanel_module_css_default.settingsSelector,
|
|
24311
|
+
"data-recording": recording ? "" : void 0,
|
|
22723
24312
|
onClick: () => {
|
|
22724
|
-
setRecording(
|
|
24313
|
+
setRecording((value) => !value);
|
|
22725
24314
|
},
|
|
22726
24315
|
onBlur: () => {
|
|
22727
24316
|
setRecording(false);
|
|
@@ -22740,28 +24329,44 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
22740
24329
|
}
|
|
22741
24330
|
} : void 0,
|
|
22742
24331
|
[dataAttribute]: true,
|
|
22743
|
-
children:
|
|
24332
|
+
children: recording ? placeholder : value === "" ? noneLabel : value
|
|
22744
24333
|
});
|
|
22745
24334
|
}
|
|
22746
|
-
/** One shortcut row: title + description, chord recorder right. */
|
|
22747
|
-
function ShortcutRow({ title, description, value, onChange, dataAttribute, placeholder }) {
|
|
24335
|
+
/** One shortcut row: title + description, chord recorder and its reset right. */
|
|
24336
|
+
function ShortcutRow({ title, description, value, defaultValue, onChange, dataAttribute, placeholder, noneLabel, resetLabel }) {
|
|
22748
24337
|
return (0, react_jsx_runtime.jsxs)("div", {
|
|
22749
24338
|
className: PendingPanel_module_css_default.settingsRow,
|
|
22750
|
-
children: [
|
|
22751
|
-
|
|
22752
|
-
|
|
22753
|
-
|
|
22754
|
-
|
|
22755
|
-
|
|
22756
|
-
|
|
22757
|
-
|
|
22758
|
-
|
|
22759
|
-
|
|
22760
|
-
|
|
22761
|
-
|
|
22762
|
-
|
|
22763
|
-
|
|
22764
|
-
|
|
24339
|
+
children: [
|
|
24340
|
+
(0, react_jsx_runtime.jsxs)("div", {
|
|
24341
|
+
className: PendingPanel_module_css_default.settingsRowText,
|
|
24342
|
+
children: [(0, react_jsx_runtime.jsx)("div", {
|
|
24343
|
+
className: PendingPanel_module_css_default.settingsRowTitle,
|
|
24344
|
+
children: title
|
|
24345
|
+
}), (0, react_jsx_runtime.jsx)("div", {
|
|
24346
|
+
className: PendingPanel_module_css_default.settingsRowDesc,
|
|
24347
|
+
children: description
|
|
24348
|
+
})]
|
|
24349
|
+
}),
|
|
24350
|
+
(0, react_jsx_runtime.jsx)(ShortcutRecorder, {
|
|
24351
|
+
value,
|
|
24352
|
+
onChange,
|
|
24353
|
+
dataAttribute,
|
|
24354
|
+
placeholder,
|
|
24355
|
+
noneLabel
|
|
24356
|
+
}),
|
|
24357
|
+
(0, react_jsx_runtime.jsx)("button", {
|
|
24358
|
+
type: "button",
|
|
24359
|
+
className: PendingPanel_module_css_default.settingsReset,
|
|
24360
|
+
"data-reset": dataAttribute,
|
|
24361
|
+
"aria-label": resetLabel,
|
|
24362
|
+
title: resetLabel,
|
|
24363
|
+
disabled: value === defaultValue,
|
|
24364
|
+
onClick: () => {
|
|
24365
|
+
onChange(defaultValue);
|
|
24366
|
+
},
|
|
24367
|
+
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconRefreshOutline14, { size: 14 })
|
|
24368
|
+
})
|
|
24369
|
+
]
|
|
22765
24370
|
});
|
|
22766
24371
|
}
|
|
22767
24372
|
/** One color-picker row: title + description left, a DSH pill trigger that opens
|
|
@@ -22944,6 +24549,25 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
22944
24549
|
const addDefault = currentDiffAddColor();
|
|
22945
24550
|
const delDefault = currentDiffDelColor();
|
|
22946
24551
|
const [diffOpen, setDiffOpen] = (0, react.useState)(true);
|
|
24552
|
+
const [cover, setCoverState] = (0, react.useState)(panelCover);
|
|
24553
|
+
const [coverOpen, setCoverOpen] = (0, react.useState)(false);
|
|
24554
|
+
(0, react.useEffect)(() => {
|
|
24555
|
+
const onCover = () => {
|
|
24556
|
+
setCoverState(panelCover());
|
|
24557
|
+
};
|
|
24558
|
+
window.addEventListener(COVER_CHANGED_EVENT, onCover);
|
|
24559
|
+
return () => {
|
|
24560
|
+
window.removeEventListener(COVER_CHANGED_EVENT, onCover);
|
|
24561
|
+
};
|
|
24562
|
+
}, []);
|
|
24563
|
+
const setCoverFlag = (edge, next) => {
|
|
24564
|
+
const value = {
|
|
24565
|
+
...cover,
|
|
24566
|
+
[edge]: next
|
|
24567
|
+
};
|
|
24568
|
+
setCoverState(value);
|
|
24569
|
+
setPanelCover(value);
|
|
24570
|
+
};
|
|
22947
24571
|
const [summon, setSummonState] = (0, react.useState)(quickSummonKey);
|
|
22948
24572
|
const [keysOpen, setKeysOpen] = (0, react.useState)(false);
|
|
22949
24573
|
const [keybindings, setKeybindingsState] = (0, react.useState)(() => {
|
|
@@ -23165,21 +24789,82 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
23165
24789
|
title: t("panel.quickSummon"),
|
|
23166
24790
|
description: t("panel.quickSummonDesc"),
|
|
23167
24791
|
value: summon,
|
|
24792
|
+
defaultValue: "Ctrl+D",
|
|
23168
24793
|
onChange: setSummon,
|
|
23169
24794
|
dataAttribute: "data-diff-quick-summon-key",
|
|
23170
|
-
placeholder: t("panel.recordShortcut")
|
|
24795
|
+
placeholder: t("panel.recordShortcut"),
|
|
24796
|
+
noneLabel: t("panel.shortcutNone"),
|
|
24797
|
+
resetLabel: t("panel.shortcutReset")
|
|
23171
24798
|
}), Object.keys(DEFAULT_KEYBINDINGS).map((action) => (0, react_jsx_runtime.jsx)(ShortcutRow, {
|
|
23172
24799
|
title: t(`panel.key.${action}`),
|
|
23173
24800
|
description: t("panel.keyDesc"),
|
|
23174
24801
|
value: keybindings[action] ?? DEFAULT_KEYBINDINGS[action] ?? "",
|
|
24802
|
+
defaultValue: DEFAULT_KEYBINDINGS[action] ?? "",
|
|
23175
24803
|
onChange: (chord) => {
|
|
23176
24804
|
setKB(action, chord);
|
|
23177
24805
|
},
|
|
23178
|
-
dataAttribute: `data-diff-key-${action}`,
|
|
23179
|
-
placeholder: t("panel.recordShortcut")
|
|
24806
|
+
dataAttribute: `data-diff-key-${action.toLowerCase()}`,
|
|
24807
|
+
placeholder: t("panel.recordShortcut"),
|
|
24808
|
+
noneLabel: t("panel.shortcutNone"),
|
|
24809
|
+
resetLabel: t("panel.shortcutReset")
|
|
23180
24810
|
}, action))]
|
|
23181
24811
|
})]
|
|
23182
24812
|
}),
|
|
24813
|
+
(0, react_jsx_runtime.jsxs)("div", {
|
|
24814
|
+
className: PendingPanel_module_css_default.settingsGroup,
|
|
24815
|
+
"data-open": coverOpen || void 0,
|
|
24816
|
+
children: [(0, react_jsx_runtime.jsxs)("button", {
|
|
24817
|
+
type: "button",
|
|
24818
|
+
className: PendingPanel_module_css_default.settingsGroupHeader,
|
|
24819
|
+
onClick: () => {
|
|
24820
|
+
setCoverOpen((open) => !open);
|
|
24821
|
+
},
|
|
24822
|
+
"data-diff-cover-toggle": true,
|
|
24823
|
+
children: [(0, react_jsx_runtime.jsxs)("span", {
|
|
24824
|
+
className: PendingPanel_module_css_default.settingsGroupText,
|
|
24825
|
+
children: [(0, react_jsx_runtime.jsx)("span", {
|
|
24826
|
+
className: PendingPanel_module_css_default.settingsGroupTitle,
|
|
24827
|
+
children: t("settings.cover")
|
|
24828
|
+
}), (0, react_jsx_runtime.jsx)("span", {
|
|
24829
|
+
className: PendingPanel_module_css_default.settingsGroupDesc,
|
|
24830
|
+
children: t("settings.coverDesc")
|
|
24831
|
+
})]
|
|
24832
|
+
}), (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, { className: PendingPanel_module_css_default.settingsGroupChevron })]
|
|
24833
|
+
}), coverOpen && (0, react_jsx_runtime.jsx)("div", {
|
|
24834
|
+
className: PendingPanel_module_css_default.settingsGroupBody,
|
|
24835
|
+
children: [
|
|
24836
|
+
[
|
|
24837
|
+
"left",
|
|
24838
|
+
"cover.left",
|
|
24839
|
+
"cover.leftDesc"
|
|
24840
|
+
],
|
|
24841
|
+
[
|
|
24842
|
+
"top",
|
|
24843
|
+
"cover.top",
|
|
24844
|
+
"cover.topDesc"
|
|
24845
|
+
],
|
|
24846
|
+
[
|
|
24847
|
+
"right",
|
|
24848
|
+
"cover.right",
|
|
24849
|
+
"cover.rightDesc"
|
|
24850
|
+
],
|
|
24851
|
+
[
|
|
24852
|
+
"composer",
|
|
24853
|
+
"cover.composer",
|
|
24854
|
+
"cover.composerDesc"
|
|
24855
|
+
]
|
|
24856
|
+
].map(([edge, title, description]) => (0, react_jsx_runtime.jsx)(PreferenceRow, {
|
|
24857
|
+
title: t(title),
|
|
24858
|
+
description: t(description),
|
|
24859
|
+
value: cover[edge],
|
|
24860
|
+
onSelect: (next) => {
|
|
24861
|
+
setCoverFlag(edge, next);
|
|
24862
|
+
},
|
|
24863
|
+
dataAttribute: `data-diff-cover-${edge}`,
|
|
24864
|
+
t
|
|
24865
|
+
}, edge))
|
|
24866
|
+
})]
|
|
24867
|
+
}),
|
|
23183
24868
|
(0, react_jsx_runtime.jsx)(PreferenceRow, {
|
|
23184
24869
|
title: t("panel.importUntracked"),
|
|
23185
24870
|
description: t("panel.importUntrackedDesc"),
|
|
@@ -23904,6 +25589,69 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
23904
25589
|
};
|
|
23905
25590
|
}
|
|
23906
25591
|
//#endregion
|
|
25592
|
+
//#region lib/types/client/header-entry.js
|
|
25593
|
+
/**
|
|
25594
|
+
* The pending-changes entry in the Session header.
|
|
25595
|
+
*
|
|
25596
|
+
* The sidebar footer's badge is only where the sidebar is: with the sidebar
|
|
25597
|
+
* collapsed to its rail, or hidden on a narrow window, the review has no entry.
|
|
25598
|
+
* The Session header's right-aligned utilities hold the app's own per-session
|
|
25599
|
+
* controls — "open in app" and the more-actions button the session-log export
|
|
25600
|
+
* lives behind — and are visible whatever the sidebar does. This seats the same
|
|
25601
|
+
* action there: one compact button carrying the pending count.
|
|
25602
|
+
*
|
|
25603
|
+
* The header entry is a *second mount* of one action, not a second panel: the
|
|
25604
|
+
* click travels to the mount that owns the open state ({@link TOGGLE_PANEL_EVENT})
|
|
25605
|
+
* and the visibility comes back ({@link PANEL_STATE_EVENT}), because the two sit
|
|
25606
|
+
* in different slot trees and neither owns the other. That is also why a press is
|
|
25607
|
+
* exactly the footer badge's press: a docked panel's tab is revealed, and still
|
|
25608
|
+
* closed by the chip on its own tab rather than from here.
|
|
25609
|
+
*
|
|
25610
|
+
* @module dsh-diff-approval/client/header-entry
|
|
25611
|
+
*/
|
|
25612
|
+
/**
|
|
25613
|
+
* Render the Session header's pending-changes button.
|
|
25614
|
+
* @param props - the bound hooks, the session seat, and the translator.
|
|
25615
|
+
* @returns the icon button, with the pending count while there is one.
|
|
25616
|
+
*/
|
|
25617
|
+
function DiffApprovalHeaderEntry({ usePending, useSessions, useDock, t }) {
|
|
25618
|
+
const count = usePending((snapshot) => snapshot.files.length);
|
|
25619
|
+
const dockShowing = useDock?.((state) => state.open) === true;
|
|
25620
|
+
const [open, setOpen] = (0, react.useState)(false);
|
|
25621
|
+
(0, react.useEffect)(() => {
|
|
25622
|
+
const onState = (event) => {
|
|
25623
|
+
setOpen(event.detail?.open === true);
|
|
25624
|
+
};
|
|
25625
|
+
window.addEventListener(PANEL_STATE_EVENT, onState);
|
|
25626
|
+
return () => {
|
|
25627
|
+
window.removeEventListener(PANEL_STATE_EVENT, onState);
|
|
25628
|
+
};
|
|
25629
|
+
}, []);
|
|
25630
|
+
const noSession = useSessions === void 0 ? false : useSessions((state) => state.current === void 0 || (state.byId[state.current]?.blank ?? false));
|
|
25631
|
+
const active = open || dockShowing;
|
|
25632
|
+
return (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
25633
|
+
label: summonHint(t),
|
|
25634
|
+
side: "bottom",
|
|
25635
|
+
delayMs: 500,
|
|
25636
|
+
children: (0, react_jsx_runtime.jsxs)("button", {
|
|
25637
|
+
type: "button",
|
|
25638
|
+
className: PendingPanel_module_css_default.headerEntry,
|
|
25639
|
+
"data-diff-approval-header-entry": count,
|
|
25640
|
+
"data-active": active ? "" : void 0,
|
|
25641
|
+
"aria-label": t("panel.aria"),
|
|
25642
|
+
"aria-expanded": active,
|
|
25643
|
+
disabled: noSession,
|
|
25644
|
+
onClick: () => {
|
|
25645
|
+
window.dispatchEvent(new CustomEvent(TOGGLE_PANEL_EVENT));
|
|
25646
|
+
},
|
|
25647
|
+
children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconListPenOutline16, { size: 16 }), count > 0 && (0, react_jsx_runtime.jsx)("span", {
|
|
25648
|
+
className: PendingPanel_module_css_default.headerEntryCount,
|
|
25649
|
+
children: count
|
|
25650
|
+
})]
|
|
25651
|
+
})
|
|
25652
|
+
});
|
|
25653
|
+
}
|
|
25654
|
+
//#endregion
|
|
23907
25655
|
//#region lib/types/client/locales.js
|
|
23908
25656
|
/** Pending-edit review UI dictionaries. */
|
|
23909
25657
|
const NS = "diff-approval";
|
|
@@ -23911,6 +25659,9 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
23911
25659
|
const zh = {
|
|
23912
25660
|
"panel.title": "待处理改动",
|
|
23913
25661
|
"panel.empty": "列表中暂无文件",
|
|
25662
|
+
"panel.dockGuide": "在右侧边栏中打开待处理改动面板",
|
|
25663
|
+
"panel.dockUnavailable": "这个环境没有可停靠的右侧边栏",
|
|
25664
|
+
"panel.dockFailed": "右侧边栏现在还不能接收面板",
|
|
23914
25665
|
"panel.loading": "读取中…",
|
|
23915
25666
|
"panel.readFailed": "读取待处理改动失败:{message}",
|
|
23916
25667
|
"panel.group.current": "当前会话",
|
|
@@ -23923,7 +25674,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
23923
25674
|
"panel.blockSingle": "仅有一个差异块",
|
|
23924
25675
|
"panel.viewDiff": "查看差异",
|
|
23925
25676
|
"panel.fileNotPending": "该文件不在待处理差异列表中",
|
|
23926
|
-
"panel.selectHint": "
|
|
25677
|
+
"panel.selectHint": "点击文件列表项目查看差异",
|
|
23927
25678
|
"panel.searchPlaceholder": "搜索",
|
|
23928
25679
|
"panel.missing": "文件已不存在",
|
|
23929
25680
|
"panel.missingHint": "该文件已不存在,回退会恢复该文件。",
|
|
@@ -23959,7 +25710,9 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
23959
25710
|
"settings.diffView": "差异视图",
|
|
23960
25711
|
"settings.diffViewDesc": "调整差异的显示效果(字号、行高、新增/删除颜色)与布局偏好。",
|
|
23961
25712
|
"settings.diffPreview": "预览",
|
|
23962
|
-
"panel.keyDesc": "
|
|
25713
|
+
"panel.keyDesc": "点击按钮后按下新的按键组合即可修改;录制时点击其他地方表示不绑定,右侧按钮恢复默认。",
|
|
25714
|
+
"panel.shortcutNone": "无",
|
|
25715
|
+
"panel.shortcutReset": "恢复默认",
|
|
23963
25716
|
"panel.key.jumpUp": "跳转到上一个差异块",
|
|
23964
25717
|
"panel.key.jumpDown": "跳转到下一个差异块",
|
|
23965
25718
|
"panel.key.copyRef": "复制引用",
|
|
@@ -23972,6 +25725,10 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
23972
25725
|
"panel.key.redo": "重做",
|
|
23973
25726
|
"panel.key.cycleNext": "下一个待处理文件",
|
|
23974
25727
|
"panel.key.cyclePrev": "上一个待处理文件",
|
|
25728
|
+
"panel.key.coverLeft": "浮窗:覆盖左侧边栏",
|
|
25729
|
+
"panel.key.coverComposer": "浮窗:覆盖下方输入框",
|
|
25730
|
+
"panel.key.coverRight": "浮窗:覆盖右侧边栏",
|
|
25731
|
+
"panel.key.coverTop": "浮窗:覆盖上方标题栏",
|
|
23975
25732
|
"settings.tabLabel": "改动审批",
|
|
23976
25733
|
"row.create": "新增",
|
|
23977
25734
|
"row.failed": "失败",
|
|
@@ -23990,6 +25747,8 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
23990
25747
|
"action.nextDiff": "下一处差异",
|
|
23991
25748
|
"action.showFileList": "展开文件列表",
|
|
23992
25749
|
"action.hideFileList": "收起文件列表",
|
|
25750
|
+
"action.fileListFloatOn": "文件列表始终折叠",
|
|
25751
|
+
"action.fileListFloatOff": "取消始终折叠",
|
|
23993
25752
|
"action.viewSplit": "切换到双栏",
|
|
23994
25753
|
"action.viewUnified": "切换到单栏",
|
|
23995
25754
|
"action.viewPreview": "预览",
|
|
@@ -24017,9 +25776,31 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
24017
25776
|
"panel.importFailed": "导入失败:{message}",
|
|
24018
25777
|
"action.search": "搜索",
|
|
24019
25778
|
"action.settings": "设置",
|
|
24020
|
-
"action.
|
|
24021
|
-
"action.
|
|
25779
|
+
"action.presentation": "呈现方式",
|
|
25780
|
+
"action.presentationCurrent": "呈现方式:{state}",
|
|
25781
|
+
"action.cover": "覆盖范围",
|
|
25782
|
+
"cover.left": "覆盖左侧边栏",
|
|
25783
|
+
"cover.right": "覆盖右侧边栏",
|
|
25784
|
+
"cover.composer": "覆盖下方输入框",
|
|
25785
|
+
"cover.top": "覆盖上方标题栏",
|
|
25786
|
+
"cover.leftDesc": "浮窗盖住左侧边栏;关闭时让开它(边栏折叠时让开图标栏)。",
|
|
25787
|
+
"cover.topDesc": "浮窗盖住标题区;关闭时露出会话标题行,视图标签栏仍会被盖住。",
|
|
25788
|
+
"cover.rightDesc": "浮窗盖住右侧边栏;关闭时让开它。",
|
|
25789
|
+
"cover.composerDesc": "浮窗盖住下方输入框;关闭时停在输入框上方(浮窗默认)。",
|
|
25790
|
+
"settings.cover": "浮窗覆盖",
|
|
25791
|
+
"settings.coverDesc": "浮窗形态下,面板四边是否分别盖住左侧边栏、上方标题栏、右侧边栏、下方输入框;四个全开即原来的全屏。",
|
|
25792
|
+
"cover.on": "开",
|
|
25793
|
+
"cover.off": "关",
|
|
25794
|
+
"cover.notice": "{label}:{state}",
|
|
25795
|
+
"action.presentationFloat": "浮窗",
|
|
25796
|
+
"action.presentationDock": "停靠到右侧边栏",
|
|
25797
|
+
"action.presentationDocked": "已停靠",
|
|
24022
25798
|
"action.close": "关闭",
|
|
25799
|
+
"action.closeHint": "关闭(Esc / {chord})",
|
|
25800
|
+
"action.closeHintEsc": "关闭(Esc)",
|
|
25801
|
+
"action.summonHint": "待处理改动({chord})",
|
|
25802
|
+
"action.retry": "重试",
|
|
25803
|
+
"panel.crashed": "审阅面板出错了,详情见控制台",
|
|
24023
25804
|
"status.kept": "已保留",
|
|
24024
25805
|
"status.reverted": "已回退",
|
|
24025
25806
|
"status.missing": "该改动已不存在",
|
|
@@ -24062,6 +25843,9 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
24062
25843
|
const en = {
|
|
24063
25844
|
"panel.title": "Pending changes",
|
|
24064
25845
|
"panel.empty": "No files in the list yet",
|
|
25846
|
+
"panel.dockGuide": "Open the pending-changes panel in the right sidebar",
|
|
25847
|
+
"panel.dockUnavailable": "This build has no right sidebar to dock into",
|
|
25848
|
+
"panel.dockFailed": "The right sidebar cannot take the panel yet",
|
|
24065
25849
|
"panel.loading": "Reading…",
|
|
24066
25850
|
"panel.readFailed": "Reading pending changes failed: {message}",
|
|
24067
25851
|
"panel.group.current": "This session",
|
|
@@ -24074,7 +25858,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
24074
25858
|
"panel.blockSingle": "Only one diff block",
|
|
24075
25859
|
"panel.viewDiff": "View diff",
|
|
24076
25860
|
"panel.fileNotPending": "This file is not in the pending diff list",
|
|
24077
|
-
"panel.selectHint": "
|
|
25861
|
+
"panel.selectHint": "Click a file in the list to review its diff",
|
|
24078
25862
|
"panel.searchPlaceholder": "Search",
|
|
24079
25863
|
"panel.missing": "File is gone",
|
|
24080
25864
|
"panel.missingHint": "This file no longer exists; reverting restores it.",
|
|
@@ -24110,7 +25894,9 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
24110
25894
|
"settings.diffView": "Diff view",
|
|
24111
25895
|
"settings.diffViewDesc": "Adjust the diff appearance (font size, line height, add/delete colors) and layout preferences.",
|
|
24112
25896
|
"settings.diffPreview": "Preview",
|
|
24113
|
-
"panel.keyDesc": "Click the button
|
|
25897
|
+
"panel.keyDesc": "Click the button and press a new chord to rebind it; pressing elsewhere while recording leaves it unbound, and the button beside it restores the default.",
|
|
25898
|
+
"panel.shortcutNone": "None",
|
|
25899
|
+
"panel.shortcutReset": "Restore default",
|
|
24114
25900
|
"panel.key.jumpUp": "Jump to the previous diff block",
|
|
24115
25901
|
"panel.key.jumpDown": "Jump to the next diff block",
|
|
24116
25902
|
"panel.key.copyRef": "Copy reference",
|
|
@@ -24123,6 +25909,10 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
24123
25909
|
"panel.key.redo": "Redo",
|
|
24124
25910
|
"panel.key.cycleNext": "Next pending file",
|
|
24125
25911
|
"panel.key.cyclePrev": "Previous pending file",
|
|
25912
|
+
"panel.key.coverLeft": "Float: cover the left sidebar",
|
|
25913
|
+
"panel.key.coverComposer": "Float: cover the composer below",
|
|
25914
|
+
"panel.key.coverRight": "Float: cover the right sidebar",
|
|
25915
|
+
"panel.key.coverTop": "Float: cover the header above",
|
|
24126
25916
|
"settings.tabLabel": "Diff Approval",
|
|
24127
25917
|
"row.create": "New",
|
|
24128
25918
|
"row.failed": "Failed",
|
|
@@ -24141,6 +25931,8 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
24141
25931
|
"action.nextDiff": "Next diff",
|
|
24142
25932
|
"action.showFileList": "Show file list",
|
|
24143
25933
|
"action.hideFileList": "Hide file list",
|
|
25934
|
+
"action.fileListFloatOn": "Always fold the file list",
|
|
25935
|
+
"action.fileListFloatOff": "Stop always folding",
|
|
24144
25936
|
"action.viewSplit": "Switch to side-by-side",
|
|
24145
25937
|
"action.viewUnified": "Switch to unified",
|
|
24146
25938
|
"action.viewPreview": "Preview",
|
|
@@ -24168,9 +25960,31 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
24168
25960
|
"panel.importFailed": "Import failed: {message}",
|
|
24169
25961
|
"action.search": "Search",
|
|
24170
25962
|
"action.settings": "Settings",
|
|
24171
|
-
"action.
|
|
24172
|
-
"action.
|
|
25963
|
+
"action.presentation": "Presentation",
|
|
25964
|
+
"action.presentationCurrent": "Presentation: {state}",
|
|
25965
|
+
"action.cover": "Coverage",
|
|
25966
|
+
"cover.left": "Cover the left sidebar",
|
|
25967
|
+
"cover.right": "Cover the right sidebar",
|
|
25968
|
+
"cover.composer": "Cover the composer below",
|
|
25969
|
+
"cover.top": "Cover the header above",
|
|
25970
|
+
"cover.leftDesc": "The floating panel covers the left sidebar; off leaves it visible (its collapsed rail included).",
|
|
25971
|
+
"cover.topDesc": "The floating panel covers the header area; off leaves the session title row visible while the view tabs stay covered.",
|
|
25972
|
+
"cover.rightDesc": "The floating panel covers the right sidebar; off leaves it visible.",
|
|
25973
|
+
"cover.composerDesc": "The floating panel covers the composer below; off stops above it, which is the floating default.",
|
|
25974
|
+
"settings.cover": "Floating coverage",
|
|
25975
|
+
"settings.coverDesc": "While floating, whether the panel covers the left sidebar, the header above, the right sidebar, and the composer below; all four is what fullscreen used to be.",
|
|
25976
|
+
"cover.on": "on",
|
|
25977
|
+
"cover.off": "off",
|
|
25978
|
+
"cover.notice": "{label}: {state}",
|
|
25979
|
+
"action.presentationFloat": "Floating",
|
|
25980
|
+
"action.presentationDock": "Dock in the right sidebar",
|
|
25981
|
+
"action.presentationDocked": "Docked",
|
|
24173
25982
|
"action.close": "Close",
|
|
25983
|
+
"action.closeHint": "Close (Esc / {chord})",
|
|
25984
|
+
"action.closeHintEsc": "Close (Esc)",
|
|
25985
|
+
"action.summonHint": "Pending changes ({chord})",
|
|
25986
|
+
"action.retry": "Retry",
|
|
25987
|
+
"panel.crashed": "The review panel hit an error; see the console for details",
|
|
24174
25988
|
"status.kept": "Kept",
|
|
24175
25989
|
"status.reverted": "Reverted",
|
|
24176
25990
|
"status.missing": "This change no longer exists",
|
|
@@ -24212,6 +26026,25 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
24212
26026
|
//#endregion
|
|
24213
26027
|
//#region lib/types/client/index.js
|
|
24214
26028
|
/** Pending-edit review panel, browser half: footer action, pending list, and whole-file diff viewer. */
|
|
26029
|
+
/**
|
|
26030
|
+
* The footer seat's registrant: the panel inside its own error boundary, so a
|
|
26031
|
+
* crash there cannot retire the entry for the life of the page (see
|
|
26032
|
+
* {@link PanelBoundary}).
|
|
26033
|
+
* @param props - the seat's runtime props, this plugin's face, and the locale `t`.
|
|
26034
|
+
* @returns the boundary-wrapped panel.
|
|
26035
|
+
*/
|
|
26036
|
+
function PendingPanelEntry(props) {
|
|
26037
|
+
return (0, react.createElement)(PanelBoundary, { t: props.t }, (0, react.createElement)(PendingPanel, props));
|
|
26038
|
+
}
|
|
26039
|
+
/**
|
|
26040
|
+
* The header seat's registrant: the entry button inside the same boundary, so a
|
|
26041
|
+
* crash costs a visible note rather than the whole button for the page's life.
|
|
26042
|
+
* @param props - the seat's runtime props, the face's hooks, and the locale `t`.
|
|
26043
|
+
* @returns the boundary-wrapped entry.
|
|
26044
|
+
*/
|
|
26045
|
+
function HeaderEntry(props) {
|
|
26046
|
+
return (0, react.createElement)(PanelBoundary, { t: props.t }, (0, react.createElement)(DiffApprovalHeaderEntry, props));
|
|
26047
|
+
}
|
|
24215
26048
|
/** Required services: locale, slots, the wire channel, the current session, and
|
|
24216
26049
|
* the layout controller (this plugin collapses the sidebar before its modal opens). */
|
|
24217
26050
|
const inject = [
|
|
@@ -24222,6 +26055,22 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
24222
26055
|
"layout"
|
|
24223
26056
|
];
|
|
24224
26057
|
/**
|
|
26058
|
+
* Fill one slot, with the failure contained: a registration runs inside the
|
|
26059
|
+
* client's boot (or, for a seat declared later, inside the slot machinery), so a
|
|
26060
|
+
* throw here would take down more than this panel. A refused seat costs that one
|
|
26061
|
+
* surface, and the reason goes to the console rather than being swallowed.
|
|
26062
|
+
* @param slots - the client slot service.
|
|
26063
|
+
* @param name - the SlotMap key to fill.
|
|
26064
|
+
* @param register - the registration, run when the seat's declaration is live.
|
|
26065
|
+
*/
|
|
26066
|
+
function seat(slots, name, register) {
|
|
26067
|
+
try {
|
|
26068
|
+
slots.inject(name, () => register());
|
|
26069
|
+
} catch (error) {
|
|
26070
|
+
console.error(`diff-approval could not fill ${name}:`, error);
|
|
26071
|
+
}
|
|
26072
|
+
}
|
|
26073
|
+
/**
|
|
24225
26074
|
* The dsh web-react renderer gives `div[data-slot="sidebar.footer.action"]` an
|
|
24226
26075
|
* inline `display: contents`, so every plugin's footer entry root participates
|
|
24227
26076
|
* directly in the `.footerActions` flex row — several plugins (e.g. this one
|
|
@@ -24312,48 +26161,91 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
|
|
|
24312
26161
|
ctx.on("connection/reset", () => {
|
|
24313
26162
|
store.reset();
|
|
24314
26163
|
});
|
|
24315
|
-
|
|
26164
|
+
const dock = createDockState();
|
|
26165
|
+
/** The face both the footer panel and the docked tab render with. */
|
|
26166
|
+
const buildFace = () => ({
|
|
26167
|
+
hooks: {
|
|
26168
|
+
pending: store,
|
|
26169
|
+
dock: dock.face.hooks.dock
|
|
26170
|
+
},
|
|
26171
|
+
onOpenDock: dock.face.open,
|
|
26172
|
+
onDockShowing: dock.face.setShowing,
|
|
26173
|
+
onDockClose: dock.face.setClose,
|
|
26174
|
+
closeDock: dock.face.close,
|
|
26175
|
+
onRefresh: (sessionId) => {
|
|
26176
|
+
currentSessionId = sessionId;
|
|
26177
|
+
store.refresh(sessionId);
|
|
26178
|
+
},
|
|
26179
|
+
onKeep: (sessionId, path, keepListed) => store.keep(sessionId, path, keepListed),
|
|
26180
|
+
onRevert: (sessionId, path, keepListed) => {
|
|
26181
|
+
const entry = store.getSnapshot().files.find((file) => file.id === path);
|
|
26182
|
+
const before = entry?.newText;
|
|
26183
|
+
const after = entry?.oldText;
|
|
26184
|
+
const filePath = entry?.path;
|
|
26185
|
+
return store.revert(sessionId, path, keepListed).then(() => {
|
|
26186
|
+
if (filePath !== void 0 && before !== void 0 && after !== void 0) remapFile(filePath, before, after);
|
|
26187
|
+
});
|
|
26188
|
+
},
|
|
26189
|
+
onBlockKeep: (sessionId, id, block, removeWhenResolved) => store.blockKeep(sessionId, id, block, removeWhenResolved),
|
|
26190
|
+
onBlockRevert: (sessionId, id, block, removeWhenResolved) => store.blockRevert(sessionId, id, block, removeWhenResolved),
|
|
26191
|
+
onOpen: (sessionId, id, action) => store.open(sessionId, id, action),
|
|
26192
|
+
onPreviewImage: (sessionId, path) => store.previewImage(sessionId, path),
|
|
26193
|
+
onUndo: (sessionId) => store.undo(sessionId),
|
|
26194
|
+
onRedo: (sessionId) => store.redo(sessionId),
|
|
26195
|
+
onImportVcs: (sessionId, includeUntracked) => store.importVcs(sessionId, includeUntracked),
|
|
26196
|
+
onRefreshVcs: (sessionId, id, includeUntracked) => store.refreshVcs(sessionId, id, includeUntracked),
|
|
26197
|
+
onBrowse: (sessionId, path) => store.browse(sessionId, path),
|
|
26198
|
+
onAddPath: (sessionId, path, includeUnchanged) => store.addPath(sessionId, path, includeUnchanged),
|
|
26199
|
+
onKeepAll: (sessionId) => store.keepAll(sessionId),
|
|
26200
|
+
onRevertAll: (sessionId) => store.revertAll(sessionId),
|
|
26201
|
+
onAckRedoCleared: () => store.clearRedoCleared(),
|
|
26202
|
+
onPasteReference: (sessionId, reference) => {
|
|
26203
|
+
conversationAccess(ctx, () => sessionId).appendDraft(reference);
|
|
26204
|
+
document.querySelector("[data-composer-input]")?.focus();
|
|
26205
|
+
},
|
|
26206
|
+
collapseSidebar
|
|
26207
|
+
});
|
|
26208
|
+
seat(ctx.slots, "sidebar.footer.action", () => ctx.slots.register({
|
|
24316
26209
|
name: "sidebar.footer.action",
|
|
24317
26210
|
id: "diff-approval-panel",
|
|
24318
26211
|
locale: NS,
|
|
24319
|
-
inject:
|
|
24320
|
-
|
|
24321
|
-
|
|
24322
|
-
|
|
24323
|
-
|
|
24324
|
-
|
|
24325
|
-
|
|
24326
|
-
|
|
24327
|
-
|
|
24328
|
-
|
|
24329
|
-
|
|
24330
|
-
|
|
24331
|
-
|
|
24332
|
-
|
|
24333
|
-
|
|
24334
|
-
|
|
24335
|
-
|
|
24336
|
-
|
|
24337
|
-
|
|
24338
|
-
|
|
24339
|
-
|
|
24340
|
-
|
|
24341
|
-
|
|
24342
|
-
|
|
24343
|
-
|
|
24344
|
-
|
|
24345
|
-
|
|
24346
|
-
|
|
24347
|
-
|
|
24348
|
-
onPasteReference: (sessionId, reference) => {
|
|
24349
|
-
conversationAccess(ctx, () => sessionId).appendDraft(reference);
|
|
24350
|
-
document.querySelector("[data-composer-input]")?.focus();
|
|
24351
|
-
},
|
|
24352
|
-
collapseSidebar
|
|
24353
|
-
})
|
|
24354
|
-
}, PendingPanel));
|
|
26212
|
+
inject: buildFace
|
|
26213
|
+
}, PendingPanelEntry));
|
|
26214
|
+
const looseSlots = ctx.slots;
|
|
26215
|
+
try {
|
|
26216
|
+
looseSlots.inject("conversation.session.header.utilities", () => looseSlots.register({
|
|
26217
|
+
name: "conversation.session.header.utilities",
|
|
26218
|
+
id: "diff-approval-entry",
|
|
26219
|
+
order: -5,
|
|
26220
|
+
locale: NS,
|
|
26221
|
+
inject: () => ({ ...buildFace() })
|
|
26222
|
+
}, HeaderEntry));
|
|
26223
|
+
} catch {}
|
|
26224
|
+
try {
|
|
26225
|
+
looseSlots.inject("sidebar.right.pane.tab", () => looseSlots.register({
|
|
26226
|
+
name: "sidebar.right.pane.tab",
|
|
26227
|
+
key: DIFF_DOCK_ID,
|
|
26228
|
+
locale: NS,
|
|
26229
|
+
inject: () => ({
|
|
26230
|
+
...buildFace(),
|
|
26231
|
+
docked: true
|
|
26232
|
+
})
|
|
26233
|
+
}, DiffDockBody));
|
|
26234
|
+
looseSlots.inject("sidebar.right.pane.tab.title", () => looseSlots.register({
|
|
26235
|
+
name: "sidebar.right.pane.tab.title",
|
|
26236
|
+
key: DIFF_DOCK_ID,
|
|
26237
|
+
locale: NS,
|
|
26238
|
+
inject: () => ({ ...buildFace() })
|
|
26239
|
+
}, DiffDockTitle));
|
|
26240
|
+
} catch {}
|
|
24355
26241
|
if (typeof window !== "undefined") ctx.effect(() => startProducedDiffInjection(t("panel.viewDiff"), (path) => window.dispatchEvent(new CustomEvent(OPEN_FILE_EVENT, { detail: { path } }))), "diff-approval: produced-files diff buttons");
|
|
24356
|
-
|
|
26242
|
+
try {
|
|
26243
|
+
attachDiffDock(ctx, {
|
|
26244
|
+
title: () => t("panel.title"),
|
|
26245
|
+
guideDescription: () => t("panel.dockGuide")
|
|
26246
|
+
}, (sidebar, reason) => dock.attach(sidebar, reason));
|
|
26247
|
+
} catch {}
|
|
26248
|
+
seat(ctx.slots, "settings.section", () => ctx.slots.register({
|
|
24357
26249
|
name: "settings.section",
|
|
24358
26250
|
id: "diff-approval",
|
|
24359
26251
|
order: 100,
|