@wenaixi/dsh-prompt-history 2.2.0 → 2.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,13 @@
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [2.2.1] - 2026-10-06
8
+
9
+ ### 修复
10
+
11
+ - **双击 Esc 关闭时 Esc 让位宿主**:`doubleEsc=false` 时连按 Esc 不再被插件消费,宿主自行关闭其浮层/菜单。
12
+ - **宽屏历史面板右侧完整预览**:窗口 ≥1000px 时在列表右侧显示高亮条目的完整内容(最多 6 行,超出显示 `… +N more lines`),对齐 Claude Code `HistorySearchDialog` 的 preview。
13
+
7
14
  ## [2.2.0] - 2026-10-06
8
15
 
9
16
  ### 变更
@@ -257,7 +264,8 @@
257
264
 
258
265
  - 选中即复制(任意选中方式)。
259
266
 
260
- [Unreleased]: https://github.com/Wenaixi/dsh-prompt-history/compare/v2.2.0...HEAD
267
+ [Unreleased]: https://github.com/Wenaixi/dsh-prompt-history/compare/v2.2.1...HEAD
268
+ [2.2.1]: https://github.com/Wenaixi/dsh-prompt-history/compare/v2.2.0...v2.2.1
261
269
  [2.2.0]: https://github.com/Wenaixi/dsh-prompt-history/compare/v2.1.0...v2.2.0
262
270
  [2.1.0]: https://github.com/Wenaixi/dsh-prompt-history/compare/v2.0.2...v2.1.0
263
271
  [2.0.2]: https://github.com/Wenaixi/dsh-prompt-history/compare/v2.0.1...v2.0.2
