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/CHANGELOG.md +22 -0
- package/README.en.md +5 -4
- package/README.md +5 -6
- package/lib/client.js +81 -70
- package/package.json +1 -1
- package/src/client/components/SlashMenu.tsx +1 -1
- package/src/client/components/StatsRow.tsx +140 -6
- package/src/client/components/Toolbar.tsx +2 -121
- package/src/client/stats.ts +5 -4
- package/src/client/styles.ts +52 -41
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
/** 数据行(卡片下方):官方 composer
|
|
2
|
-
*
|
|
3
|
-
*
|
|
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
|
-
|
|
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
|
-
|
|
185
|
-
|
|
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 {
|
|
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
|
-
}
|
package/src/client/stats.ts
CHANGED
|
@@ -23,7 +23,7 @@ export interface SessionStats {
|
|
|
23
23
|
tokensPerSecond?: number;
|
|
24
24
|
totalTokens?: number;
|
|
25
25
|
cacheHitPct?: number;
|
|
26
|
-
/** 上下文占用百分比(pressureTokens
|
|
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
|
-
|
|
156
|
-
|
|
157
|
-
|
|
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
|
}
|
package/src/client/styles.ts
CHANGED
|
@@ -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))
|
|
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
|
-
.
|
|
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))
|
|
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:
|
|
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:
|
|
135
|
-
text-align: left; background: transparent; border: none; border-radius: 10px; align-items: center; gap:
|
|
136
|
-
padding: 8px
|
|
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:
|
|
139
|
-
padding:
|
|
140
|
-
.dci-slash-sectionTitle:not(:first-child) { margin-top:
|
|
141
|
-
.dci-slash-itemIcon { width:
|
|
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:
|
|
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;
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
.dci-slash-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
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:
|
|
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
|
|
160
|
-
.dci-slash-skeletonRow { box-sizing: border-box; align-items: center; min-height:
|
|
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:
|
|
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:
|
|
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
|
|
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
|
-
.
|
|
418
|
-
.dci-gauge-
|
|
419
|
-
|
|
420
|
-
|
|
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
|
-
|
|
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; }
|