dsh-codinput 0.1.0 → 0.1.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-codinput",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "DSH WebUI 插件:把聊天输入框变成代码编辑器一样的输入风格(CodeMirror 6 接管 conversation.composer.bar)。",
5
5
  "license": "MIT",
6
6
  "author": "Witherwithwinter",
@@ -22,7 +22,7 @@ export interface SlashMenuProps {
22
22
  }
23
23
 
24
24
  /** 组标题按来源映射到 i18n;未知来源回退原名(与官方同语义)。 */
25
- const MAX_HEIGHT = 320;
25
+ const MAX_HEIGHT = 400;
26
26
 
27
27
  function sourceLabel(source: string): string {
28
28
  return tOptional(`menu.source.${source}`) ?? source;
@@ -1,6 +1,7 @@
1
- /** 数据行(卡片下方):官方 composer stats 复刻(bOPqQW + bRhRbq)——两枚
2
- * 胶囊(仪表盘「轮/步 · tok/s」、数据库「累计 tok · 缓存命中」),点击弹出
3
- * 官方同构详情面板(会话统计 / Token 用量),锚点上方弹出、点外关闭。 */
1
+ /** 数据行(卡片下方):官方 composer dock 复刻——两枚胶囊(仪表盘「轮/步 · tok/s」、
2
+ * 数据库「累计 tok · 缓存命中」)+ 上下文小圈(0.1.7-rc.2 起官方把它挪进本行,圈+百分比
3
+ * 药丸),点击弹出官方同构详情面板(会话统计 / Token 用量 / 上下文分段),锚点上方弹出、
4
+ * 点外关闭。 */
4
5
 
5
6
  import { useEffect, useRef, useState } from 'react';
6
7
  import { useSessionStats, formatTokens, type SessionStats } from '../stats';
@@ -178,11 +179,144 @@ export function StatsRow(props: StatsRowProps): JSX.Element | null {
178
179
  return () => window.removeEventListener('pointerdown', onDown, true);
179
180
  }, [open]);
180
181
 
181
- if (!stats) return null;
182
+ // 官方 rc.2:上下文小圈独立于 stats 胶囊渲染(无 stats 也要出圈)。
183
+ const pressure = useProjection
184
+ ? (useProjection('contextPressure') as PressureSnapshot | undefined)
185
+ : undefined;
186
+ const hasGauge = pressure !== undefined && !!pressure.contextWindow;
187
+ if (!stats && !hasGauge) return null;
182
188
  return (
183
189
  <div className="dci-stat-root" data-composer-stats="codinput" ref={rootRef}>
184
- <Pill stats={stats} open={open === 'rounds'} onToggle={() => setOpen(open === 'rounds' ? null : 'rounds')} />
185
- <TokenPill stats={stats} open={open === 'tokens'} onToggle={() => setOpen(open === 'tokens' ? null : 'tokens')} />
190
+ {stats ? (
191
+ <>
192
+ <Pill stats={stats} open={open === 'rounds'} onToggle={() => setOpen(open === 'rounds' ? null : 'rounds')} />
193
+ <TokenPill stats={stats} open={open === 'tokens'} onToggle={() => setOpen(open === 'tokens' ? null : 'tokens')} />
194
+ </>
195
+ ) : null}
196
+ <ContextGauge useProjection={useProjection} />
186
197
  </div>
187
198
  );
188
199
  }
