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/CHANGELOG.md +20 -0
- package/lib/client.js +159 -40
- package/package.json +2 -1
- package/src/client/components/AttachmentRail.tsx +3 -3
- package/src/client/components/StatsRow.tsx +17 -17
- package/src/client/components/UserMessageMarkdown.tsx +591 -0
- package/src/client/i18n.ts +16 -0
- package/src/client/index.tsx +41 -0
- package/src/client/prefs.ts +3 -0
- package/src/client/settings.tsx +7 -0
- package/src/client/styles.ts +118 -0
package/package.json
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-codinput",
|
|
3
|
-
"version": "0.3.
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
257
|
-
|
|
258
|
-
|
|
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={
|
|
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" />
|