dsh-prompt-history 1.2.0 → 1.2.2

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
@@ -351,6 +351,29 @@ window.__ModuleLoader__.load({
351
351
  };
352
352
  const gripLeft = pos?.left ?? origin.left + 6;
353
353
  const gripTop = pos?.top ?? gripY() - 26;
354
+ const [panelStyle, setPanelStyle] = (0, react.useState)(null);
355
+ (0, react.useLayoutEffect)(() => {
356
+ if (!open || entries.length === 0) return;
357
+ const panel = panelRef.current;
358
+ if (panel === null) return;
359
+ const gap = 8;
360
+ const gripRight = gripLeft + 20;
361
+ const gripCenterY = gripTop + 26;
362
+ const natural = Math.min(panel.getBoundingClientRect().height, 480);
363
+ const maxHeight = Math.min(natural, window.innerHeight - 8);
364
+ const left = Math.max(4, Math.min(gripRight + gap, window.innerWidth - 230 - 4));
365
+ const next = {
366
+ top: Math.max(4, Math.min(gripCenterY - maxHeight / 2, window.innerHeight - maxHeight - 4)),
367
+ left,
368
+ maxHeight
369
+ };
370
+ setPanelStyle((prev) => prev !== null && prev.top === next.top && prev.left === next.left && prev.maxHeight === next.maxHeight ? prev : next);
371
+ }, [
372
+ open,
373
+ entries.length,
374
+ gripLeft,
375
+ gripTop
376
+ ]);
354
377
  return (0, react_dom.createPortal)(/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
355
378
  type: "button",
356
379
  className: "dsh-ph-toc-grip",
@@ -374,7 +397,7 @@ window.__ModuleLoader__.load({
374
397
  }), open && entries.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
375
398
  ref: panelRef,
376
399
  className: "dsh-ph-toc",
377
- style: {
400
+ style: panelStyle ?? {
378
401
  top: Math.max(4, gripTop - 8),
379
402
  left: Math.min(gripLeft + 30, window.innerWidth - 230)
380
403
  },
@@ -1115,10 +1138,10 @@ window.__ModuleLoader__.load({
1115
1138
  ".dsh-ph-group{margin:8px 0 4px;font-size:13px;font-weight:600;color:var(--dsw-alias-label-primary);}",
1116
1139
  ".dsh-ph-toc-grip{position:fixed;z-index:2147483000;width:20px;height:52px;border:1px solid var(--dsw-alias-border-l1);border-radius:9px;background:var(--dsw-specific-menu);background:color-mix(in srgb,var(--dsw-specific-menu) 88%,#000);box-shadow:0 1px 4px rgba(0,0,0,.18);color:var(--dsw-alias-label-primary);font-size:14px;line-height:1;cursor:pointer;opacity:.65;transition:opacity .15s;display:flex;align-items:center;justify-content:center;padding:0;}",
1117
1140
  ".dsh-ph-toc-grip:hover{opacity:1;background:var(--dsw-alias-bg-layer-2);}",
1118
- ".dsh-ph-toc{position:fixed;z-index:2147483000;width:220px;max-height:min(60vh,480px);display:flex;flex-direction:column;padding:8px;border-radius:12px;background:var(--dsw-specific-menu);border:1px solid var(--dsw-alias-border-l1);box-shadow:0 8px 28px rgba(0,0,0,.25);}",
1141
+ ".dsh-ph-toc{position:fixed;z-index:2147483000;width:220px;max-height:min(60vh,480px);display:flex;flex-direction:column;padding:8px;border-radius:12px;background:var(--dsw-specific-menu);border:1px solid var(--dsw-alias-border-l1);box-shadow:0 8px 28px rgba(0,0,0,.25);box-sizing:border-box;}",
1119
1142
  ".dsh-ph-toc-title{margin:0 4px 6px;font-size:13px;font-weight:600;color:var(--dsw-alias-label-primary);}",
1120
- ".dsh-ph-toc-list{overflow-y:auto;display:flex;flex-direction:column;gap:2px;}",
1121
- ".dsh-ph-toc-item{display:block;width:100%;text-align:left;padding:5px 8px;border:0;border-radius:6px;background:transparent;color:var(--dsw-alias-label-primary);font-size:12px;line-height:1.4;cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}",
1143
+ ".dsh-ph-toc-list{overflow-y:auto;display:flex;flex-direction:column;gap:2px;box-sizing:border-box;}",
1144
+ ".dsh-ph-toc-item{display:block;width:100%;text-align:left;padding:5px 8px;border:0;border-radius:6px;background:transparent;color:var(--dsw-alias-label-primary);font-size:12px;line-height:1.4;cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;box-sizing:border-box;}",
1122
1145
  ".dsh-ph-toc-item:hover{background:var(--dsw-alias-bg-layer-2);}"
1123
1146
  ].join("");
1124
1147
  /**
@@ -15,10 +15,10 @@ const SETTINGS_CSS = [
15
15
  '.dsh-ph-group{margin:8px 0 4px;font-size:13px;font-weight:600;color:var(--dsw-alias-label-primary);}',
16
16
  '.dsh-ph-toc-grip{position:fixed;z-index:2147483000;width:20px;height:52px;border:1px solid var(--dsw-alias-border-l1);border-radius:9px;background:var(--dsw-specific-menu);background:color-mix(in srgb,var(--dsw-specific-menu) 88%,#000);box-shadow:0 1px 4px rgba(0,0,0,.18);color:var(--dsw-alias-label-primary);font-size:14px;line-height:1;cursor:pointer;opacity:.65;transition:opacity .15s;display:flex;align-items:center;justify-content:center;padding:0;}',
17
17
  '.dsh-ph-toc-grip:hover{opacity:1;background:var(--dsw-alias-bg-layer-2);}',
18
- '.dsh-ph-toc{position:fixed;z-index:2147483000;width:220px;max-height:min(60vh,480px);display:flex;flex-direction:column;padding:8px;border-radius:12px;background:var(--dsw-specific-menu);border:1px solid var(--dsw-alias-border-l1);box-shadow:0 8px 28px rgba(0,0,0,.25);}',
18
+ '.dsh-ph-toc{position:fixed;z-index:2147483000;width:220px;max-height:min(60vh,480px);display:flex;flex-direction:column;padding:8px;border-radius:12px;background:var(--dsw-specific-menu);border:1px solid var(--dsw-alias-border-l1);box-shadow:0 8px 28px rgba(0,0,0,.25);box-sizing:border-box;}',
19
19
  '.dsh-ph-toc-title{margin:0 4px 6px;font-size:13px;font-weight:600;color:var(--dsw-alias-label-primary);}',
20
- '.dsh-ph-toc-list{overflow-y:auto;display:flex;flex-direction:column;gap:2px;}',
21
- '.dsh-ph-toc-item{display:block;width:100%;text-align:left;padding:5px 8px;border:0;border-radius:6px;background:transparent;color:var(--dsw-alias-label-primary);font-size:12px;line-height:1.4;cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}',
20
+ '.dsh-ph-toc-list{overflow-y:auto;display:flex;flex-direction:column;gap:2px;box-sizing:border-box;}',
21
+ '.dsh-ph-toc-item{display:block;width:100%;text-align:left;padding:5px 8px;border:0;border-radius:6px;background:transparent;color:var(--dsw-alias-label-primary);font-size:12px;line-height:1.4;cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;box-sizing:border-box;}',
22
22
  '.dsh-ph-toc-item:hover{background:var(--dsw-alias-bg-layer-2);}',
23
23
  ].join('');
24
24
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-prompt-history",
3
- "version": "1.2.0",
3
+ "version": "1.2.2",
4
4
  "description": "DSH web plugin: terminal-style input for the composer — bash-like Up/Down prompt history with prefix search and Ctrl+R, selection toolbar with copy and quote (Codex-style > blockquote), right-click paste, toggleable draggable chat TOC, fully internationalized (zh/en), with copy modes in Settings (history sourced from the session's own message log; zero config required).",
5
5
  "keywords": [
6
6
  "dsh",
@@ -5,7 +5,7 @@
5
5
  * message. Rendered via a portal to document.body so the fixed positioning is
6
6
  * viewport-relative, and lives off the conversation snapshot's user nodes.
7
7
  */
8
- import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
8
+ import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react'
9
9
  import type { MouseEvent as ReactMouseEvent } from 'react'
10
10
  import { createPortal } from 'react-dom'
11
11
  import type { ConversationNode } from '@deepseek-ai/dsh-client-runtime/client'
@@ -146,6 +146,32 @@ export function ChatToc({ nodes }: ChatTocProps): JSX.Element {
146
146
 
147
147
  const gripLeft = pos?.left ?? origin.left + 6
148
148
  const gripTop = pos?.top ?? gripY() - 26
149
+
150
+ // The panel is anchored to the grip like a context menu: it sits to the
151
+ // grip's right, vertically centered on it, and is clamped so the box always
152
+ // fits the viewport — dragging the grip to the bottom edge shifts the panel
153
+ // up just enough (never clipping the directory).
154
+ const [panelStyle, setPanelStyle] = useState<{ top: number; left: number; maxHeight: number } | null>(null)
155
+ useLayoutEffect(() => {
156
+ if (!open || entries.length === 0) return
157
+ const panel = panelRef.current
158
+ if (panel === null) return
159
+ const gap = 8
160
+ const gripRight = gripLeft + 20 // grip width from .dsh-ph-toc-grip
161
+ const gripCenterY = gripTop + 26 // grip height 52 → vertical center
162
+ const natural = Math.min(panel.getBoundingClientRect().height, 480)
163
+ const maxHeight = Math.min(natural, window.innerHeight - 8)
164
+ const left = Math.max(4, Math.min(gripRight + gap, window.innerWidth - 230 - 4))
165
+ // Prefer centering on the grip; clamp so both edges stay inside the viewport.
166
+ const top = Math.max(4, Math.min(gripCenterY - maxHeight / 2, window.innerHeight - maxHeight - 4))
167
+ const next = { top, left, maxHeight }
168
+ setPanelStyle((prev) => (
169
+ prev !== null && prev.top === next.top && prev.left === next.left && prev.maxHeight === next.maxHeight
170
+ ? prev
171
+ : next
172
+ ))
173
+ }, [open, entries.length, gripLeft, gripTop])
174
+
149
175
  return createPortal(
150
176
  <>
151
177
  <button
@@ -167,7 +193,7 @@ export function ChatToc({ nodes }: ChatTocProps): JSX.Element {
167
193
  <div
168
194
  ref={panelRef}
169
195
  className="dsh-ph-toc"
170
- style={{ top: Math.max(4, gripTop - 8), left: Math.min(gripLeft + 30, window.innerWidth - 230) }}
196
+ style={panelStyle ?? { top: Math.max(4, gripTop - 8), left: Math.min(gripLeft + 30, window.innerWidth - 230) }}
171
197
  >
172
198
  <div className="dsh-ph-toc-title">{T('toc.title')}</div>
173
199
  <div className="dsh-ph-toc-list">
@@ -31,10 +31,10 @@ const SETTINGS_CSS = [
31
31
  '.dsh-ph-group{margin:8px 0 4px;font-size:13px;font-weight:600;color:var(--dsw-alias-label-primary);}',
32
32
  '.dsh-ph-toc-grip{position:fixed;z-index:2147483000;width:20px;height:52px;border:1px solid var(--dsw-alias-border-l1);border-radius:9px;background:var(--dsw-specific-menu);background:color-mix(in srgb,var(--dsw-specific-menu) 88%,#000);box-shadow:0 1px 4px rgba(0,0,0,.18);color:var(--dsw-alias-label-primary);font-size:14px;line-height:1;cursor:pointer;opacity:.65;transition:opacity .15s;display:flex;align-items:center;justify-content:center;padding:0;}',
33
33
  '.dsh-ph-toc-grip:hover{opacity:1;background:var(--dsw-alias-bg-layer-2);}',
34
- '.dsh-ph-toc{position:fixed;z-index:2147483000;width:220px;max-height:min(60vh,480px);display:flex;flex-direction:column;padding:8px;border-radius:12px;background:var(--dsw-specific-menu);border:1px solid var(--dsw-alias-border-l1);box-shadow:0 8px 28px rgba(0,0,0,.25);}',
34
+ '.dsh-ph-toc{position:fixed;z-index:2147483000;width:220px;max-height:min(60vh,480px);display:flex;flex-direction:column;padding:8px;border-radius:12px;background:var(--dsw-specific-menu);border:1px solid var(--dsw-alias-border-l1);box-shadow:0 8px 28px rgba(0,0,0,.25);box-sizing:border-box;}',
35
35
  '.dsh-ph-toc-title{margin:0 4px 6px;font-size:13px;font-weight:600;color:var(--dsw-alias-label-primary);}',
36
- '.dsh-ph-toc-list{overflow-y:auto;display:flex;flex-direction:column;gap:2px;}',
37
- '.dsh-ph-toc-item{display:block;width:100%;text-align:left;padding:5px 8px;border:0;border-radius:6px;background:transparent;color:var(--dsw-alias-label-primary);font-size:12px;line-height:1.4;cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}',
36
+ '.dsh-ph-toc-list{overflow-y:auto;display:flex;flex-direction:column;gap:2px;box-sizing:border-box;}',
37
+ '.dsh-ph-toc-item{display:block;width:100%;text-align:left;padding:5px 8px;border:0;border-radius:6px;background:transparent;color:var(--dsw-alias-label-primary);font-size:12px;line-height:1.4;cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;box-sizing:border-box;}',
38
38
  '.dsh-ph-toc-item:hover{background:var(--dsw-alias-bg-layer-2);}',
39
39
  ].join('')
40
40