dsh-working-activity 0.2.6 → 0.3.0
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/LICENSE +28 -28
- package/README.i18n.yaml +6 -6
- package/README.md +122 -122
- package/README.zh.md +113 -113
- package/cordis.patch.yml +19 -19
- package/lib/client.js +6 -6
- package/lib/client.js.map +1 -1
- package/lib/types/index.d.ts +3 -0
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/index.js +11 -4
- package/lib/types/lang.d.ts +90 -0
- package/lib/types/lang.d.ts.map +1 -0
- package/lib/types/lang.js +128 -0
- package/lib/types/phrases.d.ts +39 -5
- package/lib/types/phrases.d.ts.map +1 -1
- package/lib/types/phrases.js +115 -11
- package/lib/types/status.d.ts.map +1 -1
- package/lib/types/status.js +26 -16
- package/package.json +119 -117
- package/src/client/WorkingLine.module.css +74 -74
- package/src/client/WorkingLine.tsx +42 -42
- package/src/client/activity.ts +50 -50
- package/src/client/css-modules.d.ts +6 -6
- package/src/client/index.ts +42 -42
- package/src/events.ts +45 -45
- package/src/index.ts +237 -228
- package/src/invariant.ts +69 -69
- package/src/lang.ts +141 -0
- package/src/phrases.ts +306 -180
- package/src/registration.ts +99 -99
- package/src/status.ts +511 -501
package/lib/types/status.js
CHANGED
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
* no timers, no cordis — deterministic given the event stream and a clock.
|
|
6
6
|
* @module @deepseek-ai/dsh-working-activity/status
|
|
7
7
|
*/
|
|
8
|
-
import { actionFor, fmtDuration, isGitTool, isNight,
|
|
8
|
+
import { actionFor, donePhrase, failPhrase, fmtDuration, isGitTool, isNight, thinkingPhrase, waitingPhrase, } from './phrases.js';
|
|
9
|
+
import { t } from './lang.js';
|
|
9
10
|
/** Format one tool into its display fragment (`跑个命令 npm test`). */
|
|
10
11
|
function toolFragment(tool) {
|
|
11
12
|
return tool.detail.length === 0 ? tool.action : `${tool.action} ${tool.detail}`;
|
|
@@ -94,7 +95,7 @@ export class ActivityTracker {
|
|
|
94
95
|
/** Total tokens reported across the turn's assistant messages. */
|
|
95
96
|
turnTokens = 0;
|
|
96
97
|
/** Completion prefix drawn ONCE at turn end so the done line stays stable. */
|
|
97
|
-
donePrefix = '
|
|
98
|
+
donePrefix = t('done-prefix');
|
|
98
99
|
/**
|
|
99
100
|
* @param config - Behavioral knobs.
|
|
100
101
|
* @param now - Wall-clock supplier (injectable for tests).
|
|
@@ -231,13 +232,15 @@ export class ActivityTracker {
|
|
|
231
232
|
this.thinkingMs += Math.max(0, at - this.thinkingStartedAt);
|
|
232
233
|
}
|
|
233
234
|
// Draw the completion prefix ONCE so repeated renders of the done line
|
|
234
|
-
// stay stable (a fresh random per render would make it flicker).
|
|
235
|
+
// stay stable (a fresh random per render would make it flicker). The
|
|
236
|
+
// pools are language-aware, so the line matches the language the turn
|
|
237
|
+
// ended in.
|
|
235
238
|
const lastTool = this.doneQueue.at(-1);
|
|
236
239
|
if (this.config.phrases) {
|
|
237
|
-
this.donePrefix = lastTool?.failed ?
|
|
240
|
+
this.donePrefix = lastTool?.failed ? failPhrase() : donePhrase();
|
|
238
241
|
}
|
|
239
242
|
else {
|
|
240
|
-
this.donePrefix = '
|
|
243
|
+
this.donePrefix = t('done-prefix');
|
|
241
244
|
}
|
|
242
245
|
this.setPhase('done', at);
|
|
243
246
|
return;
|
|
@@ -314,11 +317,12 @@ export class ActivityTracker {
|
|
|
314
317
|
? 0
|
|
315
318
|
: this.thinkingMs + Math.max(0, nowMs - this.thinkingStartedAt);
|
|
316
319
|
const elapsed = fmtDuration(this.turnElapsedMs(nowMs));
|
|
320
|
+
const elapsedLine = t('line-elapsed', { elapsed });
|
|
317
321
|
const narration = this.freshNarration(nowMs);
|
|
318
322
|
if (narration !== null) {
|
|
319
323
|
return {
|
|
320
324
|
phase: this.phase,
|
|
321
|
-
line: `⏵ ${narration} ·
|
|
325
|
+
line: `⏵ ${narration} · ${elapsedLine}`,
|
|
322
326
|
phrase: narration,
|
|
323
327
|
toolCount: this.toolCount,
|
|
324
328
|
turnElapsedMs: this.turnElapsedMs(nowMs),
|
|
@@ -328,28 +332,29 @@ export class ActivityTracker {
|
|
|
328
332
|
if (this.config.phrases) {
|
|
329
333
|
if (nowMs - this.phraseChangedAt >= PHRASE_ROTATE_MS) {
|
|
330
334
|
// Waiting (pre-first-token) draws from the waiting pool; thinking
|
|
331
|
-
// rotates the playful copy pool with night mixing.
|
|
335
|
+
// rotates the playful copy pool with night mixing. Both pools are
|
|
336
|
+
// language-aware, so a `/lang` switch shows on the next rotation.
|
|
332
337
|
this.previousPhrase = this.phase === 'waiting'
|
|
333
|
-
?
|
|
338
|
+
? waitingPhrase(this.previousPhrase)
|
|
334
339
|
: thinkingPhrase(thinkingMs, this.previousPhrase, isNight(new Date(nowMs).getHours()));
|
|
335
340
|
this.phraseChangedAt = nowMs;
|
|
336
341
|
}
|
|
337
342
|
const phrase = this.previousPhrase ?? (this.phase === 'waiting'
|
|
338
|
-
?
|
|
343
|
+
? waitingPhrase()
|
|
339
344
|
: thinkingPhrase(thinkingMs, undefined, isNight(new Date(nowMs).getHours())));
|
|
340
345
|
return {
|
|
341
346
|
phase: this.phase,
|
|
342
|
-
line: `${phrase} ·
|
|
347
|
+
line: `${phrase} · ${elapsedLine}`,
|
|
343
348
|
phrase,
|
|
344
349
|
toolCount: this.toolCount,
|
|
345
350
|
turnElapsedMs: this.turnElapsedMs(nowMs),
|
|
346
351
|
phaseStartedAt: this.phaseStartedAt,
|
|
347
352
|
};
|
|
348
353
|
}
|
|
349
|
-
const label = this.phase === 'waiting' ? '
|
|
354
|
+
const label = this.phase === 'waiting' ? t('waiting-label') : t('thinking-label');
|
|
350
355
|
return {
|
|
351
356
|
phase: this.phase,
|
|
352
|
-
line: `${label} ·
|
|
357
|
+
line: `${label} · ${elapsedLine}`,
|
|
353
358
|
label,
|
|
354
359
|
toolCount: this.toolCount,
|
|
355
360
|
turnElapsedMs: this.turnElapsedMs(nowMs),
|
|
@@ -359,16 +364,21 @@ export class ActivityTracker {
|
|
|
359
364
|
doneSummary(nowMs) {
|
|
360
365
|
const { thinkingMs, toolMs, toolCount } = this.stats();
|
|
361
366
|
const tokens = this.turnTokens > 0 ? ` · 🔥 ${fmtTokens(this.turnTokens)}` : '';
|
|
362
|
-
const
|
|
367
|
+
const tools = t(toolCount === 1 ? 'tool-count-one' : 'tool-count-many', { count: toolCount });
|
|
368
|
+
const summary = t('done-summary', {
|
|
369
|
+
tools,
|
|
370
|
+
thinking: fmtDuration(thinkingMs),
|
|
371
|
+
tooling: fmtDuration(toolMs),
|
|
372
|
+
});
|
|
363
373
|
if (!this.config.phrases) {
|
|
364
|
-
return { line:
|
|
374
|
+
return { line: `${t('done-prefix')} · ${summary}${tokens}` };
|
|
365
375
|
}
|
|
366
376
|
const last = this.doneQueue.at(-1);
|
|
367
377
|
if (last !== undefined && nowMs - last.endedAt < DONE_FRAGMENT_MS) {
|
|
368
378
|
const fragment = toolFragment(last);
|
|
369
|
-
return { line: `${this.donePrefix} · ${fragment} · ${
|
|
379
|
+
return { line: `${this.donePrefix} · ${fragment} · ${tools}${tokens}`, phrase: this.donePrefix };
|
|
370
380
|
}
|
|
371
|
-
return { line:
|
|
381
|
+
return { line: `${this.donePrefix} · ${summary}${tokens}`, phrase: this.donePrefix };
|
|
372
382
|
}
|
|
373
383
|
/** The fresh self-narration line, or null once the stream has been quiet. */
|
|
374
384
|
freshNarration(nowMs) {
|
package/package.json
CHANGED
|
@@ -1,117 +1,119 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "dsh-working-activity",
|
|
3
|
-
"description": "Live model working-status line: playful copy, running tool, turn elapsed — for TUI prompt and Web UI",
|
|
4
|
-
"version": "0.
|
|
5
|
-
"type": "module",
|
|
6
|
-
"main": "lib/types/index.js",
|
|
7
|
-
"types": "lib/types/index.d.ts",
|
|
8
|
-
"exports": {
|
|
9
|
-
".": {
|
|
10
|
-
"types": "./lib/types/index.d.ts",
|
|
11
|
-
"default": "./lib/types/index.js"
|
|
12
|
-
},
|
|
13
|
-
"./client": {
|
|
14
|
-
"types": "./lib/types/client/index.d.ts",
|
|
15
|
-
"default": "./lib/client.js"
|
|
16
|
-
},
|
|
17
|
-
"./events": {
|
|
18
|
-
"types": "./lib/types/events.d.ts",
|
|
19
|
-
"default": "./lib/types/events.js"
|
|
20
|
-
},
|
|
21
|
-
"./invariant": {
|
|
22
|
-
"types": "./lib/types/invariant.d.ts",
|
|
23
|
-
"default": "./lib/types/invariant.js"
|
|
24
|
-
},
|
|
25
|
-
"./status": {
|
|
26
|
-
"types": "./lib/types/status.d.ts",
|
|
27
|
-
"default": "./lib/types/status.js"
|
|
28
|
-
},
|
|
29
|
-
"./cordis.patch.yml": "./cordis.patch.yml",
|
|
30
|
-
"./src/*": "./src/*",
|
|
31
|
-
"./package.json": "./package.json"
|
|
32
|
-
},
|
|
33
|
-
"files": [
|
|
34
|
-
"lib",
|
|
35
|
-
"src",
|
|
36
|
-
"cordis.patch.yml"
|
|
37
|
-
],
|
|
38
|
-
"engines": {
|
|
39
|
-
"node": "^22.19 || >=24"
|
|
40
|
-
},
|
|
41
|
-
"scripts": {
|
|
42
|
-
"build": "tsc -p tsconfig.json && tsc -p tsconfig.client.json",
|
|
43
|
-
"build:client": "tsdown -c tsdown.config.ts",
|
|
44
|
-
"
|
|
45
|
-
"verify:
|
|
46
|
-
"
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
"
|
|
50
|
-
|
|
51
|
-
"
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
"
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
"
|
|
64
|
-
|
|
65
|
-
"@deepseek-ai/dsh-client-
|
|
66
|
-
"@deepseek-ai/dsh-client-ui-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
"@deepseek-ai/
|
|
73
|
-
"@deepseek-ai/dsh-
|
|
74
|
-
"@deepseek-ai/dsh-client-
|
|
75
|
-
"@deepseek-ai/dsh-client-ui-
|
|
76
|
-
"@deepseek-ai/dsh-
|
|
77
|
-
"@deepseek-ai/dsh-
|
|
78
|
-
"@deepseek-ai/dsh-
|
|
79
|
-
"@deepseek-ai/
|
|
80
|
-
"
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
"@deepseek-ai/
|
|
85
|
-
"@deepseek-ai/dsh-agent
|
|
86
|
-
"@deepseek-ai/dsh-agent-loop
|
|
87
|
-
"@deepseek-ai/dsh-
|
|
88
|
-
"@deepseek-ai/dsh-client-
|
|
89
|
-
"@deepseek-ai/dsh-client-ui-
|
|
90
|
-
"@deepseek-ai/dsh-
|
|
91
|
-
"@deepseek-ai/dsh-
|
|
92
|
-
"@deepseek-ai/dsh-
|
|
93
|
-
"@deepseek-ai/dsh-
|
|
94
|
-
"@deepseek-ai/dsh-
|
|
95
|
-
"@deepseek-ai/
|
|
96
|
-
"@
|
|
97
|
-
"@types/
|
|
98
|
-
"
|
|
99
|
-
"
|
|
100
|
-
"
|
|
101
|
-
"
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "dsh-working-activity",
|
|
3
|
+
"description": "Live model working-status line: playful copy, running tool, turn elapsed — for TUI prompt and Web UI",
|
|
4
|
+
"version": "0.3.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "lib/types/index.js",
|
|
7
|
+
"types": "lib/types/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./lib/types/index.d.ts",
|
|
11
|
+
"default": "./lib/types/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./client": {
|
|
14
|
+
"types": "./lib/types/client/index.d.ts",
|
|
15
|
+
"default": "./lib/client.js"
|
|
16
|
+
},
|
|
17
|
+
"./events": {
|
|
18
|
+
"types": "./lib/types/events.d.ts",
|
|
19
|
+
"default": "./lib/types/events.js"
|
|
20
|
+
},
|
|
21
|
+
"./invariant": {
|
|
22
|
+
"types": "./lib/types/invariant.d.ts",
|
|
23
|
+
"default": "./lib/types/invariant.js"
|
|
24
|
+
},
|
|
25
|
+
"./status": {
|
|
26
|
+
"types": "./lib/types/status.d.ts",
|
|
27
|
+
"default": "./lib/types/status.js"
|
|
28
|
+
},
|
|
29
|
+
"./cordis.patch.yml": "./cordis.patch.yml",
|
|
30
|
+
"./src/*": "./src/*",
|
|
31
|
+
"./package.json": "./package.json"
|
|
32
|
+
},
|
|
33
|
+
"files": [
|
|
34
|
+
"lib",
|
|
35
|
+
"src",
|
|
36
|
+
"cordis.patch.yml"
|
|
37
|
+
],
|
|
38
|
+
"engines": {
|
|
39
|
+
"node": "^22.19 || >=24"
|
|
40
|
+
},
|
|
41
|
+
"scripts": {
|
|
42
|
+
"build": "tsc -p tsconfig.json && tsc -p tsconfig.client.json",
|
|
43
|
+
"build:client": "tsdown -c tsdown.config.ts",
|
|
44
|
+
"test": "vitest run",
|
|
45
|
+
"verify:deps": "node scripts/verify-manifest-deps.mjs",
|
|
46
|
+
"verify:lockfiles": "node scripts/verify-lockfiles.mjs",
|
|
47
|
+
"prepublishOnly": "npm run verify:deps && npm run verify:lockfiles && npm run build && npm run build:client"
|
|
48
|
+
},
|
|
49
|
+
"license": "BSD-3-Clause",
|
|
50
|
+
"repository": {
|
|
51
|
+
"type": "git",
|
|
52
|
+
"url": "git+https://github.com/ccch1mneyyy/working-activity.git"
|
|
53
|
+
},
|
|
54
|
+
"bugs": {
|
|
55
|
+
"url": "https://github.com/ccch1mneyyy/working-activity/issues"
|
|
56
|
+
},
|
|
57
|
+
"homepage": "https://github.com/ccch1mneyyy/working-activity#readme",
|
|
58
|
+
"dsh": {
|
|
59
|
+
"bundle": {
|
|
60
|
+
"patch": "./cordis.patch.yml"
|
|
61
|
+
},
|
|
62
|
+
"client": {
|
|
63
|
+
"platform": "web",
|
|
64
|
+
"inject": [
|
|
65
|
+
"@deepseek-ai/dsh-client-runtime",
|
|
66
|
+
"@deepseek-ai/dsh-client-ui-conversation",
|
|
67
|
+
"@deepseek-ai/dsh-client-ui-slots"
|
|
68
|
+
]
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
"peerDependencies": {
|
|
72
|
+
"@deepseek-ai/cordis": "^4.0.1",
|
|
73
|
+
"@deepseek-ai/dsh-agent": "^0.1.0-rc.6",
|
|
74
|
+
"@deepseek-ai/dsh-client-runtime": "^0.1.0-rc.6",
|
|
75
|
+
"@deepseek-ai/dsh-client-ui-conversation": "^0.1.0-rc.6",
|
|
76
|
+
"@deepseek-ai/dsh-client-ui-slots": "^0.1.0-rc.6",
|
|
77
|
+
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.6",
|
|
78
|
+
"@deepseek-ai/dsh-session": "^0.1.0-rc.6",
|
|
79
|
+
"@deepseek-ai/dsh-system-prompt": "^0.1.0-rc.6",
|
|
80
|
+
"@deepseek-ai/schemastery": "^3.18.1",
|
|
81
|
+
"react": "^18.2.0"
|
|
82
|
+
},
|
|
83
|
+
"devDependencies": {
|
|
84
|
+
"@deepseek-ai/cordis": "^4.0.1",
|
|
85
|
+
"@deepseek-ai/dsh-agent": "^0.1.0-rc.6",
|
|
86
|
+
"@deepseek-ai/dsh-agent-loop": "^0.1.0-rc.6",
|
|
87
|
+
"@deepseek-ai/dsh-agent-loop-testkit": "^0.1.0-rc.6",
|
|
88
|
+
"@deepseek-ai/dsh-client-runtime": "^0.1.0-rc.6",
|
|
89
|
+
"@deepseek-ai/dsh-client-ui-conversation": "^0.1.0-rc.6",
|
|
90
|
+
"@deepseek-ai/dsh-client-ui-slots": "^0.1.0-rc.6",
|
|
91
|
+
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.6",
|
|
92
|
+
"@deepseek-ai/dsh-llm": "^0.1.0-rc.6",
|
|
93
|
+
"@deepseek-ai/dsh-session": "^0.1.0-rc.6",
|
|
94
|
+
"@deepseek-ai/dsh-system-prompt": "^0.1.0-rc.6",
|
|
95
|
+
"@deepseek-ai/dsh-tools": "^0.1.0-rc.6",
|
|
96
|
+
"@deepseek-ai/schemastery": "^3.18.1",
|
|
97
|
+
"@types/node": "^22.0.0",
|
|
98
|
+
"@types/react": "~18.3.1",
|
|
99
|
+
"lightningcss": "^1.32.0",
|
|
100
|
+
"react": "^18.2.0",
|
|
101
|
+
"tsdown": "^0.22.2",
|
|
102
|
+
"typescript": "^6.0.3",
|
|
103
|
+
"vitest": "^3.2.4"
|
|
104
|
+
},
|
|
105
|
+
"peerDependenciesMeta": {
|
|
106
|
+
"@deepseek-ai/dsh-client-runtime": {
|
|
107
|
+
"optional": true
|
|
108
|
+
},
|
|
109
|
+
"@deepseek-ai/dsh-client-ui-conversation": {
|
|
110
|
+
"optional": true
|
|
111
|
+
},
|
|
112
|
+
"@deepseek-ai/dsh-client-ui-slots": {
|
|
113
|
+
"optional": true
|
|
114
|
+
},
|
|
115
|
+
"react": {
|
|
116
|
+
"optional": true
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
@@ -1,74 +1,74 @@
|
|
|
1
|
-
/* Working line row: one dim line with a phase-colored breathing marker and
|
|
2
|
-
the turn's tool-count badge, sized to the composer dock inset like the
|
|
3
|
-
queue strip. Mirrors the pre-slots patch's WorkingLine visuals, re-targeted
|
|
4
|
-
at the rc.6 design tokens. */
|
|
5
|
-
|
|
6
|
-
.line {
|
|
7
|
-
box-sizing: border-box;
|
|
8
|
-
display: flex;
|
|
9
|
-
align-items: center;
|
|
10
|
-
gap: 8px;
|
|
11
|
-
width: 100%;
|
|
12
|
-
max-width: var(--dsh-composer-card-max-width);
|
|
13
|
-
margin: 0 auto;
|
|
14
|
-
padding: 4px 16px 0;
|
|
15
|
-
font-family: var(--dsw-font-family);
|
|
16
|
-
font-size: 13px;
|
|
17
|
-
line-height: 18px;
|
|
18
|
-
color: var(--dsw-alias-label-secondary);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
.marker {
|
|
22
|
-
flex: none;
|
|
23
|
-
width: 7px;
|
|
24
|
-
height: 7px;
|
|
25
|
-
border-radius: 50%;
|
|
26
|
-
background: var(--dsw-alias-label-tertiary);
|
|
27
|
-
animation: working-pulse 1.6s ease-in-out infinite;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/* The model's turn is done: steady marker in the brand tone, no pulse. */
|
|
31
|
-
.line[data-activity-phase='done'] .marker {
|
|
32
|
-
background: var(--dsw-alias-state-business-primary);
|
|
33
|
-
animation: none;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/* A tool is actually executing: brand-tone pulse. */
|
|
37
|
-
.line[data-activity-phase='tool'] .marker {
|
|
38
|
-
background: var(--dsw-alias-state-business-primary);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/* Waiting for the first token: muted pulse. */
|
|
42
|
-
.line[data-activity-phase='waiting'] .marker {
|
|
43
|
-
background: var(--dsw-alias-label-tertiary);
|
|
44
|
-
opacity: 0.6;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
.text {
|
|
48
|
-
overflow: hidden;
|
|
49
|
-
text-overflow: ellipsis;
|
|
50
|
-
white-space: nowrap;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
.tools {
|
|
54
|
-
flex: none;
|
|
55
|
-
min-width: 18px;
|
|
56
|
-
padding: 0 5px;
|
|
57
|
-
border-radius: 9px;
|
|
58
|
-
background: var(--dsw-alias-surface-tertiary);
|
|
59
|
-
color: var(--dsw-alias-label-secondary);
|
|
60
|
-
font-size: 11px;
|
|
61
|
-
line-height: 18px;
|
|
62
|
-
text-align: center;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
@keyframes working-pulse {
|
|
66
|
-
0%,
|
|
67
|
-
100% {
|
|
68
|
-
opacity: 1;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
50% {
|
|
72
|
-
opacity: 0.35;
|
|
73
|
-
}
|
|
74
|
-
}
|
|
1
|
+
/* Working line row: one dim line with a phase-colored breathing marker and
|
|
2
|
+
the turn's tool-count badge, sized to the composer dock inset like the
|
|
3
|
+
queue strip. Mirrors the pre-slots patch's WorkingLine visuals, re-targeted
|
|
4
|
+
at the rc.6 design tokens. */
|
|
5
|
+
|
|
6
|
+
.line {
|
|
7
|
+
box-sizing: border-box;
|
|
8
|
+
display: flex;
|
|
9
|
+
align-items: center;
|
|
10
|
+
gap: 8px;
|
|
11
|
+
width: 100%;
|
|
12
|
+
max-width: var(--dsh-composer-card-max-width);
|
|
13
|
+
margin: 0 auto;
|
|
14
|
+
padding: 4px 16px 0;
|
|
15
|
+
font-family: var(--dsw-font-family);
|
|
16
|
+
font-size: 13px;
|
|
17
|
+
line-height: 18px;
|
|
18
|
+
color: var(--dsw-alias-label-secondary);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.marker {
|
|
22
|
+
flex: none;
|
|
23
|
+
width: 7px;
|
|
24
|
+
height: 7px;
|
|
25
|
+
border-radius: 50%;
|
|
26
|
+
background: var(--dsw-alias-label-tertiary);
|
|
27
|
+
animation: working-pulse 1.6s ease-in-out infinite;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/* The model's turn is done: steady marker in the brand tone, no pulse. */
|
|
31
|
+
.line[data-activity-phase='done'] .marker {
|
|
32
|
+
background: var(--dsw-alias-state-business-primary);
|
|
33
|
+
animation: none;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/* A tool is actually executing: brand-tone pulse. */
|
|
37
|
+
.line[data-activity-phase='tool'] .marker {
|
|
38
|
+
background: var(--dsw-alias-state-business-primary);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/* Waiting for the first token: muted pulse. */
|
|
42
|
+
.line[data-activity-phase='waiting'] .marker {
|
|
43
|
+
background: var(--dsw-alias-label-tertiary);
|
|
44
|
+
opacity: 0.6;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.text {
|
|
48
|
+
overflow: hidden;
|
|
49
|
+
text-overflow: ellipsis;
|
|
50
|
+
white-space: nowrap;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.tools {
|
|
54
|
+
flex: none;
|
|
55
|
+
min-width: 18px;
|
|
56
|
+
padding: 0 5px;
|
|
57
|
+
border-radius: 9px;
|
|
58
|
+
background: var(--dsw-alias-surface-tertiary);
|
|
59
|
+
color: var(--dsw-alias-label-secondary);
|
|
60
|
+
font-size: 11px;
|
|
61
|
+
line-height: 18px;
|
|
62
|
+
text-align: center;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
@keyframes working-pulse {
|
|
66
|
+
0%,
|
|
67
|
+
100% {
|
|
68
|
+
opacity: 1;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
50% {
|
|
72
|
+
opacity: 0.35;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
@@ -1,42 +1,42 @@
|
|
|
1
|
-
// Working-line dock entry: one dim full-width row above the composer card
|
|
2
|
-
// showing the live working-activity snapshot — phase-colored breathing
|
|
3
|
-
// marker, the host-composed status line, and the turn's tool count badge.
|
|
4
|
-
// Renders for every live phase (waiting/thinking/tool) and the done summary;
|
|
5
|
-
// hides while idle or before the first publish.
|
|
6
|
-
//
|
|
7
|
-
// The 'conversation.input.dock' SlotMap declaration lives in
|
|
8
|
-
// @deepseek-ai/dsh-client-ui-conversation/client (contract/slots.ts); this
|
|
9
|
-
// entry contributes into it without owning it.
|
|
10
|
-
import type { PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots'
|
|
11
|
-
import css from './WorkingLine.module.css'
|
|
12
|
-
|
|
13
|
-
/** Full props of the dock entry: the input-zone runtime share (session standard kit). */
|
|
14
|
-
export type WorkingLineProps = PropsRuntime<'conversation.input.dock'>
|
|
15
|
-
|
|
16
|
-
/** Tool-count badge copy (no locale seat: the line text itself is host-composed). */
|
|
17
|
-
const TOOLS_LABEL = 'tools this turn'
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Working-line dock entry: reads the latest activity snapshot off the
|
|
21
|
-
* conversation snapshot and renders the row, or nothing when idle/absent.
|
|
22
|
-
*/
|
|
23
|
-
export function WorkingLine({ useSession }: WorkingLineProps) {
|
|
24
|
-
// Defensive read: the runtime patch that puts `activity` onto the
|
|
25
|
-
// conversation snapshot ships separately — on an unpatched host the field
|
|
26
|
-
// is absent (undefined), not null, and this component must render nothing
|
|
27
|
-
// instead of crashing into the slot error boundary. Same never-throw
|
|
28
|
-
// discipline as the node side's registration.js.
|
|
29
|
-
const activity = useSession(s => s.activity ?? null)
|
|
30
|
-
if (activity === null || activity.phase === 'idle' || activity.line === '') return null
|
|
31
|
-
return (
|
|
32
|
-
<div className={css.line} data-activity-phase={activity.phase}>
|
|
33
|
-
<span className={css.marker} aria-hidden="true" />
|
|
34
|
-
<span className={css.text}>{activity.line}</span>
|
|
35
|
-
{activity.toolCount > 0 && (
|
|
36
|
-
<span className={css.tools} title={`${activity.toolCount} ${TOOLS_LABEL}`}>
|
|
37
|
-
{activity.toolCount}
|
|
38
|
-
</span>
|
|
39
|
-
)}
|
|
40
|
-
</div>
|
|
41
|
-
)
|
|
42
|
-
}
|
|
1
|
+
// Working-line dock entry: one dim full-width row above the composer card
|
|
2
|
+
// showing the live working-activity snapshot — phase-colored breathing
|
|
3
|
+
// marker, the host-composed status line, and the turn's tool count badge.
|
|
4
|
+
// Renders for every live phase (waiting/thinking/tool) and the done summary;
|
|
5
|
+
// hides while idle or before the first publish.
|
|
6
|
+
//
|
|
7
|
+
// The 'conversation.input.dock' SlotMap declaration lives in
|
|
8
|
+
// @deepseek-ai/dsh-client-ui-conversation/client (contract/slots.ts); this
|
|
9
|
+
// entry contributes into it without owning it.
|
|
10
|
+
import type { PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots'
|
|
11
|
+
import css from './WorkingLine.module.css'
|
|
12
|
+
|
|
13
|
+
/** Full props of the dock entry: the input-zone runtime share (session standard kit). */
|
|
14
|
+
export type WorkingLineProps = PropsRuntime<'conversation.input.dock'>
|
|
15
|
+
|
|
16
|
+
/** Tool-count badge copy (no locale seat: the line text itself is host-composed). */
|
|
17
|
+
const TOOLS_LABEL = 'tools this turn'
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Working-line dock entry: reads the latest activity snapshot off the
|
|
21
|
+
* conversation snapshot and renders the row, or nothing when idle/absent.
|
|
22
|
+
*/
|
|
23
|
+
export function WorkingLine({ useSession }: WorkingLineProps) {
|
|
24
|
+
// Defensive read: the runtime patch that puts `activity` onto the
|
|
25
|
+
// conversation snapshot ships separately — on an unpatched host the field
|
|
26
|
+
// is absent (undefined), not null, and this component must render nothing
|
|
27
|
+
// instead of crashing into the slot error boundary. Same never-throw
|
|
28
|
+
// discipline as the node side's registration.js.
|
|
29
|
+
const activity = useSession(s => s.activity ?? null)
|
|
30
|
+
if (activity === null || activity.phase === 'idle' || activity.line === '') return null
|
|
31
|
+
return (
|
|
32
|
+
<div className={css.line} data-activity-phase={activity.phase}>
|
|
33
|
+
<span className={css.marker} aria-hidden="true" />
|
|
34
|
+
<span className={css.text}>{activity.line}</span>
|
|
35
|
+
{activity.toolCount > 0 && (
|
|
36
|
+
<span className={css.tools} title={`${activity.toolCount} ${TOOLS_LABEL}`}>
|
|
37
|
+
{activity.toolCount}
|
|
38
|
+
</span>
|
|
39
|
+
)}
|
|
40
|
+
</div>
|
|
41
|
+
)
|
|
42
|
+
}
|