dsh-diff-approval 0.2.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/client.js CHANGED
@@ -11333,6 +11333,8 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
11333
11333
  xml_default,
11334
11334
  yaml_default
11335
11335
  ];
11336
+ /** Supported grammar ids, in picker order, for the viewer's language selector. */
11337
+ const HIGHLIGHT_LANGS = [...new Set(LANGS.flat().map((lang) => lang.name))];
11336
11338
  /** All token colors resolve through `--shiki-*` custom properties (theme package sheets). */
11337
11339
  const cssVariablesTheme = createCssVariablesTheme({
11338
11340
  name: "css-variables",
@@ -11485,7 +11487,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
11485
11487
  * @param path - the path to shorten.
11486
11488
  * @returns the segment after the final separator.
11487
11489
  */
11488
- function basenameOf(path) {
11490
+ function basenameOf$1(path) {
11489
11491
  const normalized = path.replaceAll("\\", "/");
11490
11492
  return normalized.slice(normalized.lastIndexOf("/") + 1);
11491
11493
  }
@@ -11497,8 +11499,8 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
11497
11499
  * @returns the display path for a copied reference.
11498
11500
  */
11499
11501
  function copyDisplayPath(path, files) {
11500
- const base = basenameOf(path);
11501
- return files.some((file) => file.path !== path && basenameOf(file.path) === base) ? path : base;
11502
+ const base = basenameOf$1(path);
11503
+ return files.some((file) => file.path !== path && basenameOf$1(file.path) === base) ? path : base;
11502
11504
  }
11503
11505
  /**
11504
11506
  * Format one line range as a reference suffix: a single line number, or the
@@ -11522,8 +11524,8 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
11522
11524
  return `${copyDisplayPath(path, files)}:${lineRangeLabel(start, end)}`;
11523
11525
  }
11524
11526
  //#endregion
11525
- //#region \0dsh-css:C:\PROJECTS\dsh-diff-approval\src\client\PendingPanel.module.css.mjs
11526
- const css = ".Ay7oXG_layer{flex:none;align-items:center;width:100%;height:49px;margin:8px 0 0;display:flex;position:relative}.Ay7oXG_footerButtons{align-items:center;width:100%;display:flex}.Ay7oXG_badge{width:100%;height:49px;color:var(--dsw-alias-label-primary);cursor:pointer;background:0 0;border:none;border-radius:12px;align-items:center;gap:8px;padding:0 8px 0 6px;font-family:inherit;font-size:14px;display:inline-flex;overflow:hidden}.Ay7oXG_badge:hover{background:var(--dsw-alias-interactive-bg-hover-solid)}.Ay7oXG_badge[data-active]{background:var(--dsw-alias-interactive-bg-hover)}.Ay7oXG_badgeLabel{text-overflow:ellipsis;white-space:nowrap;min-width:0;overflow:hidden}.Ay7oXG_badgeCount{color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;flex:none;margin-left:auto;font-size:12px;line-height:16px}.Ay7oXG_layer.Ay7oXG_rail{width:36px;height:36px;margin:0}.Ay7oXG_rail .Ay7oXG_badge{border-radius:50%;justify-content:center;gap:0;width:36px;height:36px;padding:0}.Ay7oXG_rail .Ay7oXG_badgeLabel{display:none}.Ay7oXG_rail .Ay7oXG_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}.Ay7oXG_panel{z-index:30;border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-base);width:auto;max-width:none;max-height:86vh;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;bottom:128px;left:8px;right:8px;overflow:hidden}.Ay7oXG_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}.Ay7oXG_body{flex:1;min-height:0;padding:4px 12px 12px;overflow-y:auto}.Ay7oXG_title{color:var(--dsw-alias-label-primary);font-size:13px;font-weight:500;line-height:20px}.Ay7oXG_note,.Ay7oXG_readError,.Ay7oXG_hint{color:var(--dsw-alias-label-tertiary);margin:4px 0;font-size:12px;line-height:18px}.Ay7oXG_readError{color:var(--dsw-alias-state-error-primary)}.Ay7oXG_group{color:var(--dsw-alias-label-tertiary);margin:8px 0 4px;font-size:12px;font-weight:500;line-height:16px}.Ay7oXG_rows{margin:0;padding:0;list-style:none}.Ay7oXG_row{border:1px solid #0000;border-radius:10px;margin:2px 0}.Ay7oXG_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}.Ay7oXG_rowHead:hover{background:var(--dsw-alias-interactive-bg-hover-solid)}.Ay7oXG_rowHead[data-selected]{background:var(--dsw-alias-interactive-bg-hover)}.Ay7oXG_rowPath{text-overflow:ellipsis;white-space:nowrap;min-width:0;font:var(--dsw-font-markdown-code-block);text-align:left;direction:rtl;overflow:hidden}.Ay7oXG_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}.Ay7oXG_kindHint{color:var(--dsw-alias-label-tertiary);flex:none;font-size:12px;line-height:16px}.Ay7oXG_divergedHint,.Ay7oXG_missingHint{border-bottom:1px solid var(--dsw-alias-border-l2);margin:0;padding:6px 8px;font-size:12px;line-height:18px}.Ay7oXG_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}.Ay7oXG_missingHint{color:var(--dsw-alias-state-warn-label,var(--dsw-alias-state-warn-primary))}.Ay7oXG_rowMeta{color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;flex:none;margin-left:auto;font-size:12px;line-height:16px}.Ay7oXG_diff{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-markdown-code-block);border-radius:12px;margin:4px 0 8px;position:relative;overflow:hidden}.Ay7oXG_diffActions{border-bottom:1px solid var(--dsw-alias-border-l2);align-items:center;gap:8px;padding:6px 8px;display:flex}.Ay7oXG_diffStats{color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;font-size:12px;line-height:16px}.Ay7oXG_flexSpacer{flex:1}.Ay7oXG_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}.Ay7oXG_action:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.Ay7oXG_action:disabled{color:var(--dsw-alias-label-tertiary);cursor:default}.Ay7oXG_diffBody{max-height:360px;font:var(--dsw-font-markdown-code-block);padding:8px 10px;overflow:auto}.Ay7oXG_lines{border-spacing:0;width:max-content;display:table}.Ay7oXG_line{user-select:none;display:table-row}.Ay7oXG_gutter{box-sizing:border-box;text-align:right;width:44px;color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;user-select:none;padding-right:10px;display:table-cell}.Ay7oXG_code{white-space:pre;display:table-cell}.Ay7oXG_context{color:var(--dsw-alias-label-primary)}.Ay7oXG_del{background-color:color-mix(in srgb, var(--dsw-alias-state-error-primary) 12%, transparent)}.Ay7oXG_add{background-color:color-mix(in srgb, var(--dsw-alias-state-success-primary) 10%, transparent)}.Ay7oXG_toolbar{z-index:2;border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-base);box-shadow:var(--dsw-shadow-lv2);border-radius:8px;gap:4px;padding:3px;display:flex;position:absolute;right:10px}.Ay7oXG_toolbarButton{color:var(--dsw-alias-label-primary);white-space:nowrap;cursor:pointer;background:0 0;border:none;border-radius:6px;padding:3px 10px;font-family:inherit;font-size:12px;line-height:18px}.Ay7oXG_toolbarButton:hover{background:var(--dsw-alias-interactive-bg-hover)}";
11527
+ //#region \0dsh-css:/home/runner/work/dsh-diff-approval/dsh-diff-approval/src/client/PendingPanel.module.css.mjs
11528
+ const css = ".F1KBNa_layer{flex:none;align-items:center;width:calc(100% + 8px);height:34px;margin:4px -4px;display:flex;position:relative}.F1KBNa_footerButtons{align-items:center;width:100%;display:flex}.F1KBNa_badge{box-sizing:border-box;width:100%;height:34px;color:var(--dsw-alias-label-primary);cursor:pointer;background:0 0;border:none;border-radius:12px;align-items:center;gap:8px;padding:6px 2px 6px 10px;font-family:inherit;font-size:14px;line-height:22px;display:flex;overflow:hidden}.F1KBNa_badge:hover{background:var(--dsw-alias-interactive-bg-hover)}.F1KBNa_badge[data-active]{background:var(--dsw-alias-interactive-bg-hover-solid)}.F1KBNa_badgeLabel{text-overflow:ellipsis;white-space:nowrap;min-width:0;overflow:hidden}.F1KBNa_badgeCount{color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;flex:none;margin-left:auto;margin-right:6px;font-size:12px;line-height:16px}.F1KBNa_layer.F1KBNa_rail{width:36px;height:36px;margin:8px 0 10px}.F1KBNa_rail .F1KBNa_badge{border-radius:50%;justify-content:center;gap:0;width:36px;height:36px;padding:0}.F1KBNa_rail .F1KBNa_badgeLabel{display:none}.F1KBNa_rail .F1KBNa_badgeCount{box-sizing:border-box;background:var(--dsw-alias-state-business-primary);min-width:18px;height:18px;color:var(--dsw-alias-label-primary-foreground);font-variant-numeric:tabular-nums;border-radius:9px;justify-content:center;align-items:center;padding:0 4px;font-size:11px;line-height:18px;display:flex;position:absolute;top:-3px;right:-7px}.F1KBNa_panel{z-index:30;border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-base);width:auto;max-width:none;box-shadow:var(--dsw-shadow-lv2);--dsh-scrollbar-thumb:var(--dsw-alias-scrollbar-bg-l2);--dsh-scrollbar-thumb-hover:var(--dsw-alias-scrollbar-hover-l2);border-radius:12px;flex-direction:column;display:flex;position:fixed;inset:8px 8px 128px;overflow:hidden}.F1KBNa_header{box-sizing:border-box;border-bottom:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-base);flex:none;justify-content:space-between;align-items:center;min-height:44px;padding:10px 12px;display:flex}.F1KBNa_states{flex:1;min-height:0;padding:4px 12px 12px;overflow-y:auto}.F1KBNa_split{flex:1;align-items:stretch;min-height:0;display:flex}.F1KBNa_fileList{box-sizing:border-box;border-right:1px solid var(--dsw-alias-border-l2);flex:none;width:240px;min-height:0;padding:4px 8px 12px;overflow-y:auto}.F1KBNa_resizeHandle{cursor:col-resize;background:0 0;flex:none;width:5px;margin:0 -2px}.F1KBNa_resizeHandle:hover{background:var(--dsw-alias-border-l2)}.F1KBNa_detail{flex-direction:column;flex:1;min-width:0;min-height:0;display:flex}.F1KBNa_detailEmpty{color:var(--dsw-alias-label-tertiary);text-align:center;flex:1;justify-content:center;align-items:center;margin:0;padding:24px;font-size:12px;line-height:18px;display:flex}.F1KBNa_title{color:var(--dsw-alias-label-primary);font-size:13px;font-weight:500;line-height:20px}.F1KBNa_note,.F1KBNa_readError,.F1KBNa_hint{color:var(--dsw-alias-label-tertiary);margin:4px 0;font-size:12px;line-height:18px}.F1KBNa_readError{color:var(--dsw-alias-state-error-primary)}.F1KBNa_group{color:var(--dsw-alias-label-tertiary);margin:8px 0 4px;font-size:12px;font-weight:500;line-height:16px}.F1KBNa_rows{margin:0;padding:0;list-style:none}.F1KBNa_row{border:1px solid #0000;border-radius:10px;margin:2px 0}.F1KBNa_rowHead{width:100%;color:var(--dsw-alias-label-primary);font:inherit;text-align:left;cursor:pointer;background:0 0;border:none;border-radius:10px;align-items:baseline;gap:8px;padding:6px 8px;display:flex}.F1KBNa_rowHead:hover{background:var(--dsw-alias-interactive-bg-hover-solid)}.F1KBNa_rowHead[data-selected]{background:var(--dsw-alias-interactive-bg-hover)}.F1KBNa_rowPath{text-overflow:ellipsis;white-space:nowrap;min-width:0;font:var(--dsw-font-markdown-code-block);overflow:hidden}.F1KBNa_kindTag{border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-tertiary);white-space:nowrap;border-radius:6px;flex:none;padding:1px 6px;font-size:11px;line-height:14px}.F1KBNa_kindHint{color:var(--dsw-alias-label-tertiary);flex:none;font-size:12px;line-height:16px}.F1KBNa_divergedHint,.F1KBNa_missingHint{border-bottom:1px solid var(--dsw-alias-border-l2);margin:0;padding:6px 8px;font-size:12px;line-height:18px}.F1KBNa_missing{border:1px solid var(--dsw-alias-state-warn-primary);color:var(--dsw-alias-state-warn-label,var(--dsw-alias-state-warn-primary));white-space:nowrap;border-radius:6px;flex:none;padding:1px 6px;font-size:11px;line-height:14px}.F1KBNa_missingHint{color:var(--dsw-alias-state-warn-label,var(--dsw-alias-state-warn-primary))}.F1KBNa_rowMeta{color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;flex:none;gap:6px;margin-left:auto;font-size:12px;line-height:16px;display:inline-flex}.F1KBNa_addCount{color:color-mix(in srgb, var(--dsw-alias-state-success-primary) 60%, var(--dsw-alias-label-primary))}.F1KBNa_delCount{color:color-mix(in srgb, var(--dsw-alias-state-error-primary) 60%, var(--dsw-alias-label-primary))}.F1KBNa_diff{background:var(--dsw-alias-markdown-code-block);flex-direction:column;flex:1;min-height:0;display:flex;position:relative;overflow:hidden}.F1KBNa_diffHeader{border-bottom:1px solid var(--dsw-alias-border-l2);align-items:center;gap:8px;padding:6px 8px;display:flex}.F1KBNa_diffPath{text-overflow:ellipsis;white-space:nowrap;min-width:0;color:var(--dsw-alias-label-primary);font:var(--dsw-font-markdown-code-block);flex:1;font-size:12px;line-height:18px;overflow:hidden}.F1KBNa_diffActions{border-bottom:1px solid var(--dsw-alias-border-l2);align-items:center;gap:8px;padding:6px 8px;display:flex}.F1KBNa_diffStats{color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;font-size:12px;line-height:16px}.F1KBNa_flexSpacer{flex:1}.F1KBNa_action{border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-primary);cursor:pointer;background:0 0;border-radius:8px;padding:3px 12px;font-family:inherit;font-size:12px;line-height:18px}.F1KBNa_action:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.F1KBNa_action:disabled{color:var(--dsw-alias-label-tertiary);cursor:default}.F1KBNa_iconAction{justify-content:center;align-items:center;padding:4px 6px;display:inline-flex}.F1KBNa_close{cursor:pointer;width:28px;height:28px;color:var(--dsw-alias-label-tertiary);background:0 0;border:none;border-radius:28px;justify-content:center;align-items:center;padding:0;display:inline-flex}.F1KBNa_close:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}.F1KBNa_diffBody{min-height:0;font:var(--dsw-font-markdown-code-block);flex:1;padding:8px 10px;overflow:auto}.F1KBNa_lines{border-spacing:0;width:max-content;min-width:100%;display:table}.F1KBNa_line{display:table-row}.F1KBNa_gutter{box-sizing:border-box;text-align:right;width:44px;color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;user-select:none;padding-right:10px;display:table-cell}.F1KBNa_code{white-space:pre;display:table-cell}.F1KBNa_context{color:var(--dsw-alias-label-primary)}.F1KBNa_del{background-color:color-mix(in srgb, var(--dsw-alias-state-error-primary) 12%, transparent)}.F1KBNa_add{background-color:color-mix(in srgb, var(--dsw-alias-state-success-primary) 10%, transparent)}.F1KBNa_statusBar{box-sizing:border-box;border-top:1px solid var(--dsw-alias-border-l2);height:34px;color:var(--dsw-alias-label-tertiary);font-family:var(--dsw-font-markdown-code-block);font-variant-numeric:tabular-nums;flex:none;align-items:center;padding:0 8px;font-size:12px;line-height:18px;display:flex}.F1KBNa_statusAction{border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-primary);white-space:nowrap;cursor:pointer;background:0 0;border-radius:8px;padding:3px 12px;font-family:inherit;font-size:12px;line-height:18px}.F1KBNa_statusAction:hover{background:var(--dsw-alias-interactive-bg-hover)}.F1KBNa_langSelect{border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-primary);cursor:pointer;background:0 0;border-radius:8px;flex:none;align-items:center;gap:4px;padding:3px 12px;font-family:inherit;font-size:12px;line-height:18px;display:inline-flex}.F1KBNa_langSelect:hover{background:var(--dsw-alias-interactive-bg-hover)}.F1KBNa_langLabel{text-overflow:ellipsis;white-space:nowrap;max-width:140px;overflow:hidden}";
11527
11529
  const tagId = "dsh-diff-approval/PendingPanel.module.css";
11528
11530
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
11529
11531
  const tag = document.createElement("style");
@@ -11533,49 +11535,62 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
11533
11535
  document.head.appendChild(tag);
11534
11536
  }
11535
11537
  var PendingPanel_module_css_default = {
11536
- "diffActions": "Ay7oXG_diffActions",
11537
- "rows": "Ay7oXG_rows",
11538
- "divergedHint": "Ay7oXG_divergedHint",
11539
- "badgeCount": "Ay7oXG_badgeCount",
11540
- "rail": "Ay7oXG_rail",
11541
- "kindHint": "Ay7oXG_kindHint",
11542
- "row": "Ay7oXG_row",
11543
- "add": "Ay7oXG_add",
11544
- "flexSpacer": "Ay7oXG_flexSpacer",
11545
- "group": "Ay7oXG_group",
11546
- "title": "Ay7oXG_title",
11547
- "missingHint": "Ay7oXG_missingHint",
11548
- "diffStats": "Ay7oXG_diffStats",
11549
- "toolbarButton": "Ay7oXG_toolbarButton",
11550
- "missing": "Ay7oXG_missing",
11551
- "hint": "Ay7oXG_hint",
11552
- "panel": "Ay7oXG_panel",
11553
- "rowHead": "Ay7oXG_rowHead",
11554
- "badgeLabel": "Ay7oXG_badgeLabel",
11555
- "badge": "Ay7oXG_badge",
11556
- "header": "Ay7oXG_header",
11557
- "rowPath": "Ay7oXG_rowPath",
11558
- "rowMeta": "Ay7oXG_rowMeta",
11559
- "readError": "Ay7oXG_readError",
11560
- "diff": "Ay7oXG_diff",
11561
- "footerButtons": "Ay7oXG_footerButtons",
11562
- "action": "Ay7oXG_action",
11563
- "note": "Ay7oXG_note",
11564
- "diffBody": "Ay7oXG_diffBody",
11565
- "gutter": "Ay7oXG_gutter",
11566
- "kindTag": "Ay7oXG_kindTag",
11567
- "line": "Ay7oXG_line",
11568
- "context": "Ay7oXG_context",
11569
- "lines": "Ay7oXG_lines",
11570
- "toolbar": "Ay7oXG_toolbar",
11571
- "del": "Ay7oXG_del",
11572
- "body": "Ay7oXG_body",
11573
- "layer": "Ay7oXG_layer",
11574
- "code": "Ay7oXG_code"
11538
+ "lines": "F1KBNa_lines",
11539
+ "badge": "F1KBNa_badge",
11540
+ "readError": "F1KBNa_readError",
11541
+ "hint": "F1KBNa_hint",
11542
+ "diffStats": "F1KBNa_diffStats",
11543
+ "flexSpacer": "F1KBNa_flexSpacer",
11544
+ "code": "F1KBNa_code",
11545
+ "close": "F1KBNa_close",
11546
+ "diffPath": "F1KBNa_diffPath",
11547
+ "split": "F1KBNa_split",
11548
+ "missingHint": "F1KBNa_missingHint",
11549
+ "add": "F1KBNa_add",
11550
+ "diffBody": "F1KBNa_diffBody",
11551
+ "rowHead": "F1KBNa_rowHead",
11552
+ "diffActions": "F1KBNa_diffActions",
11553
+ "kindHint": "F1KBNa_kindHint",
11554
+ "badgeLabel": "F1KBNa_badgeLabel",
11555
+ "fileList": "F1KBNa_fileList",
11556
+ "rowMeta": "F1KBNa_rowMeta",
11557
+ "del": "F1KBNa_del",
11558
+ "langSelect": "F1KBNa_langSelect",
11559
+ "langLabel": "F1KBNa_langLabel",
11560
+ "title": "F1KBNa_title",
11561
+ "context": "F1KBNa_context",
11562
+ "rows": "F1KBNa_rows",
11563
+ "divergedHint": "F1KBNa_divergedHint",
11564
+ "states": "F1KBNa_states",
11565
+ "statusAction": "F1KBNa_statusAction",
11566
+ "kindTag": "F1KBNa_kindTag",
11567
+ "footerButtons": "F1KBNa_footerButtons",
11568
+ "gutter": "F1KBNa_gutter",
11569
+ "addCount": "F1KBNa_addCount",
11570
+ "line": "F1KBNa_line",
11571
+ "statusBar": "F1KBNa_statusBar",
11572
+ "detail": "F1KBNa_detail",
11573
+ "missing": "F1KBNa_missing",
11574
+ "resizeHandle": "F1KBNa_resizeHandle",
11575
+ "diffHeader": "F1KBNa_diffHeader",
11576
+ "group": "F1KBNa_group",
11577
+ "rail": "F1KBNa_rail",
11578
+ "iconAction": "F1KBNa_iconAction",
11579
+ "header": "F1KBNa_header",
11580
+ "row": "F1KBNa_row",
11581
+ "delCount": "F1KBNa_delCount",
11582
+ "action": "F1KBNa_action",
11583
+ "detailEmpty": "F1KBNa_detailEmpty",
11584
+ "badgeCount": "F1KBNa_badgeCount",
11585
+ "layer": "F1KBNa_layer",
11586
+ "rowPath": "F1KBNa_rowPath",
11587
+ "panel": "F1KBNa_panel",
11588
+ "note": "F1KBNa_note",
11589
+ "diff": "F1KBNa_diff"
11575
11590
  };
11576
11591
  //#endregion
11577
11592
  //#region lib/types/client/PendingPanel.js
11578
- /** Sidebar-foot pending-edit review action and the whole-file diff list it opens. */
11593
+ /** Sidebar-foot pending-edit review action and the split review panel it opens. */
11579
11594
  /**
11580
11595
  * How often the panel re-reads the pending list. An external plugin cannot
11581
11596
  * register on the host's forwarded-event allowlist, so polling is its change
@@ -11583,6 +11598,29 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
11583
11598
  * even with the panel closed. The read is one small RPC per second.
11584
11599
  */
11585
11600
  const POLL_INTERVAL_MS = 1e3;
11601
+ /** Panel bottom offset when the composer cannot be measured, in px. */
11602
+ const FALLBACK_BOTTOM_PX = 128;
11603
+ /** Gap kept between the composer's top edge and the panel bottom, in px. */
11604
+ const COMPOSER_GAP_PX = 12;
11605
+ /** The harness composer seat (conversation scroll body + seat div). */
11606
+ const SCROLL_SELECTOR = "[data-conversation-scroll]";
11607
+ const SEAT_SELECTOR = "[data-composer-seat]";
11608
+ /** Seat height ui-conversation publishes for floating controls (its own seat observer). */
11609
+ const COMPOSER_HEIGHT_VAR = "--dsh-composer-height";
11610
+ /** Interactive composer/approval cards; clicking these keeps the panel open.
11611
+ Deliberately the cards themselves, not the seat: the approval frame's wide
11612
+ side gutters are blank space, so a click there must still close. */
11613
+ const KEEP_OPEN_SELECTOR = "[data-composer-card],[data-question-key] > *,[data-plan-review-key] > *";
11614
+ /** Seat counts as docked when its bottom is this close to the window bottom. */
11615
+ const DOCKED_TOLERANCE_PX = 48;
11616
+ /** File-list pane width bounds for the manual split drag, in px. */
11617
+ const MIN_LIST_WIDTH_PX = 160;
11618
+ const MAX_LIST_WIDTH_PX = 560;
11619
+ /** The trailing file-name segment of a path, used for row display. */
11620
+ function basenameOf(path) {
11621
+ const index = Math.max(path.lastIndexOf("/"), path.lastIndexOf("\\"));
11622
+ return index < 0 ? path : path.slice(index + 1);
11623
+ }
11586
11624
  /** The diff body's row class per line kind. */
11587
11625
  const ROW_CLASS = {
11588
11626
  context: PendingPanel_module_css_default.context,
@@ -11610,19 +11648,144 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
11610
11648
  });
11611
11649
  return blocks;
11612
11650
  }
11613
- function PendingRow({ file, files, selected, busy, t, onSelect, onKeep, onRevert }) {
11614
- const lang = (0, react.useMemo)(() => langFromPath(file.path), [file.path]);
11651
+ /** The diff-row index containing a node, or undefined. */
11652
+ function rowIndexAt(node) {
11653
+ if (node === null) return void 0;
11654
+ const row = (node instanceof Element ? node : node.parentElement)?.closest("[data-diff-row]");
11655
+ if (row === null || row === void 0) return void 0;
11656
+ const index = Number(row.dataset.diffRow);
11657
+ return Number.isFinite(index) ? index : void 0;
11658
+ }
11659
+ /** Character offset of a selection boundary within its line's code text. */
11660
+ function lineOffsetAt(node, offset) {
11661
+ const code = (node instanceof Element ? node : node.parentElement)?.closest("[data-diff-code]");
11662
+ if (code === null || code === void 0) return 0;
11663
+ let before = 0;
11664
+ const walker = document.createTreeWalker(code, NodeFilter.SHOW_TEXT);
11665
+ let current = walker.nextNode();
11666
+ while (current !== null) {
11667
+ if (current === node) return before + offset;
11668
+ if (node instanceof Element && node.contains(current)) break;
11669
+ before += current.length;
11670
+ current = walker.nextNode();
11671
+ }
11672
+ if (node instanceof Element) {
11673
+ const children = [...node.childNodes];
11674
+ for (let i = 0; i < Math.min(offset, children.length); i++) {
11675
+ const inner = document.createTreeWalker(children[i], NodeFilter.SHOW_TEXT);
11676
+ let text = inner.nextNode();
11677
+ while (text !== null) {
11678
+ before += text.length;
11679
+ text = inner.nextNode();
11680
+ }
11681
+ }
11682
+ }
11683
+ return before;
11684
+ }
11685
+ /** Length of the code text on the line holding a node. */
11686
+ function lineLengthAt(node) {
11687
+ return ((node instanceof Element ? node : node.parentElement)?.closest("[data-diff-code]"))?.textContent?.length ?? 0;
11688
+ }
11689
+ /**
11690
+ * Derive the selected diff-row range from a native text selection. A
11691
+ * boundary sitting exactly at a line edge contributes no content: a start at
11692
+ * the line's end skips to the next line, an end at the line's start falls
11693
+ * back to the previous line.
11694
+ */
11695
+ function rowRangeOf(selection) {
11696
+ if (selection === null || selection.isCollapsed || selection.rangeCount === 0) return void 0;
11697
+ const range = selection.getRangeAt(0);
11698
+ let start = rowIndexAt(range.startContainer);
11699
+ let end = rowIndexAt(range.endContainer);
11700
+ if (start === void 0 || end === void 0) return void 0;
11701
+ if (lineOffsetAt(range.startContainer, range.startOffset) >= lineLengthAt(range.startContainer)) start += 1;
11702
+ if (lineOffsetAt(range.endContainer, range.endOffset) === 0) end -= 1;
11703
+ if (start > end) return void 0;
11704
+ return {
11705
+ start,
11706
+ end
11707
+ };
11708
+ }
11709
+ /** One row of the file list: the clickable head in the left pane. */
11710
+ function PendingFileRow({ file, selected, t, onSelect }) {
11711
+ const stats = (0, react.useMemo)(() => computeWholeFileDiff(file.oldText, file.newText), [file.oldText, file.newText]);
11712
+ return (0, react_jsx_runtime.jsx)("li", {
11713
+ className: PendingPanel_module_css_default.row,
11714
+ children: (0, react_jsx_runtime.jsxs)("button", {
11715
+ type: "button",
11716
+ className: PendingPanel_module_css_default.rowHead,
11717
+ "data-selected": selected || void 0,
11718
+ onClick: () => {
11719
+ onSelect(file.id);
11720
+ },
11721
+ children: [
11722
+ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
11723
+ label: file.path,
11724
+ delayMs: 500,
11725
+ maxWidth: 560,
11726
+ children: (0, react_jsx_runtime.jsx)("span", {
11727
+ className: PendingPanel_module_css_default.rowPath,
11728
+ children: basenameOf(file.path)
11729
+ })
11730
+ }),
11731
+ file.kind === "create" && (0, react_jsx_runtime.jsx)("span", {
11732
+ className: PendingPanel_module_css_default.kindTag,
11733
+ children: t("row.create")
11734
+ }),
11735
+ file.missing && (0, react_jsx_runtime.jsx)("span", {
11736
+ className: PendingPanel_module_css_default.missing,
11737
+ title: t("panel.missingHint"),
11738
+ children: t("panel.missing")
11739
+ }),
11740
+ (0, react_jsx_runtime.jsxs)("span", {
11741
+ className: PendingPanel_module_css_default.rowMeta,
11742
+ children: [(0, react_jsx_runtime.jsx)("span", {
11743
+ className: PendingPanel_module_css_default.addCount,
11744
+ children: t("row.added", { added: stats.added })
11745
+ }), (0, react_jsx_runtime.jsx)("span", {
11746
+ className: PendingPanel_module_css_default.delCount,
11747
+ children: t("row.removed", { removed: stats.removed })
11748
+ })]
11749
+ })
11750
+ ]
11751
+ })
11752
+ });
11753
+ }
11754
+ /** The selected file's diff, actions, jump controls, and copy toolbar. */
11755
+ function PendingDiff({ file, files, busy, t, onKeep, onRevert, onOpen }) {
11756
+ const [langOverride, setLangOverride] = (0, react.useState)(void 0);
11757
+ const [langMenuOpen, setLangMenuOpen] = (0, react.useState)(false);
11758
+ const langMenuItems = (0, react.useMemo)(() => [{
11759
+ id: "",
11760
+ label: t("action.langAuto")
11761
+ }, ...HIGHLIGHT_LANGS.map((language) => ({
11762
+ id: language,
11763
+ label: language
11764
+ }))], [t]);
11765
+ const detectedLang = (0, react.useMemo)(() => langFromPath(file.path), [file.path]);
11766
+ const lang = (0, react.useMemo)(() => langOverride ?? detectedLang, [detectedLang, langOverride]);
11767
+ const langLabel = langOverride ?? (detectedLang === void 0 ? t("action.langAuto") : t("action.langAutoDetected", { lang: detectedLang }));
11615
11768
  const model = (0, react.useMemo)(() => {
11616
- if (!selected) return void 0;
11617
11769
  const diff = computeWholeFileDiff(file.oldText, file.newText);
11618
11770
  return {
11619
11771
  diff,
11620
- blocks: changeBlocksOf(diff),
11621
- oldRuns: highlightLines(file.oldText, lang),
11622
- newRuns: highlightLines(file.newText, lang)
11772
+ blocks: changeBlocksOf(diff)
11773
+ };
11774
+ }, [file.oldText, file.newText]);
11775
+ const [runs, setRuns] = (0, react.useState)(void 0);
11776
+ (0, react.useEffect)(() => {
11777
+ setRuns(void 0);
11778
+ const timer = window.setTimeout(() => {
11779
+ setRuns({
11780
+ oldRuns: highlightLines(file.oldText, lang),
11781
+ newRuns: highlightLines(file.newText, lang)
11782
+ });
11783
+ }, 0);
11784
+ return () => {
11785
+ window.clearTimeout(timer);
11623
11786
  };
11624
11787
  }, [
11625
- selected,
11788
+ file.id,
11626
11789
  file.oldText,
11627
11790
  file.newText,
11628
11791
  lang
@@ -11630,24 +11793,24 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
11630
11793
  const rowRefs = (0, react.useRef)(/* @__PURE__ */ new Map());
11631
11794
  const bodyRef = (0, react.useRef)(null);
11632
11795
  const [focus, setFocus] = (0, react.useState)(0);
11633
- const [drag, setDrag] = (0, react.useState)();
11634
- const [range, setRange] = (0, react.useState)();
11796
+ const [selection, setSelection] = (0, react.useState)(void 0);
11635
11797
  const [copied, setCopied] = (0, react.useState)(false);
11636
11798
  (0, react.useEffect)(() => {
11637
11799
  setFocus(0);
11638
- setDrag(void 0);
11639
- setRange(void 0);
11800
+ setSelection(void 0);
11801
+ setLangOverride(void 0);
11802
+ setLangMenuOpen(false);
11640
11803
  setCopied(false);
11641
- }, [file.id, selected]);
11804
+ }, [file.id]);
11642
11805
  (0, react.useEffect)(() => {
11643
- if (model === void 0 || model.blocks.length === 0) return;
11806
+ if (model.blocks.length === 0) return;
11644
11807
  const block = model.blocks[focus];
11645
11808
  if (block === void 0) return;
11646
11809
  rowRefs.current.get(block.start)?.scrollIntoView({ block: "center" });
11647
11810
  }, [model, focus]);
11648
11811
  const jump = (direction) => {
11649
11812
  setFocus((current) => {
11650
- if (model === void 0 || model.blocks.length === 0) return current;
11813
+ if (model.blocks.length === 0) return current;
11651
11814
  if (direction === -1) return (current - 1 + model.blocks.length) % model.blocks.length;
11652
11815
  const bodyRect = bodyRef.current?.getBoundingClientRect();
11653
11816
  for (let index = current + 1; index < model.blocks.length; index++) {
@@ -11663,220 +11826,251 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
11663
11826
  if (element === null) rowRefs.current.delete(index);
11664
11827
  else rowRefs.current.set(index, element);
11665
11828
  };
11666
- const rowMouseDown = (index) => (event) => {
11667
- if (event.button !== 0) return;
11668
- event.preventDefault();
11669
- setDrag({
11670
- anchor: index,
11671
- end: index
11672
- });
11673
- setRange({
11674
- start: index,
11675
- end: index
11676
- });
11677
- };
11678
- const rowMouseEnter = (index) => () => {
11679
- setDrag((current) => {
11680
- if (current === void 0) return current;
11681
- const next = {
11682
- ...current,
11683
- end: index
11684
- };
11685
- setRange({
11686
- start: Math.min(next.anchor, next.end),
11687
- end: Math.max(next.anchor, next.end)
11688
- });
11689
- return next;
11690
- });
11691
- };
11692
- const endDrag = () => {
11693
- setDrag((current) => {
11694
- if (current === void 0) return void 0;
11695
- setRange({
11696
- start: Math.min(current.anchor, current.end),
11697
- end: Math.max(current.anchor, current.end)
11698
- });
11699
- });
11700
- };
11701
- const selection = drag === void 0 && range !== void 0 && model !== void 0 ? range : void 0;
11702
- const selectionTop = (() => {
11703
- if (selection === void 0) return 0;
11704
- const row = rowRefs.current.get(selection.start);
11705
- const body = bodyRef.current;
11706
- if (row === void 0 || body === null) return 0;
11707
- const bodyTop = body.offsetTop;
11708
- const rowTop = bodyTop + row.getBoundingClientRect().top - body.getBoundingClientRect().top;
11709
- const clamped = Math.max(rowTop - 36, bodyTop);
11710
- return Math.min(clamped, Math.max(bodyTop, bodyTop + body.clientHeight - 36));
11711
- })();
11712
- const copySelection = async () => {
11713
- if (selection === void 0 || model === void 0) return;
11829
+ (0, react.useEffect)(() => {
11830
+ const update = () => setSelection(rowRangeOf(window.getSelection()));
11831
+ document.addEventListener("selectionchange", update);
11832
+ update();
11833
+ return () => {
11834
+ document.removeEventListener("selectionchange", update);
11835
+ };
11836
+ }, [file.id]);
11837
+ const selectionReference = (() => {
11838
+ if (selection === void 0) return void 0;
11714
11839
  const lineNumbers = model.diff.rows.slice(selection.start, selection.end + 1).map((row) => row.newLine ?? row.oldLine).filter((number) => number !== void 0);
11715
- if (lineNumbers.length === 0) return;
11716
- if (!await (0, _deepseek_ai_dsh_client_ui_primitives.writeClipboard)(referenceOf(file.path, files, Math.min(...lineNumbers), Math.max(...lineNumbers)))) return;
11840
+ if (lineNumbers.length === 0) return void 0;
11841
+ return referenceOf(file.path, files, Math.min(...lineNumbers), Math.max(...lineNumbers));
11842
+ })();
11843
+ const copySelection = (0, react.useCallback)(async () => {
11844
+ if (selectionReference === void 0) return;
11845
+ if (!await (0, _deepseek_ai_dsh_client_ui_primitives.writeClipboard)(selectionReference)) return;
11717
11846
  setCopied(true);
11718
11847
  window.setTimeout(() => {
11719
11848
  setCopied(false);
11720
11849
  }, 1500);
11721
- };
11850
+ }, [selectionReference]);
11851
+ (0, react.useEffect)(() => {
11852
+ const onKeyDown = (event) => {
11853
+ if (!(event.ctrlKey || event.metaKey) || event.altKey || event.shiftKey) return;
11854
+ if (event.key.toLowerCase() !== "l") return;
11855
+ if (selectionReference === void 0) return;
11856
+ event.preventDefault();
11857
+ copySelection();
11858
+ };
11859
+ document.addEventListener("keydown", onKeyDown);
11860
+ return () => {
11861
+ document.removeEventListener("keydown", onKeyDown);
11862
+ };
11863
+ }, [copySelection]);
11722
11864
  /** One line's text: token spans when highlighted, plain text otherwise. */
11723
11865
  const renderLine = (row) => {
11724
11866
  const lineNumber = row.kind === "del" ? row.oldLine : row.newLine;
11725
- const runs = model === void 0 || lineNumber === void 0 ? void 0 : (row.kind === "del" ? model.oldRuns : model.newRuns)?.[lineNumber - 1];
11726
- if (runs === void 0 || runs.length === 0) return row.text === "" ? "\xA0" : row.text;
11727
- return runs.map((span, index) => (0, react_jsx_runtime.jsx)("span", {
11867
+ const sideRuns = row.kind === "del" ? runs?.oldRuns : runs?.newRuns;
11868
+ const lineRuns = lineNumber === void 0 ? void 0 : sideRuns?.[lineNumber - 1];
11869
+ if (lineRuns === void 0 || lineRuns.length === 0) return row.text === "" ? "\xA0" : row.text;
11870
+ return lineRuns.map((span, index) => (0, react_jsx_runtime.jsx)("span", {
11728
11871
  style: span.style,
11729
11872
  children: span.text
11730
11873
  }, index));
11731
11874
  };
11732
- const focusedBlock = model !== void 0 && model.blocks.length > 0 ? model.blocks[focus] : void 0;
11875
+ const focusedBlock = model.blocks.length > 0 ? model.blocks[focus] : void 0;
11733
11876
  const inFocusedBlock = (index) => focusedBlock !== void 0 && index >= focusedBlock.start && index <= focusedBlock.end;
11734
- return (0, react_jsx_runtime.jsxs)("li", {
11735
- className: PendingPanel_module_css_default.row,
11736
- children: [(0, react_jsx_runtime.jsxs)("button", {
11737
- type: "button",
11738
- className: PendingPanel_module_css_default.rowHead,
11739
- "data-selected": selected || void 0,
11740
- "aria-expanded": selected,
11741
- onClick: () => {
11742
- onSelect(selected ? "" : file.id);
11743
- },
11744
- children: [
11745
- (0, react_jsx_runtime.jsx)("span", {
11746
- className: PendingPanel_module_css_default.rowPath,
11747
- title: file.path,
11748
- children: copyDisplayPath(file.path, files)
11749
- }),
11750
- file.kind === "create" && (0, react_jsx_runtime.jsx)("span", {
11751
- className: PendingPanel_module_css_default.kindTag,
11752
- children: t("row.create")
11753
- }),
11754
- file.missing && (0, react_jsx_runtime.jsx)("span", {
11755
- className: PendingPanel_module_css_default.missing,
11756
- title: t("panel.missingHint"),
11757
- children: t("panel.missing")
11758
- })
11759
- ]
11760
- }), selected && model !== void 0 && (0, react_jsx_runtime.jsxs)("div", {
11761
- className: PendingPanel_module_css_default.diff,
11762
- "data-diff-approval-diff": true,
11763
- onMouseUp: endDrag,
11764
- children: [
11765
- (0, react_jsx_runtime.jsxs)("div", {
11766
- className: PendingPanel_module_css_default.diffActions,
11767
- children: [
11768
- (0, react_jsx_runtime.jsx)("span", {
11769
- className: PendingPanel_module_css_default.diffStats,
11770
- children: t("panel.stats", {
11771
- added: model.diff.added,
11772
- removed: model.diff.removed
11877
+ return (0, react_jsx_runtime.jsxs)("div", {
11878
+ className: PendingPanel_module_css_default.diff,
11879
+ "data-diff-approval-diff": true,
11880
+ children: [
11881
+ (0, react_jsx_runtime.jsxs)("div", {
11882
+ className: PendingPanel_module_css_default.diffHeader,
11883
+ children: [
11884
+ (0, react_jsx_runtime.jsx)("span", {
11885
+ className: PendingPanel_module_css_default.diffPath,
11886
+ children: file.path
11887
+ }),
11888
+ (0, react_jsx_runtime.jsx)("button", {
11889
+ type: "button",
11890
+ className: `${PendingPanel_module_css_default.action} ${PendingPanel_module_css_default.iconAction}`,
11891
+ "data-diff-open": true,
11892
+ "aria-label": t("action.openFile"),
11893
+ title: t("action.openFile"),
11894
+ onClick: () => {
11895
+ onOpen(file.sessionId, file.id, "open");
11896
+ },
11897
+ children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconBrowseOutline16, { size: 14 })
11898
+ }),
11899
+ (0, react_jsx_runtime.jsx)("button", {
11900
+ type: "button",
11901
+ className: `${PendingPanel_module_css_default.action} ${PendingPanel_module_css_default.iconAction}`,
11902
+ "data-diff-reveal": true,
11903
+ "aria-label": t("action.revealFile"),
11904
+ title: t("action.revealFile"),
11905
+ onClick: () => {
11906
+ onOpen(file.sessionId, file.id, "reveal");
11907
+ },
11908
+ children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconFolderOpenOutline16, { size: 14 })
11909
+ })
11910
+ ]
11911
+ }),
11912
+ (0, react_jsx_runtime.jsxs)("div", {
11913
+ className: PendingPanel_module_css_default.diffActions,
11914
+ children: [
11915
+ (0, react_jsx_runtime.jsx)("span", {
11916
+ className: PendingPanel_module_css_default.diffStats,
11917
+ children: t("panel.stats", {
11918
+ added: model.diff.added,
11919
+ removed: model.diff.removed
11920
+ })
11921
+ }),
11922
+ file.kind === "create" && (0, react_jsx_runtime.jsx)("span", {
11923
+ className: PendingPanel_module_css_default.kindHint,
11924
+ children: t("panel.createHint")
11925
+ }),
11926
+ model.blocks.length > 0 && (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)("button", {
11927
+ type: "button",
11928
+ className: `${PendingPanel_module_css_default.action} ${PendingPanel_module_css_default.iconAction}`,
11929
+ "data-diff-prev": true,
11930
+ "aria-label": t("action.prevDiff"),
11931
+ title: t("action.prevDiff"),
11932
+ disabled: busy,
11933
+ onClick: () => {
11934
+ jump(-1);
11935
+ },
11936
+ children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronUpOutline14, { size: 14 })
11937
+ }), (0, react_jsx_runtime.jsx)("button", {
11938
+ type: "button",
11939
+ className: `${PendingPanel_module_css_default.action} ${PendingPanel_module_css_default.iconAction}`,
11940
+ "data-diff-next": true,
11941
+ "aria-label": t("action.nextDiff"),
11942
+ title: t("action.nextDiff"),
11943
+ disabled: busy,
11944
+ onClick: () => {
11945
+ jump(1);
11946
+ },
11947
+ children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, { size: 14 })
11948
+ })] }),
11949
+ (0, react_jsx_runtime.jsx)("span", { className: PendingPanel_module_css_default.flexSpacer }),
11950
+ (0, react_jsx_runtime.jsx)("button", {
11951
+ type: "button",
11952
+ className: PendingPanel_module_css_default.action,
11953
+ "data-diff-keep": true,
11954
+ disabled: busy,
11955
+ onClick: () => {
11956
+ onKeep(file.sessionId, file.id);
11957
+ },
11958
+ children: busy ? t("action.busy") : t("action.keep")
11959
+ }),
11960
+ (0, react_jsx_runtime.jsx)("button", {
11961
+ type: "button",
11962
+ className: PendingPanel_module_css_default.action,
11963
+ "data-diff-revert": true,
11964
+ disabled: busy,
11965
+ onClick: () => {
11966
+ onRevert(file.sessionId, file.id);
11967
+ },
11968
+ children: busy ? t("action.busy") : t("action.revert")
11969
+ })
11970
+ ]
11971
+ }),
11972
+ file.missing && (0, react_jsx_runtime.jsx)("p", {
11973
+ className: PendingPanel_module_css_default.missingHint,
11974
+ children: t("panel.missingHint")
11975
+ }),
11976
+ (0, react_jsx_runtime.jsx)("div", {
11977
+ className: PendingPanel_module_css_default.diffBody,
11978
+ ref: bodyRef,
11979
+ children: (0, react_jsx_runtime.jsx)("div", {
11980
+ className: PendingPanel_module_css_default.lines,
11981
+ children: model.diff.rows.map((row, index) => (0, react_jsx_runtime.jsxs)("div", {
11982
+ ref: registerRow(index),
11983
+ className: `${PendingPanel_module_css_default.line} ${ROW_CLASS[row.kind]}`,
11984
+ "data-diff-line": row.kind,
11985
+ "data-diff-row": index,
11986
+ "data-diff-focused": inFocusedBlock(index) ? "" : void 0,
11987
+ children: [
11988
+ (0, react_jsx_runtime.jsx)("span", {
11989
+ className: PendingPanel_module_css_default.gutter,
11990
+ children: row.oldLine ?? ""
11991
+ }),
11992
+ (0, react_jsx_runtime.jsx)("span", {
11993
+ className: PendingPanel_module_css_default.gutter,
11994
+ children: row.newLine ?? ""
11995
+ }),
11996
+ (0, react_jsx_runtime.jsx)("span", {
11997
+ className: PendingPanel_module_css_default.code,
11998
+ "data-diff-code": true,
11999
+ children: renderLine(row)
11773
12000
  })
11774
- }),
11775
- file.kind === "create" && (0, react_jsx_runtime.jsx)("span", {
11776
- className: PendingPanel_module_css_default.kindHint,
11777
- children: t("panel.createHint")
11778
- }),
11779
- model.blocks.length > 0 && (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)("button", {
11780
- type: "button",
11781
- className: PendingPanel_module_css_default.action,
11782
- "data-diff-prev": true,
11783
- disabled: busy,
11784
- onClick: () => {
11785
- jump(-1);
11786
- },
11787
- children: t("action.prevDiff")
11788
- }), (0, react_jsx_runtime.jsx)("button", {
12001
+ ]
12002
+ }, index))
12003
+ })
12004
+ }),
12005
+ (0, react_jsx_runtime.jsxs)("div", {
12006
+ className: PendingPanel_module_css_default.statusBar,
12007
+ "data-diff-status-bar": true,
12008
+ children: [
12009
+ selectionReference === void 0 ? null : (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
12010
+ label: copied ? t("action.copied") : `${t("action.copyHint")} (Ctrl+L)`,
12011
+ side: "top",
12012
+ delayMs: 300,
12013
+ children: (0, react_jsx_runtime.jsx)("button", {
11789
12014
  type: "button",
11790
- className: PendingPanel_module_css_default.action,
11791
- "data-diff-next": true,
11792
- disabled: busy,
11793
- onClick: () => {
11794
- jump(1);
12015
+ className: PendingPanel_module_css_default.statusAction,
12016
+ "data-diff-copy": true,
12017
+ onMouseDown: (event) => {
12018
+ event.preventDefault();
11795
12019
  },
11796
- children: t("action.nextDiff")
11797
- })] }),
11798
- (0, react_jsx_runtime.jsx)("span", { className: PendingPanel_module_css_default.flexSpacer }),
11799
- (0, react_jsx_runtime.jsx)("button", {
11800
- type: "button",
11801
- className: PendingPanel_module_css_default.action,
11802
- "data-diff-keep": true,
11803
- disabled: busy,
11804
12020
  onClick: () => {
11805
- onKeep(file.sessionId, file.id);
12021
+ copySelection();
11806
12022
  },
11807
- children: busy ? t("action.busy") : t("action.keep")
11808
- }),
11809
- (0, react_jsx_runtime.jsx)("button", {
12023
+ children: copied ? t("action.copied") : selectionReference
12024
+ })
12025
+ }),
12026
+ (0, react_jsx_runtime.jsx)("span", { className: PendingPanel_module_css_default.flexSpacer }),
12027
+ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Menu, {
12028
+ open: langMenuOpen,
12029
+ portal: true,
12030
+ compact: true,
12031
+ align: "end",
12032
+ items: langMenuItems,
12033
+ selectedId: langOverride ?? "",
12034
+ onSelect: (id) => {
12035
+ setLangOverride(id === "" ? void 0 : id);
12036
+ setLangMenuOpen(false);
12037
+ },
12038
+ onClose: () => {
12039
+ setLangMenuOpen(false);
12040
+ },
12041
+ anchor: (0, react_jsx_runtime.jsxs)("button", {
11810
12042
  type: "button",
11811
- className: PendingPanel_module_css_default.action,
11812
- "data-diff-revert": true,
11813
- disabled: busy,
12043
+ className: PendingPanel_module_css_default.langSelect,
12044
+ "data-diff-lang": true,
12045
+ "aria-label": t("action.langSelect"),
12046
+ title: t("action.langSelect"),
11814
12047
  onClick: () => {
11815
- onRevert(file.sessionId, file.id);
12048
+ setLangMenuOpen((value) => !value);
11816
12049
  },
11817
- children: busy ? t("action.busy") : t("action.revert")
12050
+ children: [(0, react_jsx_runtime.jsx)("span", {
12051
+ className: PendingPanel_module_css_default.langLabel,
12052
+ children: langLabel
12053
+ }), (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, { size: 12 })]
11818
12054
  })
11819
- ]
11820
- }),
11821
- file.missing && (0, react_jsx_runtime.jsx)("p", {
11822
- className: PendingPanel_module_css_default.missingHint,
11823
- children: t("panel.missingHint")
11824
- }),
11825
- (0, react_jsx_runtime.jsx)("div", {
11826
- className: PendingPanel_module_css_default.diffBody,
11827
- ref: bodyRef,
11828
- children: (0, react_jsx_runtime.jsx)("div", {
11829
- className: PendingPanel_module_css_default.lines,
11830
- children: model.diff.rows.map((row, index) => (0, react_jsx_runtime.jsxs)("div", {
11831
- ref: registerRow(index),
11832
- className: `${PendingPanel_module_css_default.line} ${ROW_CLASS[row.kind]}`,
11833
- "data-diff-line": row.kind,
11834
- "data-diff-focused": inFocusedBlock(index) ? "" : void 0,
11835
- onMouseDown: rowMouseDown(index),
11836
- onMouseEnter: rowMouseEnter(index),
11837
- children: [
11838
- (0, react_jsx_runtime.jsx)("span", {
11839
- className: PendingPanel_module_css_default.gutter,
11840
- children: row.oldLine ?? ""
11841
- }),
11842
- (0, react_jsx_runtime.jsx)("span", {
11843
- className: PendingPanel_module_css_default.gutter,
11844
- children: row.newLine ?? ""
11845
- }),
11846
- (0, react_jsx_runtime.jsx)("span", {
11847
- className: PendingPanel_module_css_default.code,
11848
- children: renderLine(row)
11849
- })
11850
- ]
11851
- }, index))
11852
12055
  })
11853
- }),
11854
- selection !== void 0 && (0, react_jsx_runtime.jsx)("div", {
11855
- className: PendingPanel_module_css_default.toolbar,
11856
- style: { top: selectionTop },
11857
- "data-diff-selection-toolbar": true,
11858
- children: (0, react_jsx_runtime.jsx)("button", {
11859
- type: "button",
11860
- className: PendingPanel_module_css_default.toolbarButton,
11861
- "data-diff-copy": true,
11862
- onClick: () => {
11863
- copySelection();
11864
- },
11865
- children: copied ? t("action.copied") : t("action.copyRange")
11866
- })
11867
- })
11868
- ]
11869
- })]
12056
+ ]
12057
+ })
12058
+ ]
11870
12059
  });
11871
12060
  }
11872
12061
  /** Render the pending-edit review panel and its unified footer action. */
11873
- function PendingPanel({ wide, useSessions, usePending, onRefresh, onKeep, onRevert, t }) {
12062
+ function PendingPanel({ wide, useSessions, usePending, onRefresh, onKeep, onRevert, onOpen, t }) {
11874
12063
  const current = useSessions((state) => state.current);
11875
12064
  const snapshot = usePending((snapshot) => snapshot);
11876
12065
  const [open, setOpen] = (0, react.useState)(false);
11877
12066
  const [selected, setSelected] = (0, react.useState)("");
11878
12067
  /** Pending count captured at open time; auto-close needs a list that emptied. */
11879
12068
  const [openedCount, setOpenedCount] = (0, react.useState)(0);
12069
+ /** Bottom offset tracking the chat composer's top edge so the input stays visible. */
12070
+ const [bottomPx, setBottomPx] = (0, react.useState)(FALLBACK_BOTTOM_PX);
12071
+ /** File-list pane width, adjustable by dragging the divider. */
12072
+ const [listWidth, setListWidth] = (0, react.useState)(240);
12073
+ const resizeDrag = (0, react.useRef)(null);
11880
12074
  (0, react.useEffect)(() => {
11881
12075
  onRefresh(current);
11882
12076
  const timer = setInterval(() => {
@@ -11886,9 +12080,63 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
11886
12080
  clearInterval(timer);
11887
12081
  };
11888
12082
  }, [current, onRefresh]);
12083
+ (0, react.useEffect)(() => {
12084
+ if (!open) return;
12085
+ const MEASURE_INTERVAL_MS = 400;
12086
+ const measure = () => {
12087
+ const scrollers = document.querySelectorAll(SCROLL_SELECTOR);
12088
+ for (const scroller of scrollers) {
12089
+ const seat = scroller.querySelector(SEAT_SELECTOR);
12090
+ if (seat === null) continue;
12091
+ const seatRect = seat.getBoundingClientRect();
12092
+ if (!(seatRect.bottom >= window.innerHeight - DOCKED_TOLERANCE_PX && seatRect.top > 0 && seatRect.top < window.innerHeight)) continue;
12093
+ const height = Number.parseFloat(scroller.style.getPropertyValue(COMPOSER_HEIGHT_VAR));
12094
+ const clearance = Number.isFinite(height) && height > 0 ? height : window.innerHeight - seatRect.top;
12095
+ setBottomPx(Math.round(clearance) + COMPOSER_GAP_PX);
12096
+ return;
12097
+ }
12098
+ setBottomPx(FALLBACK_BOTTOM_PX);
12099
+ };
12100
+ measure();
12101
+ const seats = [...document.querySelectorAll(SEAT_SELECTOR)];
12102
+ const observer = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(measure);
12103
+ for (const seat of seats) observer?.observe(seat);
12104
+ const timer = window.setInterval(measure, MEASURE_INTERVAL_MS);
12105
+ window.addEventListener("resize", measure);
12106
+ return () => {
12107
+ observer?.disconnect();
12108
+ window.clearInterval(timer);
12109
+ window.removeEventListener("resize", measure);
12110
+ };
12111
+ }, [open]);
12112
+ (0, react.useEffect)(() => {
12113
+ if (!open) return;
12114
+ const onPointerDown = (event) => {
12115
+ const target = event.target;
12116
+ if (target === null) return;
12117
+ const element = target instanceof Element ? target : target.parentElement;
12118
+ if (element !== null && (element.closest("[data-diff-approval-panel]") !== null || element.closest("[data-diff-approval-badge]") !== null || element.closest(KEEP_OPEN_SELECTOR) !== null)) return;
12119
+ setOpen(false);
12120
+ };
12121
+ document.addEventListener("pointerdown", onPointerDown, true);
12122
+ return () => {
12123
+ document.removeEventListener("pointerdown", onPointerDown, true);
12124
+ };
12125
+ }, [open]);
11889
12126
  const files = snapshot.files;
11890
12127
  const mine = files.filter((file) => file.sessionId === current);
11891
12128
  const theirs = files.filter((file) => file.sessionId !== current);
12129
+ (0, react.useEffect)(() => {
12130
+ if (!open) return;
12131
+ if (selected !== "" && files.some((file) => file.id === selected)) return;
12132
+ const next = [...mine, ...theirs][0];
12133
+ if (next !== void 0 && next.id !== selected) setSelected(next.id);
12134
+ }, [
12135
+ open,
12136
+ current,
12137
+ files,
12138
+ selected
12139
+ ]);
11892
12140
  (0, react.useEffect)(() => {
11893
12141
  if (open && openedCount > 0 && snapshot.read && snapshot.error === void 0 && files.length === 0) {
11894
12142
  setOpenedCount(0);
@@ -11905,30 +12153,64 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
11905
12153
  if (!open) setOpenedCount(files.length);
11906
12154
  setOpen((value) => !value);
11907
12155
  };
11908
- const renderEntry = (entry) => (0, react_jsx_runtime.jsx)(PendingRow, {
12156
+ const renderEntry = (entry) => (0, react_jsx_runtime.jsx)(PendingFileRow, {
11909
12157
  file: entry,
11910
- files,
11911
12158
  selected: selected === entry.id,
11912
- busy: snapshot.busy.has(entry.id),
11913
12159
  t,
11914
- onSelect: setSelected,
11915
- onKeep,
11916
- onRevert
12160
+ onSelect: setSelected
11917
12161
  }, entry.id);
12162
+ const selectedFile = files.find((file) => file.id === selected);
12163
+ /** Drag the list/detail divider; width follows the pointer within its bounds. */
12164
+ const startResize = (event) => {
12165
+ if (event.button !== 0) return;
12166
+ event.preventDefault();
12167
+ resizeDrag.current = {
12168
+ startX: event.clientX,
12169
+ startWidth: listWidth
12170
+ };
12171
+ document.body.style.userSelect = "none";
12172
+ document.body.style.cursor = "col-resize";
12173
+ const onMove = (move) => {
12174
+ const start = resizeDrag.current;
12175
+ if (start === null) return;
12176
+ const next = start.startWidth + (move.clientX - start.startX);
12177
+ setListWidth(Math.min(Math.max(next, MIN_LIST_WIDTH_PX), MAX_LIST_WIDTH_PX));
12178
+ };
12179
+ const onUp = () => {
12180
+ resizeDrag.current = null;
12181
+ document.body.style.userSelect = "";
12182
+ document.body.style.cursor = "";
12183
+ window.removeEventListener("mousemove", onMove);
12184
+ window.removeEventListener("mouseup", onUp);
12185
+ };
12186
+ window.addEventListener("mousemove", onMove);
12187
+ window.addEventListener("mouseup", onUp);
12188
+ };
11918
12189
  return (0, react_jsx_runtime.jsxs)("div", {
11919
12190
  className: wide ? PendingPanel_module_css_default.layer : `${PendingPanel_module_css_default.layer} ${PendingPanel_module_css_default.rail}`,
11920
12191
  children: [open && (0, react_jsx_runtime.jsxs)("section", {
11921
12192
  className: PendingPanel_module_css_default.panel,
12193
+ style: { bottom: bottomPx },
11922
12194
  "data-diff-approval-panel": true,
11923
12195
  "aria-label": t("panel.title"),
11924
- children: [(0, react_jsx_runtime.jsx)("header", {
12196
+ children: [(0, react_jsx_runtime.jsxs)("header", {
11925
12197
  className: PendingPanel_module_css_default.header,
11926
- children: (0, react_jsx_runtime.jsx)("span", {
12198
+ children: [(0, react_jsx_runtime.jsx)("span", {
11927
12199
  className: PendingPanel_module_css_default.title,
11928
12200
  children: t("panel.title")
11929
- })
11930
- }), (0, react_jsx_runtime.jsxs)("div", {
11931
- className: PendingPanel_module_css_default.body,
12201
+ }), (0, react_jsx_runtime.jsx)("button", {
12202
+ type: "button",
12203
+ className: PendingPanel_module_css_default.close,
12204
+ "data-diff-approval-close": true,
12205
+ "aria-label": t("action.close"),
12206
+ title: t("action.close"),
12207
+ onClick: () => {
12208
+ setOpen(false);
12209
+ },
12210
+ children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCloseOutline16, { size: 14 })
12211
+ })]
12212
+ }), snapshot.error !== void 0 || !snapshot.read || files.length === 0 ? (0, react_jsx_runtime.jsxs)("div", {
12213
+ className: PendingPanel_module_css_default.states,
11932
12214
  children: [
11933
12215
  snapshot.error !== void 0 && (0, react_jsx_runtime.jsx)("p", {
11934
12216
  className: PendingPanel_module_css_default.readError,
@@ -11942,25 +12224,49 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
11942
12224
  snapshot.read && snapshot.error === void 0 && files.length === 0 && (0, react_jsx_runtime.jsx)("p", {
11943
12225
  className: PendingPanel_module_css_default.note,
11944
12226
  children: t("panel.empty")
12227
+ })
12228
+ ]
12229
+ }) : (0, react_jsx_runtime.jsxs)("div", {
12230
+ className: PendingPanel_module_css_default.split,
12231
+ children: [
12232
+ (0, react_jsx_runtime.jsxs)("nav", {
12233
+ className: PendingPanel_module_css_default.fileList,
12234
+ style: { width: listWidth },
12235
+ "data-diff-approval-file-list": true,
12236
+ children: [mine.length > 0 && (0, react_jsx_runtime.jsxs)("section", { children: [(0, react_jsx_runtime.jsx)("h3", {
12237
+ className: PendingPanel_module_css_default.group,
12238
+ children: t("panel.group.current")
12239
+ }), (0, react_jsx_runtime.jsx)("ul", {
12240
+ className: PendingPanel_module_css_default.rows,
12241
+ children: mine.map(renderEntry)
12242
+ })] }), theirs.length > 0 && (0, react_jsx_runtime.jsxs)("section", { children: [(0, react_jsx_runtime.jsx)("h3", {
12243
+ className: PendingPanel_module_css_default.group,
12244
+ children: t("panel.group.others")
12245
+ }), (0, react_jsx_runtime.jsx)("ul", {
12246
+ className: PendingPanel_module_css_default.rows,
12247
+ children: theirs.map(renderEntry)
12248
+ })] })]
11945
12249
  }),
11946
- snapshot.read && files.length > 0 && (0, react_jsx_runtime.jsx)("p", {
11947
- className: PendingPanel_module_css_default.hint,
11948
- children: t("panel.selectHint")
12250
+ (0, react_jsx_runtime.jsx)("div", {
12251
+ className: PendingPanel_module_css_default.resizeHandle,
12252
+ "data-diff-resize": true,
12253
+ onMouseDown: startResize
11949
12254
  }),
11950
- mine.length > 0 && (0, react_jsx_runtime.jsxs)("section", { children: [(0, react_jsx_runtime.jsx)("h3", {
11951
- className: PendingPanel_module_css_default.group,
11952
- children: t("panel.group.current")
11953
- }), (0, react_jsx_runtime.jsx)("ul", {
11954
- className: PendingPanel_module_css_default.rows,
11955
- children: mine.map(renderEntry)
11956
- })] }),
11957
- theirs.length > 0 && (0, react_jsx_runtime.jsxs)("section", { children: [(0, react_jsx_runtime.jsx)("h3", {
11958
- className: PendingPanel_module_css_default.group,
11959
- children: t("panel.group.others")
11960
- }), (0, react_jsx_runtime.jsx)("ul", {
11961
- className: PendingPanel_module_css_default.rows,
11962
- children: theirs.map(renderEntry)
11963
- })] })
12255
+ (0, react_jsx_runtime.jsx)("div", {
12256
+ className: PendingPanel_module_css_default.detail,
12257
+ children: selectedFile === void 0 ? (0, react_jsx_runtime.jsx)("p", {
12258
+ className: PendingPanel_module_css_default.detailEmpty,
12259
+ children: t("panel.selectHint")
12260
+ }) : (0, react_jsx_runtime.jsx)(PendingDiff, {
12261
+ file: selectedFile,
12262
+ files,
12263
+ busy: snapshot.busy.has(selectedFile.id),
12264
+ t,
12265
+ onKeep,
12266
+ onRevert,
12267
+ onOpen
12268
+ })
12269
+ })
11964
12270
  ]
11965
12271
  })]
11966
12272
  }), (0, react_jsx_runtime.jsx)("div", {
@@ -11969,7 +12275,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
11969
12275
  type: "button",
11970
12276
  className: PendingPanel_module_css_default.badge,
11971
12277
  "data-diff-approval-badge": files.length,
11972
- "data-active": open || files.length > 0 ? "" : void 0,
12278
+ "data-active": open ? "" : void 0,
11973
12279
  "aria-label": t("panel.aria"),
11974
12280
  "aria-expanded": open,
11975
12281
  onClick: toggleOpen,
@@ -12018,6 +12324,13 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
12018
12324
  sessionId,
12019
12325
  id
12020
12326
  }));
12327
+ },
12328
+ async open(sessionId, id, action) {
12329
+ return openOf(await rpc.call(DIFF_APPROVAL_CHANNEL, "open", {
12330
+ sessionId,
12331
+ id,
12332
+ action
12333
+ }));
12021
12334
  }
12022
12335
  };
12023
12336
  }
@@ -12066,6 +12379,15 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
12066
12379
  if (outcome !== "kept" && outcome !== "reverted" && outcome !== "missing") throw new Error("the action returned a malformed outcome");
12067
12380
  return { outcome };
12068
12381
  }
12382
+ /** Narrow the open endpoint's value; a malformed wire value is an open failure. */
12383
+ function openOf(result) {
12384
+ if (!result.ok) throw new Error(`${result.error.code}: ${result.error.message}`);
12385
+ const value = result.value;
12386
+ if (typeof value !== "object" || value === null) throw new Error("the action returned a malformed value");
12387
+ const outcome = value.outcome;
12388
+ if (outcome !== "opened" && outcome !== "missing") throw new Error("the action returned a malformed outcome");
12389
+ return { outcome };
12390
+ }
12069
12391
  //#endregion
12070
12392
  //#region lib/types/client/store.js
12071
12393
  /**
@@ -12158,6 +12480,16 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
12158
12480
  await port.revert(sessionId, id);
12159
12481
  });
12160
12482
  },
12483
+ async open(sessionId, id, action) {
12484
+ try {
12485
+ await port.open(sessionId, id, action);
12486
+ } catch (error) {
12487
+ publish({
12488
+ ...snapshot,
12489
+ error: error instanceof Error ? error.message : String(error)
12490
+ });
12491
+ }
12492
+ },
12161
12493
  reset() {
12162
12494
  publish({
12163
12495
  read: false,
@@ -12181,18 +12513,26 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
12181
12513
  "panel.group.others": "其他会话",
12182
12514
  "panel.aria": "待处理改动",
12183
12515
  "panel.stats": "+{added} -{removed}",
12184
- "panel.selectHint": "点击每项查看整个文件的差异,拖动选择代码行可复制引用",
12516
+ "panel.selectHint": "点击每项查看整个文件的差异;选中文本后,用底部状态栏或 Ctrl+L 复制引用",
12185
12517
  "panel.missing": "文件已不存在",
12186
12518
  "panel.missingHint": "该文件已不存在,回退会恢复该文件。",
12187
12519
  "panel.createHint": "回退会删除该新建的文件。",
12188
12520
  "row.create": "新增文件",
12521
+ "row.added": "+{added}",
12522
+ "row.removed": "-{removed}",
12189
12523
  "action.keep": "保留",
12190
12524
  "action.revert": "回退",
12525
+ "action.openFile": "打开文件",
12526
+ "action.revealFile": "打开所在目录",
12191
12527
  "action.busy": "处理中…",
12192
12528
  "action.prevDiff": "上一处差异",
12193
12529
  "action.nextDiff": "下一处差异",
12194
- "action.copyRange": "复制引用",
12530
+ "action.copyHint": "复制引用",
12195
12531
  "action.copied": "已复制",
12532
+ "action.langAuto": "自动",
12533
+ "action.langAutoDetected": "自动:{lang}",
12534
+ "action.langSelect": "高亮语言",
12535
+ "action.close": "关闭",
12196
12536
  "status.kept": "已保留",
12197
12537
  "status.reverted": "已回退",
12198
12538
  "status.missing": "该改动已不存在"
@@ -12207,18 +12547,26 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
12207
12547
  "panel.group.others": "Other sessions",
12208
12548
  "panel.aria": "Pending changes",
12209
12549
  "panel.stats": "+{added} -{removed}",
12210
- "panel.selectHint": "Select an item to review its whole-file diff; drag lines to copy a reference",
12550
+ "panel.selectHint": "Select an item to review its whole-file diff; select text and copy its reference from the status bar (Ctrl+L)",
12211
12551
  "panel.missing": "File is gone",
12212
12552
  "panel.missingHint": "This file no longer exists; reverting restores it.",
12213
12553
  "panel.createHint": "Reverting removes this created file.",
12214
12554
  "row.create": "New file",
12555
+ "row.added": "+{added}",
12556
+ "row.removed": "-{removed}",
12215
12557
  "action.keep": "Keep",
12216
12558
  "action.revert": "Revert",
12559
+ "action.openFile": "Open file",
12560
+ "action.revealFile": "Reveal in folder",
12217
12561
  "action.busy": "Working…",
12218
12562
  "action.prevDiff": "Previous diff",
12219
12563
  "action.nextDiff": "Next diff",
12220
- "action.copyRange": "Copy reference",
12564
+ "action.copyHint": "Copy reference",
12221
12565
  "action.copied": "Copied",
12566
+ "action.langAuto": "Auto",
12567
+ "action.langAutoDetected": "Auto: {lang}",
12568
+ "action.langSelect": "Highlight language",
12569
+ "action.close": "Close",
12222
12570
  "status.kept": "Kept",
12223
12571
  "status.reverted": "Reverted",
12224
12572
  "status.missing": "This change no longer exists"
@@ -12256,7 +12604,8 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
12256
12604
  store.refresh(sessionId);
12257
12605
  },
12258
12606
  onKeep: (sessionId, path) => store.keep(sessionId, path),
12259
- onRevert: (sessionId, path) => store.revert(sessionId, path)
12607
+ onRevert: (sessionId, path) => store.revert(sessionId, path),
12608
+ onOpen: (sessionId, id, action) => store.open(sessionId, id, action)
12260
12609
  })
12261
12610
  }, PendingPanel));
12262
12611
  }