200
+
201
+ const GAUGE_CIRCUMFERENCE = 2 * Math.PI * 5.5;
202
+
203
+ interface PressureSnapshot {
204
+ contextWindow?: number;
205
+ pressureTokens?: number;
206
+ projectedTokens?: number;
207
+ }
208
+
209
+ interface BreakdownSnapshot {
210
+ messageTokens?: number;
211
+ systemTokens?: number;
212
+ toolsTokens?: number;
213
+ }
214
+
215
+ function shortTokens(n: number): string {
216
+ if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(n % 1_000_000 === 0 ? 0 : 1)}M`;
217
+ if (n >= 1_000) return `${(n / 1_000).toFixed(n % 1_000 === 0 ? 0 : 1)}K`;
218
+ return `${n}`;
219
+ }
220
+
221
+ /** 上下文占用小圈(官方 JObwrW 复刻):双环仪表 + 百分比,点击展开分段面板。
222
+ * 0.1.7-rc.2 起官方渲染在 dock 行 stats 胶囊之后(原先在工具行 trailing)。 */
223
+ export function ContextGauge(props: { useProjection: UseProjection | undefined }): JSX.Element | null {
224
+ const { useProjection } = props;
225
+ const [open, setOpen] = useState(false);
226
+ const rootRef = useRef<HTMLSpanElement | null>(null);
227
+ const pressure = useProjection
228
+ ? (useProjection('contextPressure') as PressureSnapshot | undefined)
229
+ : undefined;
230
+ const breakdown = useProjection
231
+ ? (useProjection('contextBreakdown') as BreakdownSnapshot | undefined)
232
+ : undefined;
233
+ // 点外面关闭。
234
+ useEffect(() => {
235
+ if (!open) return;
236
+ const onDown = (event: PointerEvent): void => {
237
+ if (rootRef.current && event.target instanceof Node && rootRef.current.contains(event.target)) return;
238
+ setOpen(false);
239
+ };
240
+ window.addEventListener('pointerdown', onDown, true);
241
+ return () => window.removeEventListener('pointerdown', onDown, true);
242
+ }, [open]);
243
+
244
+ if (!pressure || !pressure.contextWindow) return null;
245
+ const windowTokens = pressure.contextWindow;
246
+ // 官方 contextOccupancy 语义:优先 projectedTokens(含投影增长),缺失退 pressureTokens。
247
+ const usedTokens = pressure.projectedTokens ?? pressure.pressureTokens ?? 0;
248
+ const pct = Math.min(100, Math.round((usedTokens / windowTokens) * 100));
249
+ const dash = (pct / 100) * GAUGE_CIRCUMFERENCE;
250
+ const segments: readonly [string, number][] = [
251
+ ['dci-gauge-colorSystem', breakdown?.systemTokens ?? 0],
252
+ ['dci-gauge-colorTools', breakdown?.toolsTokens ?? 0],
253
+ ['dci-gauge-colorMessages', breakdown?.messageTokens ?? 0],
254
+ ];
255
+ return (
256
+ <span className="dci-gauge-root" ref={rootRef}>
257
+ <button
258
+ type="button"
259
+ className="dci-gauge-trigger"
260
+ aria-label={t("tool.context", { percent: Math.round(pct) })}
261
+ aria-haspopup="dialog"
262
+ aria-expanded={open}
263
+ title={t("tool.context", { percent: Math.round(pct) })}
264
+ onClick={() => setOpen(!open)}
265
+ >
266
+ <svg viewBox="0 0 14 14" width="14" height="14" aria-hidden="true">
267
+ <circle className="dci-gauge-track" cx="7" cy="7" r="5.5" />
268
+ <circle
269
+ className="dci-gauge-fill"
270
+ cx="7" cy="7" r="5.5"
271
+ strokeDasharray={`${dash} ${GAUGE_CIRCUMFERENCE}`}
272
+ transform="rotate(-90 7 7)"
273
+ />
274
+ </svg>
275
+ <span>{Math.round(pct)}%</span>
276
+ </button>
277
+ {open ? (
278
+ <div className="dci-gauge-panel" role="dialog" aria-label={t("tool.contextTitle")}>
279
+ <div className="dci-gauge-header">
280
+ <span className="dci-gauge-headline">{t("tool.contextTitle")}</span>
281
+ <span className="dci-gauge-percent">{Math.round(pct)}%</span>
282
+ <span className="dci-gauge-figures">
283
+ ~{shortTokens(usedTokens)} / {shortTokens(windowTokens)}
284
+ </span>
285
+ </div>
286
+ <div className="dci-gauge-bar">
287
+ {segments.map(([cls, tokens]) => (
288
+ <div
289
+ key={cls}
290
+ className={`dci-gauge-segment ${cls}`}
291
+ style={{ width: `${(tokens / windowTokens) * 100}%` }}
292
+ />
293
+ ))}
294
+ </div>
295
+ <dl className="dci-gauge-rows">
296
+ <div className="dci-gauge-row">
297
+ <dt>
298
+ <span className={`dci-gauge-swatch dci-gauge-colorSystem`} aria-hidden="true" />
299
+ {t("stats.prompt")}
300
+ </dt>
301
+ <dd>~{shortTokens(breakdown?.systemTokens ?? 0)}</dd>
302
+ </div>
303
+ <div className="dci-gauge-row">
304
+ <dt>
305
+ <span className={`dci-gauge-swatch dci-gauge-colorTools`} aria-hidden="true" />
306
+ {t("stats.tools")}
307
+ </dt>
308
+ <dd>~{shortTokens(breakdown?.toolsTokens ?? 0)}</dd>
309
+ </div>
310
+ <div className="dci-gauge-row">
311
+ <dt>
312
+ <span className={`dci-gauge-swatch dci-gauge-colorMessages`} aria-hidden="true" />
313
+ {t("stats.messages")}
314
+ </dt>
315
+ <dd>~{shortTokens(breakdown?.messageTokens ?? 0)}</dd>
316
+ </div>
317
+ </dl>
318
+ </div>
319
+ ) : null}
320
+ </span>
321
+ );
322
+ }
@@ -3,9 +3,10 @@
3
3
  * 左侧 .dci-tools:指令(plus)、添加附件(paperclip)、权限(shield 触发器);
4
4
  * 右侧 .dci-trailing:模型与思考强度(触发器)、发送(.primary 圆钮)。
5
5
  * 按钮沿用官方 keepFocus 语义(mousedown 不夺走编辑器焦点)。
6
+ * 0.1.7-rc.2 起官方上下文小圈不再在工具行(挪进卡片下方 dock 行,见 StatsRow)。
6
7
  */
7
8
 
8
- import { useEffect, useRef, useState } from 'react';
9
+ import { useRef } from 'react';
9
10
  import type { DraftAttachmentId, InputActions, InputState, InputTriggerController, UseProjection } from '../host-types';
10
11
  import { openCommandMenu } from '../triggers';
11
12
  import { stash } from '../services';
@@ -102,7 +103,6 @@ export function Toolbar(props: ToolbarProps): JSX.Element {
102
103
  sessionId={sessionId}
103
104
  onNotify={onNotify}
104
105
  />
105
- <ContextGauge useProjection={useProjection} />
106
106
  <button
107
107
  type="button"
108
108
  className="dci-send"
@@ -118,122 +118,3 @@ export function Toolbar(props: ToolbarProps): JSX.Element {
118
118
  </div>
119
119
  );
120
120
  }
121
-
122
- const GAUGE_CIRCUMFERENCE = 2 * Math.PI * 5.5;
123
-
124
- interface PressureSnapshot {
125
- contextWindow?: number;
126
- pressureTokens?: number;
127
- projectedTokens?: number;
128
- }
129
-
130
- interface BreakdownSnapshot {
131
- messageTokens?: number;
132
- systemTokens?: number;
133
- toolsTokens?: number;
134
- }
135
-
136
- function shortTokens(n: number): string {
137
- if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(n % 1_000_000 === 0 ? 0 : 1)}M`;
138
- if (n >= 1_000) return `${(n / 1_000).toFixed(n % 1_000 === 0 ? 0 : 1)}K`;
139
- return `${n}`;
140
- }
141
-
142
- /** 上下文占用小圈(官方 JObwrW 复刻):双环仪表 + 点击展开分段面板。 */
143
- function ContextGauge(props: { useProjection: UseProjection | undefined }): JSX.Element | null {
144
- const { useProjection } = props;
145
- const [open, setOpen] = useState(false);
146
- const rootRef = useRef<HTMLSpanElement | null>(null);
147
- const pressure = useProjection
148
- ? (useProjection('contextPressure') as PressureSnapshot | undefined)
149
- : undefined;
150
- const breakdown = useProjection
151
- ? (useProjection('contextBreakdown') as BreakdownSnapshot | undefined)
152
- : undefined;
153
- // 点外面关闭。
154
- useEffect(() => {
155
- if (!open) return;
156
- const onDown = (event: PointerEvent): void => {
157
- if (rootRef.current && event.target instanceof Node && rootRef.current.contains(event.target)) return;
158
- setOpen(false);
159
- };
160
- window.addEventListener('pointerdown', onDown, true);
161
- return () => window.removeEventListener('pointerdown', onDown, true);
162
- }, [open]);
163
-
164
- if (!pressure || !pressure.contextWindow) return null;
165
- const windowTokens = pressure.contextWindow;
166
- const pct = Math.min(100, Math.max(0, ((pressure.pressureTokens ?? 0) / windowTokens) * 100));
167
- const dash = (pct / 100) * GAUGE_CIRCUMFERENCE;
168
- const segments: readonly [string, number][] = [
169
- ['dci-gauge-colorSystem', breakdown?.systemTokens ?? 0],
170
- ['dci-gauge-colorTools', breakdown?.toolsTokens ?? 0],
171
- ['dci-gauge-colorMessages', breakdown?.messageTokens ?? 0],
172
- ];
173
- return (
174
- <span className="dci-gauge-root" ref={rootRef}>
175
- <button
176
- type="button"
177
- className="dci-gauge-trigger"
178
- aria-label={t("tool.context", { percent: Math.round(pct) })}
179
- aria-haspopup="dialog"
180
- aria-expanded={open}
181
- title={t("tool.context", { percent: Math.round(pct) })}
182
- onClick={() => setOpen(!open)}
183
- >
184
- <svg viewBox="0 0 14 14" width="14" height="14" aria-hidden="true">
185
- <circle className="dci-gauge-track" cx="7" cy="7" r="5.5" />
186
- <circle
187
- className="dci-gauge-fill"
188
- cx="7" cy="7" r="5.5"
189
- strokeDasharray={`${dash} ${GAUGE_CIRCUMFERENCE}`}
190
- transform="rotate(-90 7 7)"
191
- />
192
- </svg>
193
- </button>
194
- {open ? (
195
- <div className="dci-gauge-panel" role="dialog" aria-label={t("tool.contextTitle")}>
196
- <div className="dci-gauge-header">
197
- <span className="dci-gauge-headline">{t("tool.contextTitle")}</span>
198
- <span className="dci-gauge-percent">{Math.round(pct)}%</span>
199
- <span className="dci-gauge-figures">
200
- ~{shortTokens(pressure.pressureTokens ?? 0)} / {shortTokens(windowTokens)}
201
- </span>
202
- </div>
203
- <div className="dci-gauge-bar">
204
- {segments.map(([cls, tokens]) => (
205
- <div
206
- key={cls}
207
- className={`dci-gauge-segment ${cls}`}
208
- style={{ width: `${(tokens / windowTokens) * 100}%` }}
209
- />
210
- ))}
211
- </div>
212
- <dl className="dci-gauge-rows">
213
- <div className="dci-gauge-row">
214
- <dt>
215
- <span className={`dci-gauge-swatch dci-gauge-colorSystem`} aria-hidden="true" />
216
- {t("stats.prompt")}
217
- </dt>
218
- <dd>~{shortTokens(breakdown?.systemTokens ?? 0)}</dd>
219
- </div>
220
- <div className="dci-gauge-row">
221
- <dt>
222
- <span className={`dci-gauge-swatch dci-gauge-colorTools`} aria-hidden="true" />
223
- {t("stats.tools")}
224
- </dt>
225
- <dd>~{shortTokens(breakdown?.toolsTokens ?? 0)}</dd>
226
- </div>
227
- <div className="dci-gauge-row">
228
- <dt>
229
- <span className={`dci-gauge-swatch dci-gauge-colorMessages`} aria-hidden="true" />
230
- {t("stats.messages")}
231
- </dt>
232
- <dd>~{shortTokens(breakdown?.messageTokens ?? 0)}</dd>
233
- </div>
234
- </dl>
235
- </div>
236
- ) : null}
237
- </span>
238
- );
239
- }
@@ -23,7 +23,7 @@ export interface SessionStats {
23
23
  tokensPerSecond?: number;
24
24
  totalTokens?: number;
25
25
  cacheHitPct?: number;
26
- /** 上下文占用百分比(pressureTokens / contextWindow)。 */
26
+ /** 上下文占用百分比(projectedTokens ?? pressureTokens,除以 contextWindow)。 */
27
27
  contextPct?: number;
28
28
  /** 会话统计面板:模型用时(流式活跃累计毫秒)与首 token 延迟。 */
29
29
  modelMs?: number;
@@ -152,9 +152,10 @@ export function useSessionStats(
152
152
  if (sample.ttftMs !== undefined && sample.ttftMs > 0) stats.ttftMs = sample.ttftMs;
153
153
  }
154
154
  const window_ = pressure?.contextWindow;
155
- const pressured = pressure?.pressureTokens;
156
- if (window_ !== undefined && window_ > 0 && pressured !== undefined) {
157
- stats.contextPct = (pressured / window_) * 100;
155
+ // 官方 contextOccupancy:优先 projectedTokens。
156
+ const used = pressure?.projectedTokens ?? pressure?.pressureTokens;
157
+ if (window_ !== undefined && window_ > 0 && used !== undefined) {
158
+ stats.contextPct = (used / window_) * 100;
158
159
  }
159
160
  return Object.keys(stats).length > 0 ? stats : null;
160
161
  }
@@ -105,9 +105,10 @@ const CSS = `
105
105
  /* ===== 菜单(官方 primitives Menu:r20 卡片 / item r10 / side-top 弹出)===== */
106
106
  .dci-menu { position: fixed; z-index: 1100; box-sizing: border-box; padding: 4px; display: flex; flex-direction: column;
107
107
  min-width: 218px; max-width: min(360px, 100vw - 24px); max-height: calc(100vh - 24px); overflow-y: auto;
108
- background: var(--dsw-specific-menu, rgba(28, 28, 32, 0.98)); color: var(--dsw-alias-label-primary, #ececf1);
108
+ background: var(--dsw-menu-surface-fill, var(--dsw-specific-menu, rgba(28, 28, 32, 0.98)));
109
+ backdrop-filter: var(--dsw-menu-backdrop-filter, none); color: var(--dsw-alias-label-primary, #ececf1);
109
110
  --dsw-elevation-stroke-color: var(--dsw-alias-border-l1, transparent);
110
- box-shadow: var(--dsw-elevation-prominent, 0 10px 34px rgba(0, 0, 0, 0.35)); border: 0; border-radius: 20px; font-size: 13px; }
111
+ box-shadow: var(--dsw-elevation-prominent, 0 10px 34px rgba(0, 0, 0, 0.35)); border: 0; border-radius: var(--dsw-radius-lg, 20px); font-size: 13px; }
111
112
  .dci-viewport { display: flex; flex-direction: column; min-height: 0; }
112
113
  .dci-menuLabel { padding: 8px 10px; font-size: 12px; line-height: 16px; color: var(--dsw-alias-label-tertiary, currentColor);
113
114
  user-select: none; text-align: left; }
@@ -122,54 +123,59 @@ const CSS = `
122
123
  .dci-itemLabel { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
123
124
  .dci-check { flex: none; color: var(--dsw-alias-label-primary, currentColor); display: grid; place-items: center; }
124
125
 
125
- /* ===== 候选菜单(官方 MenuView.module.css 逐值复刻:卡片顶锚定全宽浮层)===== */
126
- .dci-slash-menu { z-index: 100; --dsh-scrollbar-thumb: var(--dsw-alias-scrollbar-bg-l2, transparent);
126
+ /* ===== 候选菜单(官方 MenuView.module.css 逐值复刻:卡片顶锚定全宽浮层)
127
+ 0.1.7-rc.2 起菜单本体不再带背景,亚克力在 primitives MenuSurface 的 material 层
128
+ (--dsw-menu-surface-fill + --dsw-menu-backdrop-filter);单元素复刻把两层合并到这里,
129
+ token 链兼容旧宿主(无新 token 时回退 rc.3 形态:不透明 --dsw-specific-menu、r20、无 blur)===== */
130
+ .dci-slash-menu { z-index: 100; box-sizing: border-box; --dsh-scrollbar-thumb: var(--dsw-alias-scrollbar-bg-l2, transparent);
127
131
  --dsh-scrollbar-thumb-hover: var(--dsw-alias-scrollbar-hover-l2, transparent);
128
- background: var(--dsw-specific-menu, rgba(30, 30, 34, 0.98)); color: var(--dsw-alias-label-primary, inherit);
132
+ background: var(--dsw-menu-surface-fill, var(--dsw-specific-menu, rgba(30, 30, 34, 0.98)));
133
+ backdrop-filter: var(--dsw-menu-backdrop-filter, none); color: var(--dsw-alias-label-primary, inherit);
129
134
  --dsw-elevation-stroke-color: var(--dsw-alias-border-l1, transparent);
130
- max-height: 320px; box-shadow: var(--dsw-elevation-prominent, 0 10px 34px rgba(0, 0, 0, 0.35));
131
- border: 0; border-radius: 20px; flex-direction: column; padding: 4px; display: flex; position: absolute;
135
+ max-height: 400px; box-shadow: var(--dsw-elevation-prominent, 0 10px 34px rgba(0, 0, 0, 0.35));
136
+ border: 0; border-radius: var(--dsw-radius-lg, 20px); flex-direction: column; padding: 4px; display: flex; position: absolute;
132
137
  bottom: calc(100% + 4px); left: 0; right: 0; overflow: hidden; }
133
138
  .dci-slash-viewport { flex-direction: column; min-height: 0; display: flex; overflow-y: auto; }
134
- .dci-slash-item { cursor: pointer; width: 100%; min-height: 40px; color: var(--dsw-alias-label-primary, inherit);
135
- text-align: left; background: transparent; border: none; border-radius: 10px; align-items: center; gap: 8px;
136
- padding: 8px 10px; font-size: 14px; line-height: 22px; display: flex; }
139
+ .dci-slash-item { cursor: pointer; width: 100%; min-height: 34px; color: var(--dsw-alias-label-primary, inherit);
140
+ text-align: left; background: transparent; border: none; border-radius: var(--dsw-radius-md, 10px); align-items: center; gap: 6px;
141
+ padding: 6px 8px; font-size: 13px; line-height: 20px; display: flex; }
137
142
  .dci-slash-item[data-active="true"], .dci-slash-item:hover { background: var(--dsw-alias-interactive-bg-hover, color-mix(in srgb, currentColor 8%, transparent)); }
138
- .dci-slash-sectionTitle { min-height: 26px; color: var(--dsw-alias-label-tertiary, currentColor); flex: none;
139
- padding: 6px 10px 2px; font-size: 12px; font-weight: 500; line-height: 18px; }
140
- .dci-slash-sectionTitle:not(:first-child) { margin-top: 4px; }
141
- .dci-slash-itemIcon { width: 16px; height: 16px; color: var(--dsw-alias-label-tertiary, currentColor); flex: none;
143
+ .dci-slash-sectionTitle { min-height: 23px; color: var(--dsw-alias-label-tertiary, currentColor); flex: none;
144
+ padding: 5px 8px 2px; font-size: 11px; font-weight: 500; line-height: 16px; }
145
+ .dci-slash-sectionTitle:not(:first-child) { margin-top: 3px; }
146
+ .dci-slash-itemIcon { width: 14px; height: 14px; color: var(--dsw-alias-menu-icon, var(--dsw-alias-label-tertiary, currentColor)); flex: none;
142
147
  justify-content: center; align-items: center; display: inline-flex; }
143
- .dci-slash-itemIcon svg { width: 16px; height: 16px; }
148
+ .dci-slash-itemIcon svg { width: 14px; height: 14px; }
144
149
  .dci-slash-itemName { text-overflow: ellipsis; white-space: nowrap; flex: none; max-width: 40%; overflow: hidden; }
145
- .dci-slash-itemAlias { text-overflow: ellipsis; white-space: nowrap; max-width: 20%; color: var(--dsw-alias-label-tertiary, currentColor); flex: none; overflow: hidden; }
146
- .dci-slash-itemDescription { text-overflow: ellipsis; white-space: nowrap; min-width: 0;
147
- color: var(--dsw-alias-label-tertiary, currentColor); flex: 1; overflow: hidden; }
148
- .dci-slash-trailing { flex: none; align-items: center; gap: 4px; margin-left: auto; display: inline-flex; }
149
- .dci-slash-drillHintText { color: var(--dsw-alias-label-caption, currentColor); white-space: nowrap; font-size: 11px;
150
- line-height: 18px; display: none; }
151
- .dci-slash-drillHint { background: var(--dsw-alias-interactive-bg-hover, color-mix(in srgb, currentColor 8%, transparent));
152
- color: var(--dsw-alias-label-caption, currentColor); border-radius: 4px; padding: 0 5px; font-family: inherit;
153
- font-size: 11px; line-height: 18px; display: none; }
150
+ .dci-slash-itemAlias { text-overflow: ellipsis; white-space: nowrap; max-width: 20%; color: var(--dsw-alias-label-tertiary, currentColor); flex: none;
151
+ font-size: 12px; line-height: 18px; overflow: hidden; }
152
+ .dci-slash-itemDescription { text-overflow: ellipsis; white-space: nowrap; text-align: right; min-width: 0;
153
+ color: var(--dsw-alias-label-tertiary, currentColor); flex: 1; font-size: 12px; line-height: 18px; overflow: hidden; }
154
+ .dci-slash-trailing { flex: none; align-items: center; gap: 3px; margin-left: auto; display: inline-flex; }
155
+ .dci-slash-drillHintText { color: var(--dsw-alias-label-caption, currentColor); white-space: nowrap; font-size: 10px;
156
+ line-height: 16px; display: none; }
157
+ .dci-slash-drillHint { border-radius: var(--dsw-radius-xs, 4px); background: var(--dsw-alias-interactive-bg-hover, color-mix(in srgb, currentColor 8%, transparent));
158
+ color: var(--dsw-alias-label-caption, currentColor); padding: 0 4px; font-family: inherit;
159
+ font-size: 10px; line-height: 16px; display: none; }
154
160
  .dci-slash-item[data-active="true"] .dci-slash-drillHintText, .dci-slash-item[data-active="true"] .dci-slash-drillHint { display: inline-flex; }
155
- .dci-slash-drill { width: 20px; height: 20px; color: var(--dsw-alias-label-caption, currentColor); border-radius: 4px;
161
+ .dci-slash-drill { width: 18px; height: 18px; border-radius: var(--dsw-radius-xs, 4px); color: var(--dsw-alias-menu-icon, var(--dsw-alias-label-caption, currentColor));
156
162
  flex: none; place-items: center; display: inline-grid; cursor: pointer; }
157
163
  .dci-slash-drill:hover { background: var(--dsw-alias-interactive-bg-hover, color-mix(in srgb, currentColor 8%, transparent));
158
164
  color: var(--dsw-alias-label-primary, inherit); }
159
- .dci-slash-groupTitle { color: var(--dsw-alias-label-tertiary, currentColor); padding: 8px 10px; font-size: 12px; line-height: 16px; }
160
- .dci-slash-skeletonRow { box-sizing: border-box; align-items: center; min-height: 40px; padding: 8px 10px; display: flex; }
165
+ .dci-slash-groupTitle { color: var(--dsw-alias-label-tertiary, currentColor); padding: 6px 8px; font-size: 11px; line-height: 15px; }
166
+ .dci-slash-skeletonRow { box-sizing: border-box; align-items: center; min-height: 34px; padding: 6px 8px; display: flex; }
161
167
  .dci-slash-skeletonBar { background: var(--dsw-alias-bg-skeleton, color-mix(in srgb, currentColor 10%, transparent));
162
- border-radius: 4px; height: 20px; animation: dci-slash-skeleton 2s cubic-bezier(0.36, 0, 0.64, 1) infinite; }
168
+ border-radius: var(--dsw-radius-xs, 4px); height: 18px; animation: dci-slash-skeleton 2s cubic-bezier(0.36, 0, 0.64, 1) infinite; }
163
169
  @keyframes dci-slash-skeleton { 0% { opacity: 1; } 40% { opacity: 0.6; } 80%, 100% { opacity: 1; } }
164
170
  .dci-slash-crumbs { border-bottom: 0.5px solid var(--dsw-alias-border-l1, transparent); flex-wrap: wrap; flex: none;
165
- align-items: center; gap: 2px; margin-bottom: 2px; padding: 4px 4px 6px; display: flex; }
171
+ align-items: center; gap: 2px; margin-bottom: 2px; padding: 3px 3px 5px; display: flex; }
166
172
  .dci-slash-crumb { max-width: 40%; color: var(--dsw-alias-label-tertiary, currentColor); cursor: pointer;
167
- text-overflow: ellipsis; white-space: nowrap; background: transparent; border: none; border-radius: 6px;
168
- flex: 0 1 auto; padding: 2px 6px; font-family: inherit; font-size: 12px; line-height: 18px; overflow: hidden; }
173
+ text-overflow: ellipsis; white-space: nowrap; background: transparent; border: none; border-radius: var(--dsw-radius-sm, 6px);
174
+ flex: 0 auto; padding: 2px 5px; font-family: inherit; font-size: 11px; line-height: 16px; overflow: hidden; }
169
175
  .dci-slash-crumb:hover:not(:disabled) { background: var(--dsw-alias-interactive-bg-hover, color-mix(in srgb, currentColor 8%, transparent));
170
176
  color: var(--dsw-alias-label-primary, inherit); }
171
177
  .dci-slash-crumb:disabled { color: var(--dsw-alias-label-primary, inherit); cursor: default; background: transparent; }
172
- .dci-slash-crumbSeparator { color: var(--dsw-alias-label-caption, currentColor); flex: none; display: inline-flex; }
178
+ .dci-slash-crumbSeparator { color: var(--dsw-alias-menu-icon, var(--dsw-alias-label-caption, currentColor)); flex: none; display: inline-flex; }
173
179
 
174
180
  .dci-crumb-row { display: flex; flex-wrap: wrap; gap: 2px; padding: 4px 6px; }
175
181
  .dci-crumb { border: none; background: transparent; color: var(--dsw-alias-label-secondary, inherit); cursor: pointer;
@@ -389,7 +395,8 @@ button.dci-attach-body { color: inherit; font: inherit; text-align: left; backgr
389
395
  .dci-stat-anchor { min-width: 0; display: inline-flex; position: relative; }
390
396
 
391
397
  /* 数据行详情面板(官方 bRhRbq 复刻:锚点上方弹出) */
392
- .dci-stat-panel { z-index: 1100; box-sizing: border-box; background: var(--dsw-specific-menu, rgba(30, 30, 34, 0.98));
398
+ .dci-stat-panel { z-index: 1100; box-sizing: border-box; background: var(--dsw-menu-surface-fill, var(--dsw-specific-menu, rgba(30, 30, 34, 0.98)));
399
+ backdrop-filter: var(--dsw-menu-backdrop-filter, none);
393
400
  color: var(--dsw-alias-label-secondary, inherit); --dsw-elevation-stroke-color: var(--dsw-alias-border-l1, transparent);
394
401
  width: max-content; min-width: min(300px, 100vw - 24px); max-width: min(440px, 100vw - 24px); text-align: left;
395
402
  box-shadow: var(--dsw-elevation-prominent, 0 10px 34px rgba(0, 0, 0, 0.35)); cursor: default; border: 0;
@@ -413,17 +420,21 @@ button.dci-attach-body { color: inherit; font: inherit; text-align: left; backgr
413
420
  .dci-stat-label { text-overflow: ellipsis; min-width: 0; overflow: hidden; }
414
421
  .dci-stat-sep { color: var(--dsw-alias-separator-primary, currentColor); margin: 0 6px; }
415
422
 
416
- /* ===== 上下文小圈(官方 JObwrW 复刻:双环仪表 + 分段面板)===== */
417
- .dci-gauge-root { display: inline-flex; position: relative; }
418
- .dci-gauge-trigger { width: 28px; height: 28px; color: var(--dsw-alias-label-secondary, currentColor); cursor: pointer;
419
- background: transparent; border: none; border-radius: 999px; flex: none; place-items: center; display: grid; }
420
- .dci-gauge-trigger:hover { background: var(--dsw-alias-interactive-bg-hover, color-mix(in srgb, currentColor 8%, transparent)); }
423
+ /* ===== 上下文小圈(官方 JObwrW 复刻:双环仪表 + 分段面板)
424
+ 0.1.7-rc.2 起官方把它挪进卡片下方 dock 行(stats 胶囊之后),trigger 变为「圈+百分比」药丸 ===== */
425
+ .dci-gauge-root { flex: none; display: inline-flex; position: relative; }
426
+ .dci-gauge-trigger { border-radius: var(--dsw-radius-sm, 8px); color: var(--dsw-alias-label-tertiary, currentColor);
427
+ font-family: inherit; font-size: 13px; font-variant-numeric: tabular-nums; line-height: 20px; white-space: nowrap;
428
+ cursor: pointer; background: transparent; border: none; flex: none; align-items: center; gap: 6px; padding: 1px 8px; display: inline-flex; }
429
+ .dci-gauge-trigger:hover, .dci-gauge-trigger[aria-expanded="true"] { background: var(--dsw-alias-interactive-bg-hover, color-mix(in srgb, currentColor 8%, transparent));
430
+ color: var(--dsw-alias-label-secondary, inherit); }
421
431
  .dci-gauge-track { fill: none; stroke: var(--dsw-alias-border-l3, currentColor); stroke-width: 2px; }
422
432
  .dci-gauge-fill { fill: none; stroke: var(--dsw-alias-label-tertiary, currentColor); stroke-width: 2px; stroke-linecap: round; }
423
- .dci-gauge-panel { z-index: 1100; box-sizing: border-box; background: var(--dsw-specific-menu, rgba(30, 30, 34, 0.98));
424
- --dsw-elevation-stroke-color: var(--dsw-alias-border-l1, transparent); width: 264px;
433
+ .dci-gauge-panel { z-index: 1100; box-sizing: border-box; background: var(--dsw-menu-surface-fill, var(--dsw-specific-menu, rgba(30, 30, 34, 0.98)));
434
+ backdrop-filter: var(--dsw-menu-backdrop-filter, none);
435
+ --dsw-elevation-stroke-color: var(--dsw-alias-border-l1, transparent); width: min(264px, 100vw - 24px);
425
436
  box-shadow: var(--dsw-elevation-prominent, 0 10px 34px rgba(0, 0, 0, 0.35));
426
- color: var(--dsw-alias-label-secondary, inherit); cursor: default; border: 0; border-radius: 12px; padding: 12px;
437
+ color: var(--dsw-alias-label-secondary, inherit); cursor: default; border: 0; border-radius: var(--dsw-radius-lg, 12px); padding: 12px;
427
438
  font-size: 12px; line-height: 20px; position: absolute; bottom: calc(100% + 8px); right: 0; }
428
439
  .dci-gauge-header { align-items: center; gap: 6px; display: flex; }
429
440
  .dci-gauge-figures { font-variant-numeric: tabular-nums; color: var(--dsw-alias-label-primary, inherit); margin-left: auto; font-weight: 500; }