package/lib/client.js CHANGED
@@ -285,6 +285,7 @@ window.__ModuleLoader__.load({
285
285
  let historyPanel = null;
286
286
  let historyList = null;
287
287
  let historyFoot = null;
288
+ let historyPreview = null;
288
289
  let historyRefs = null;
289
290
  /** Remove the history list panel and detach its outside-click listener (idempotent). */
290
291
  function hideHistoryPanel() {
@@ -295,6 +296,7 @@ window.__ModuleLoader__.load({
295
296
  }
296
297
  historyList = null;
297
298
  historyFoot = null;
299
+ historyPreview = null;
298
300
  historyRefs = null;
299
301
  }
300
302
  /** Close the panel when a pointer lands outside both it and the composer. */
@@ -343,12 +345,16 @@ window.__ModuleLoader__.load({
343
345
  const ROW_ACTIVE_CSS = "background:var(--dsw-specific-menu-hover);color:var(--dsw-alias-label-primary);border-left-color:var(--dsw-static-blue-500);";
344
346
  const AGE_CSS = `flex:none;font:11px/1.5 ui-monospace,SFMono-Regular,Consolas,monospace;min-width:${AGE_WIDTH}ch;text-align:right;color:var(--dsw-alias-label-tertiary);`;
345
347
  const ROW_TEXT_CSS = "min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;";
348
+ /** 宽屏阈值:Claude Code 终端列数 ≥100,web 用像素近似。 */
349
+ const WIDE_MIN_PX = 1e3;
350
+ /** 预览最多行数,对齐 Claude Code PREVIEW_ROWS=6。 */
351
+ const PREVIEW_ROWS = 6;
346
352
  /**
347
353
  * Open the history list above the composer card.
348
354
  *
349
355
  * 只做渲染与鼠标交互:键盘(过滤、移动、回填、关闭)在 InputHistory.tsx 里处理,
350
356
  * 因为焦点必须留在输入框,宿主 Menu 的方向键行走(依赖真实焦点)用不上。
351
- * 列表按最新在顶渲染(Claude Code direction='up' 的视觉反向近似:最新贴输入框)。
357
+ * 列表按最新在顶渲染;宽屏(≥1000px)时右侧显示高亮条目的完整内容预览。
352
358
  *
353
359
  * @param view - 命中文本、相对时间、高亮位置与计数。
354
360
  * @param refs - 提交与关闭回调。
@@ -359,25 +365,34 @@ window.__ModuleLoader__.load({
359
365
  el.className = HISTORY_CLASS;
360
366
  el.style.cssText = PANEL_CSS;
361
367
  el.setAttribute("role", "listbox");
368
+ const body = document.createElement("div");
369
+ body.style.cssText = "display:flex;align-items:stretch;";
362
370
  const list = document.createElement("div");
363
- list.style.cssText = "max-height:240px;overflow-y:auto;overscroll-behavior:contain;";
371
+ list.style.cssText = "flex:1 1 0;min-width:0;max-height:240px;overflow-y:auto;overscroll-behavior:contain;";
364
372
  list.setAttribute("role", "presentation");
373
+ const preview = document.createElement("div");
374
+ preview.className = "dsh-ph-history-preview";
375
+ preview.style.cssText = "display:none;flex:1 1 0;min-width:0;max-height:240px;overflow-y:auto;padding:7px 12px;border-left:1px solid var(--dsw-alias-border-l2);font:12.5px/1.6 inherit;color:var(--dsw-alias-label-secondary);white-space:pre-wrap;word-break:break-word;";
376
+ body.append(list, preview);
365
377
  const foot = document.createElement("div");
366
378
  foot.style.cssText = "padding:5px 12px;font:11px/1.5 ui-monospace,SFMono-Regular,Consolas,monospace;color:var(--dsw-alias-label-tertiary);border-top:1px solid var(--dsw-alias-border-l2);";
367
- el.append(list, foot);
379
+ el.append(body, foot);
368
380
  document.body.appendChild(el);
369
381
  historyPanel = el;
370
382
  historyList = list;
371
383
  historyFoot = foot;
384
+ historyPreview = preview;
372
385
  document.addEventListener("pointerdown", onHistoryOutsidePointer, true);
373
386
  }
374
387
  const el = historyPanel;
375
388
  const list = historyList;
376
389
  const foot = historyFoot;
377
- if (el === null || list === null || foot === null) return;
390
+ const preview = historyPreview;
391
+ if (el === null || list === null || foot === null || preview === null) return;
378
392
  historyRefs = refs;
379
393
  list.replaceChildren();
380
- view.texts.slice(0, HISTORY_MAX_ROWS).forEach((text, i) => {
394
+ const rows = view.texts.slice(0, HISTORY_MAX_ROWS);
395
+ rows.forEach((text, i) => {
381
396
  const row = document.createElement("button");
382
397
  row.type = "button";
383
398
  row.id = `${HISTORY_CLASS}-row-${i}`;
@@ -404,6 +419,13 @@ window.__ModuleLoader__.load({
404
419
  if (view.highlight >= 0) el.setAttribute("aria-activedescendant", `${HISTORY_CLASS}-row-${view.highlight}`);
405
420
  else el.removeAttribute("aria-activedescendant");
406
421
  foot.textContent = view.shown === 0 ? T("history.noMatch") : `${view.shown} / ${view.total} ${T("history.hint")}`;
422
+ preview.style.display = window.innerWidth >= WIDE_MIN_PX ? "block" : "none";
423
+ preview.replaceChildren();
424
+ if (view.highlight >= 0 && view.highlight < rows.length) {
425
+ const lines = (rows[view.highlight] ?? "").split("\n");
426
+ preview.textContent = lines.slice(0, PREVIEW_ROWS).join("\n");
427
+ if (lines.length > PREVIEW_ROWS) preview.append(`\n… +${lines.length - PREVIEW_ROWS} ${T("history.moreLines")}`);
428
+ }
407
429
  placeHistoryPanel(el);
408
430
  const active = list.querySelector(`#${HISTORY_CLASS}-row-${view.highlight}`);
409
431
  if (active instanceof HTMLElement) active.scrollIntoView({ block: "nearest" });
@@ -1154,8 +1176,10 @@ window.__ModuleLoader__.load({
1154
1176
  return;
1155
1177
  }
1156
1178
  if (e.key !== "Escape") lastEscapeRef.current = 0;
1157
- else if (!prefs.doubleEsc) lastEscapeRef.current = 0;
1158
- else {
1179
+ else if (!prefs.doubleEsc) {
1180
+ lastEscapeRef.current = 0;
1181
+ return;
1182
+ } else {
1159
1183
  const now = performance.now();
1160
1184
  const doubled = isDoubleEscape(lastEscapeRef.current, now);
1161
1185
  if (live.draft !== "") {
@@ -37,7 +37,7 @@ export declare function hideHistoryPanel(): void;
37
37
  *
38
38
  * 只做渲染与鼠标交互:键盘(过滤、移动、回填、关闭)在 InputHistory.tsx 里处理,
39
39
  * 因为焦点必须留在输入框,宿主 Menu 的方向键行走(依赖真实焦点)用不上。
40
- * 列表按最新在顶渲染(Claude Code direction='up' 的视觉反向近似:最新贴输入框)。
40
+ * 列表按最新在顶渲染;宽屏(≥1000px)时右侧显示高亮条目的完整内容预览。
41
41
  *
42
42
  * @param view - 命中文本、相对时间、高亮位置与计数。
43
43
  * @param refs - 提交与关闭回调。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wenaixi/dsh-prompt-history",
3
- "version": "2.2.0",
3
+ "version": "2.2.1",
4
4
  "description": "Terminal-style prompt history, selection copy/quote and right-click paste for the DSH composer (zh/en).",
5
5
  "keywords": [
6
6
  "dsh",
@@ -459,7 +459,10 @@ export function InputHistory(props: InputHistoryProps) {
459
459
  if (e.key !== 'Escape') {
460
460
  lastEscapeRef.current = 0
461
461
  } else if (!prefs.doubleEsc) {
462
+ // 双击 Esc 关闭:Esc 完全交还宿主(不提示、
463
+ // 不消费)。宿主会自行关闭其浮层/菜单。
462
464
  lastEscapeRef.current = 0
465
+ return
463
466
  } else {
464
467
  const now = performance.now()
465
468
  const doubled = isDoubleEscape(lastEscapeRef.current, now)
@@ -145,6 +145,7 @@ interface HistoryPanelRefs {
145
145
  let historyPanel: HTMLDivElement | null = null
146
146
  let historyList: HTMLDivElement | null = null
147
147
  let historyFoot: HTMLDivElement | null = null
148
+ let historyPreview: HTMLDivElement | null = null
148
149
  let historyRefs: HistoryPanelRefs | null = null
149
150
 
150
151
  /** Remove the history list panel and detach its outside-click listener (idempotent). */
@@ -156,6 +157,7 @@ export function hideHistoryPanel(): void {
156
157
  }
157
158
  historyList = null
158
159
  historyFoot = null
160
+ historyPreview = null
159
161
  historyRefs = null
160
162
  }
161
163
 
@@ -222,12 +224,17 @@ const AGE_CSS = 'flex:none;font:11px/1.5 ui-monospace,SFMono-Regular,Consolas,mo
222
224
 
223
225
  const ROW_TEXT_CSS = 'min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;'
224
226
 
227
+ /** 宽屏阈值:Claude Code 终端列数 ≥100,web 用像素近似。 */
228
+ const WIDE_MIN_PX = 1000
229
+ /** 预览最多行数,对齐 Claude Code PREVIEW_ROWS=6。 */
230
+ const PREVIEW_ROWS = 6
231
+
225
232
  /**
226
233
  * Open the history list above the composer card.
227
234
  *
228
235
  * 只做渲染与鼠标交互:键盘(过滤、移动、回填、关闭)在 InputHistory.tsx 里处理,
229
236
  * 因为焦点必须留在输入框,宿主 Menu 的方向键行走(依赖真实焦点)用不上。
230
- * 列表按最新在顶渲染(Claude Code direction='up' 的视觉反向近似:最新贴输入框)。
237
+ * 列表按最新在顶渲染;宽屏(≥1000px)时右侧显示高亮条目的完整内容预览。
231
238
  *
232
239
  * @param view - 命中文本、相对时间、高亮位置与计数。
233
240
  * @param refs - 提交与关闭回调。
@@ -238,23 +245,34 @@ export function showHistoryPanel(view: HistoryPanelView, refs: HistoryPanelRefs)
238
245
  el.className = HISTORY_CLASS
239
246
  el.style.cssText = PANEL_CSS
240
247
  el.setAttribute('role', 'listbox')
248
+ const body = document.createElement('div')
249
+ body.style.cssText = 'display:flex;align-items:stretch;'
241
250
  const list = document.createElement('div')
242
- list.style.cssText = 'max-height:240px;overflow-y:auto;overscroll-behavior:contain;'
251
+ list.style.cssText = 'flex:1 1 0;min-width:0;max-height:240px;overflow-y:auto;overscroll-behavior:contain;'
243
252
  list.setAttribute('role', 'presentation')
253
+ const preview = document.createElement('div')
254
+ preview.className = 'dsh-ph-history-preview'
255
+ preview.style.cssText = 'display:none;flex:1 1 0;min-width:0;max-height:240px;overflow-y:auto;' +
256
+ 'padding:7px 12px;border-left:1px solid var(--dsw-alias-border-l2);' +
257
+ 'font:12.5px/1.6 inherit;color:var(--dsw-alias-label-secondary);white-space:pre-wrap;' +
258
+ 'word-break:break-word;'
259
+ body.append(list, preview)
244
260
  const foot = document.createElement('div')
245
261
  foot.style.cssText = 'padding:5px 12px;font:11px/1.5 ui-monospace,SFMono-Regular,Consolas,monospace;' +
246
262
  'color:var(--dsw-alias-label-tertiary);border-top:1px solid var(--dsw-alias-border-l2);'
247
- el.append(list, foot)
263
+ el.append(body, foot)
248
264
  document.body.appendChild(el)
249
265
  historyPanel = el
250
266
  historyList = list
251
267
  historyFoot = foot
268
+ historyPreview = preview
252
269
  document.addEventListener('pointerdown', onHistoryOutsidePointer, true)
253
270
  }
254
271
  const el = historyPanel
255
272
  const list = historyList
256
273
  const foot = historyFoot
257
- if (el === null || list === null || foot === null) return
274
+ const preview = historyPreview
275
+ if (el === null || list === null || foot === null || preview === null) return
258
276
  historyRefs = refs
259
277
 
260
278
  list.replaceChildren()
@@ -288,6 +306,19 @@ export function showHistoryPanel(view: HistoryPanelView, refs: HistoryPanelRefs)
288
306
  ? T('history.noMatch')
289
307
  : `${view.shown} / ${view.total} ${T('history.hint')}`
290
308
 
309
+ // 宽屏右侧预览:显示高亮条目的完整文本,超过 6 行截断 + 「… +N more lines」。
310
+ preview.style.display = window.innerWidth >= WIDE_MIN_PX ? 'block' : 'none'
311
+ preview.replaceChildren()
312
+ if (view.highlight >= 0 && view.highlight < rows.length) {
313
+ const text = rows[view.highlight] ?? ''
314
+ const lines = text.split('\n')
315
+ const shown = lines.slice(0, PREVIEW_ROWS)
316
+ preview.textContent = shown.join('\n')
317
+ if (lines.length > PREVIEW_ROWS) {
318
+ preview.append(`\n… +${lines.length - PREVIEW_ROWS} ${T('history.moreLines')}`)
319
+ }
320
+ }
321
+
291
322
  placeHistoryPanel(el)
292
323
  // 高亮项滚进可视区,但不抢焦点。
293
324
  const active = list.querySelector(`#${HISTORY_CLASS}-row-${view.highlight}`)