dsh-codinput 0.3.6 → 0.3.7

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/package.json CHANGED
@@ -1,9 +1,10 @@
1
1
  {
2
2
  "name": "dsh-codinput",
3
- "version": "0.3.6",
3
+ "version": "0.3.7",
4
4
  "description": "DSH WebUI 插件:把聊天输入框变成代码编辑器一样的输入风格(CodeMirror 6 接管 conversation.composer.bar)。",
5
5
  "license": "MIT",
6
6
  "author": "Witherwithwinter",
7
+ "icon": "./assets/icon.png",
7
8
  "keywords": [
8
9
  "dsh",
9
10
  "deepseek",
@@ -27,7 +27,7 @@ const FILE_FOLD =
27
27
  'M26.3909 7.37445L19.0525 0V3.77445C19.0525 4.89271 19.0525 5.45184 19.2352 5.89289C19.4788 6.48096 19.946 6.94818 20.5341 7.19176C20.9751 7.37445 21.5342 7.37445 22.6525 7.37445H26.3909Z';
28
28
 
29
29
  /** 官方 FileTypeIcon 的通用文件 glyph(28×28 视图框,fill 自带轮廓)。 */
30
- function FileGlyph({ size = 28 }: { size?: number }): JSX.Element {
30
+ export function FileGlyph({ size = 28 }: { size?: number }): JSX.Element {
31
31
  return (
32
32
  <svg width={size} height={size} viewBox="0 0 28 28" fill="none" aria-hidden="true">
33
33
  <path d={FILE_BODY} fill="currentColor" />
@@ -37,14 +37,14 @@ function FileGlyph({ size = 28 }: { size?: number }): JSX.Element {
37
37
  }
38
38
 
39
39
  /** 官方 fileExtension:取 basename 最后一个点之后的部分。 */
40
- function fileExtension(name: string): string {
40
+ export function fileExtension(name: string): string {
41
41
  const base = name.split(/[\\/]/).pop() ?? name;
42
42
  const dot = base.lastIndexOf('.');
43
43
  return dot < 0 ? '' : base.slice(dot + 1);
44
44
  }
45
45
 
46
46
  /** 官方 fileSizeText 逐值复刻。 */
47
- function fileSizeText(bytes: number): string {
47
+ export function fileSizeText(bytes: number): string {
48
48
  if (bytes < 1024) return `${bytes}B`;
49
49
  const kb = bytes / 1024;
50
50
  if (kb < 1024) return `${kb < 10 ? kb.toFixed(1) : Math.round(kb)}KB`;
@@ -197,7 +197,9 @@ function TokenPill(props: { stats: SessionStats; open: boolean; onToggle(): void
197
197
 
198
198
  export function StatsRow(props: StatsRowProps): JSX.Element | null {
199
199
  const { useProjection, sessionId, chatNodes } = props;
200
- const [open, setOpen] = useState<'rounds' | 'tokens' | null>(null);
200
+ // 唯一展开:三枚(轮步/用量/上下文小圈)共用一个 open 态——任意一枚展开
201
+ // 即收起其余(官方语义:详情面板同时只存在一个)。
202
+ const [open, setOpen] = useState<'rounds' | 'tokens' | 'gauge' | null>(null);
201
203
  const rootRef = useRef<HTMLDivElement | null>(null);
202
204
  const stats = useSessionStats(useProjection, sessionId, chatNodes);
203
205
 
@@ -226,7 +228,11 @@ export function StatsRow(props: StatsRowProps): JSX.Element | null {
226
228
  <TokenPill stats={stats} open={open === 'tokens'} onToggle={() => setOpen(open === 'tokens' ? null : 'tokens')} />
227
229
  </>
228
230
  ) : null}
229
- <ContextGauge useProjection={useProjection} />
231
+ <ContextGauge
232
+ useProjection={useProjection}
233
+ open={open === 'gauge'}
234
+ onToggle={() => setOpen(open === 'gauge' ? null : 'gauge')}
235
+ />
230
236
  </div>
231
237
  );
232
238
  }
@@ -252,10 +258,14 @@ function shortTokens(n: number): string {
252
258
  }
253
259
 
254
260
  /** 上下文占用小圈(官方 JObwrW 复刻):双环仪表 + 百分比,点击展开分段面板。
255
- * 0.1.7-rc.2 起官方渲染在 dock 行 stats 胶囊之后(原先在工具行 trailing)。 */
256
- export function ContextGauge(props: { useProjection: UseProjection | undefined }): JSX.Element | null {
257
- const { useProjection } = props;
258
- const [open, setOpen] = useState(false);
261
+ * 0.1.7-rc.2 起官方渲染在 dock 行 stats 胶囊之后(原先在工具行 trailing)。
262
+ * 展开态由 StatsRow 持有(三枚详情面板唯一展开)。 */
263
+ export function ContextGauge(props: {
264
+ useProjection: UseProjection | undefined;
265
+ open: boolean;
266
+ onToggle(): void;
267
+ }): JSX.Element | null {
268
+ const { useProjection, open, onToggle } = props;
259
269
  const rootRef = useRef<HTMLSpanElement | null>(null);
260
270
  const pressure = useProjection
261
271
  ? (useProjection('contextPressure') as PressureSnapshot | undefined)
@@ -263,16 +273,6 @@ export function ContextGauge(props: { useProjection: UseProjection | undefined }
263
273
  const breakdown = useProjection
264
274
  ? (useProjection('contextBreakdown') as BreakdownSnapshot | undefined)
265
275
  : undefined;
266
- // 点外面关闭。
267
- useEffect(() => {
268
- if (!open) return;
269
- const onDown = (event: PointerEvent): void => {
270
- if (rootRef.current && event.target instanceof Node && rootRef.current.contains(event.target)) return;
271
- setOpen(false);
272
- };
273
- window.addEventListener('pointerdown', onDown, true);
274
- return () => window.removeEventListener('pointerdown', onDown, true);
275
- }, [open]);
276
276
 
277
277
  if (!pressure || !pressure.contextWindow) return null;
278
278
  const windowTokens = pressure.contextWindow;
@@ -294,7 +294,7 @@ export function ContextGauge(props: { useProjection: UseProjection | undefined }
294
294
  aria-haspopup="dialog"
295
295
  aria-expanded={open}
296
296
  title={t("tool.context", { percent: Math.round(pct) })}
297
- onClick={() => setOpen(!open)}
297
+ onClick={onToggle}
298
298
  >
299
299
  <svg viewBox="0 0 14 14" width="14" height="14" aria-hidden="true">
300
300
  <circle className="dci-gauge-track" cx="7" cy="7" r="5.5" />