@shendeguize/dsh-agent-sidecar 0.1.0 → 0.2.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/README.md +78 -11
- package/lib/client.js +4708 -2932
- package/lib/client.js.map +1 -1
- package/package.json +3 -1
- package/src/client/analysis/AnalysisPanel.tsx +19 -27
- package/src/client/analysis/analysis.module.css +36 -97
- package/src/client/board/Board.tsx +255 -48
- package/src/client/board/board.module.css +113 -92
- package/src/client/board/logic.ts +99 -21
- package/src/client/board/project-view-logic.ts +27 -34
- package/src/client/board/project-view.module.css +46 -83
- package/src/client/board/project-view.tsx +50 -8
- package/src/client/board/strings.ts +70 -85
- package/src/client/commands.ts +30 -15
- package/src/client/controller.ts +27 -7
- package/src/client/detail/SessionDetail.tsx +234 -37
- package/src/client/detail/detail.module.css +100 -153
- package/src/client/detail/logic.ts +220 -29
- package/src/client/detail/strings.ts +66 -77
- package/src/client/detail-glue.ts +33 -0
- package/src/client/detail-view.module.css +43 -35
- package/src/client/detail-view.tsx +138 -118
- package/src/client/dsh-tools/LineageTree.tsx +22 -13
- package/src/client/dsh-tools/SearchPanel.tsx +18 -11
- package/src/client/dsh-tools/dsh-tools.module.css +53 -140
- package/src/client/dsh-tools/strings.ts +42 -77
- package/src/client/index.ts +285 -124
- package/src/client/inject/InjectPanel.tsx +81 -61
- package/src/client/inject/inject.module.css +97 -197
- package/src/client/inject/logic.ts +38 -0
- package/src/client/lifecycle/handoff.ts +83 -0
- package/src/client/locales/en.ts +91 -4
- package/src/client/locales/host.ts +131 -0
- package/src/client/locales/index.ts +196 -8
- package/src/client/locales/react.ts +10 -0
- package/src/client/locales/view.ts +38 -0
- package/src/client/locales/zh.ts +200 -125
- package/src/client/mount.tsx +75 -41
- package/src/client/navigation/CenterOverlay.tsx +40 -0
- package/src/client/navigation/center-overlay.module.css +51 -0
- package/src/client/navigation/center.ts +45 -0
- package/src/client/navigation/modal-isolation.ts +152 -0
- package/src/client/navigation/modal-surface-anchor.ts +72 -0
- package/src/client/navigation/sidebar-entry.module.css +73 -0
- package/src/client/navigation/sidebar-entry.ts +309 -0
- package/src/client/primitives/StaticPill.tsx +21 -0
- package/src/client/settings-card.module.css +35 -119
- package/src/client/settings-card.tsx +31 -153
- package/src/client/settings-fields.tsx +131 -0
- package/src/client/sidebar/SidebarTab.tsx +156 -0
- package/src/client/sidebar/model.ts +65 -0
- package/src/client/sidebar/sidebar-tab.module.css +118 -0
- package/src/client/sidebar-tab.tsx +46 -320
- package/src/client/theme/agsc.module.css +31 -0
- package/src/client/theme/parts.ts +56 -0
- package/src/client/ui-integration.ts +86 -0
- package/src/client/widget.tsx +42 -16
- package/src/client/inject/overlay.module.css +0 -22
|
@@ -1,9 +1,4 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Project correlation view styles (T5.5): same visual language as
|
|
3
|
-
* board.module.css — every color rides a --dsw-alias-* design token with
|
|
4
|
-
* a neutral fallback, flex top bar, grouped sections. Sessions render as
|
|
5
|
-
* compact rows inside per-agent lanes instead of the board's card grid.
|
|
6
|
-
*/
|
|
1
|
+
/* Project correlation layout consumes the shared Agent Sidecar theme. */
|
|
7
2
|
|
|
8
3
|
.root {
|
|
9
4
|
display: flex;
|
|
@@ -14,8 +9,8 @@
|
|
|
14
9
|
padding: 16px 20px;
|
|
15
10
|
box-sizing: border-box;
|
|
16
11
|
overflow-y: auto;
|
|
17
|
-
color: var(--
|
|
18
|
-
background: var(--
|
|
12
|
+
color: var(--agsc-fg);
|
|
13
|
+
background: var(--agsc-bg);
|
|
19
14
|
}
|
|
20
15
|
|
|
21
16
|
/* ------------------------------------------------------------- top bar */
|
|
@@ -30,21 +25,12 @@
|
|
|
30
25
|
.title {
|
|
31
26
|
font-size: 15px;
|
|
32
27
|
font-weight: 650;
|
|
33
|
-
color: var(--
|
|
28
|
+
color: var(--agsc-fg);
|
|
34
29
|
}
|
|
35
30
|
|
|
36
31
|
.summary {
|
|
37
32
|
font-size: 12px;
|
|
38
|
-
color: var(--
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
.loadingChip {
|
|
42
|
-
font-size: 11px;
|
|
43
|
-
line-height: 18px;
|
|
44
|
-
padding: 0 8px;
|
|
45
|
-
border-radius: 999px;
|
|
46
|
-
color: var(--dsw-alias-label-secondary, #57606a);
|
|
47
|
-
background: var(--dsw-alias-bg-layer-2, rgba(0, 0, 0, 0.04));
|
|
33
|
+
color: var(--agsc-fg-secondary);
|
|
48
34
|
}
|
|
49
35
|
|
|
50
36
|
/* -------------------------------------------------------------- banner */
|
|
@@ -53,14 +39,14 @@
|
|
|
53
39
|
font-size: 12px;
|
|
54
40
|
line-height: 18px;
|
|
55
41
|
padding: 6px 10px;
|
|
56
|
-
border-radius:
|
|
57
|
-
border: 1px solid var(--
|
|
42
|
+
border-radius: var(--agsc-radius-control);
|
|
43
|
+
border: 1px solid var(--agsc-border);
|
|
58
44
|
}
|
|
59
45
|
|
|
60
46
|
.banner[data-tone='danger'] {
|
|
61
|
-
color: var(--
|
|
62
|
-
background: var(--dsw-alias-state-error-secondary
|
|
63
|
-
border-color: var(--dsw-alias-state-error-secondary
|
|
47
|
+
color: var(--agsc-err);
|
|
48
|
+
background: var(--dsw-alias-state-error-secondary);
|
|
49
|
+
border-color: var(--dsw-alias-state-error-secondary);
|
|
64
50
|
}
|
|
65
51
|
|
|
66
52
|
/* -------------------------------------------------------- empty states */
|
|
@@ -80,14 +66,14 @@
|
|
|
80
66
|
.emptyTitle {
|
|
81
67
|
font-size: 14px;
|
|
82
68
|
font-weight: 600;
|
|
83
|
-
color: var(--
|
|
69
|
+
color: var(--agsc-fg);
|
|
84
70
|
}
|
|
85
71
|
|
|
86
72
|
.emptyHint {
|
|
87
73
|
font-size: 12px;
|
|
88
74
|
max-width: 420px;
|
|
89
75
|
line-height: 20px;
|
|
90
|
-
color: var(--
|
|
76
|
+
color: var(--agsc-fg-secondary);
|
|
91
77
|
}
|
|
92
78
|
|
|
93
79
|
/* -------------------------------------------------------- group blocks */
|
|
@@ -97,9 +83,9 @@
|
|
|
97
83
|
flex-direction: column;
|
|
98
84
|
gap: 8px;
|
|
99
85
|
padding: 10px 12px;
|
|
100
|
-
border-radius:
|
|
101
|
-
border: 1px solid var(--
|
|
102
|
-
background: var(--
|
|
86
|
+
border-radius: var(--agsc-radius-card);
|
|
87
|
+
border: 1px solid var(--agsc-border-strong);
|
|
88
|
+
background: var(--agsc-bg-raised);
|
|
103
89
|
}
|
|
104
90
|
|
|
105
91
|
.groupHead {
|
|
@@ -113,7 +99,7 @@
|
|
|
113
99
|
.groupName {
|
|
114
100
|
font-size: 13px;
|
|
115
101
|
font-weight: 600;
|
|
116
|
-
color: var(--
|
|
102
|
+
color: var(--agsc-fg);
|
|
117
103
|
overflow: hidden;
|
|
118
104
|
text-overflow: ellipsis;
|
|
119
105
|
white-space: nowrap;
|
|
@@ -134,9 +120,9 @@
|
|
|
134
120
|
line-height: 18px;
|
|
135
121
|
padding: 0 7px;
|
|
136
122
|
border-radius: 999px;
|
|
137
|
-
border: 1px solid var(--
|
|
138
|
-
background: var(--dsw-alias-bg-layer-2
|
|
139
|
-
color: var(--
|
|
123
|
+
border: 1px solid var(--agsc-border);
|
|
124
|
+
background: var(--dsw-alias-bg-layer-2);
|
|
125
|
+
color: var(--agsc-fg-secondary);
|
|
140
126
|
white-space: nowrap;
|
|
141
127
|
}
|
|
142
128
|
|
|
@@ -145,8 +131,8 @@
|
|
|
145
131
|
line-height: 18px;
|
|
146
132
|
padding: 0 7px;
|
|
147
133
|
border-radius: 999px;
|
|
148
|
-
color: var(--dsw-alias-
|
|
149
|
-
background: var(--
|
|
134
|
+
color: var(--dsw-alias-label-primary-foreground);
|
|
135
|
+
background: var(--agsc-accent);
|
|
150
136
|
white-space: nowrap;
|
|
151
137
|
}
|
|
152
138
|
|
|
@@ -157,7 +143,7 @@
|
|
|
157
143
|
.groupMeta {
|
|
158
144
|
font-size: 11px;
|
|
159
145
|
flex: none;
|
|
160
|
-
color: var(--
|
|
146
|
+
color: var(--agsc-fg-dimmed);
|
|
161
147
|
white-space: nowrap;
|
|
162
148
|
}
|
|
163
149
|
|
|
@@ -181,12 +167,12 @@
|
|
|
181
167
|
gap: 5px;
|
|
182
168
|
font-size: 12px;
|
|
183
169
|
font-weight: 600;
|
|
184
|
-
color: var(--
|
|
170
|
+
color: var(--agsc-fg);
|
|
185
171
|
}
|
|
186
172
|
|
|
187
173
|
.glyph {
|
|
188
174
|
flex: none;
|
|
189
|
-
color: var(--
|
|
175
|
+
color: var(--agsc-accent);
|
|
190
176
|
}
|
|
191
177
|
|
|
192
178
|
.laneSessions {
|
|
@@ -195,6 +181,11 @@
|
|
|
195
181
|
gap: 4px;
|
|
196
182
|
}
|
|
197
183
|
|
|
184
|
+
/* UX-20: lane truncation fold (same look as the board's group fold). */
|
|
185
|
+
.showMore {
|
|
186
|
+
align-self: flex-start;
|
|
187
|
+
}
|
|
188
|
+
|
|
198
189
|
/* -------------------------------------------------------- session rows */
|
|
199
190
|
|
|
200
191
|
.session {
|
|
@@ -206,36 +197,25 @@
|
|
|
206
197
|
font-family: inherit;
|
|
207
198
|
font-size: 12px;
|
|
208
199
|
padding: 5px 8px;
|
|
209
|
-
border-radius:
|
|
210
|
-
border: 1px solid var(--
|
|
211
|
-
background: var(--dsw-alias-bg-base
|
|
212
|
-
color: var(--
|
|
200
|
+
border-radius: var(--agsc-radius-control);
|
|
201
|
+
border: 1px solid var(--agsc-border);
|
|
202
|
+
background: var(--dsw-alias-bg-base);
|
|
203
|
+
color: var(--agsc-fg);
|
|
213
204
|
cursor: pointer;
|
|
214
205
|
}
|
|
215
206
|
|
|
216
207
|
.session:hover {
|
|
217
|
-
border-color: var(--
|
|
218
|
-
background: var(--dsw-alias-interactive-bg-hover
|
|
208
|
+
border-color: var(--agsc-border-strong);
|
|
209
|
+
background: var(--dsw-alias-interactive-bg-hover);
|
|
219
210
|
}
|
|
220
211
|
|
|
221
212
|
.session:focus-visible {
|
|
222
|
-
outline: 2px solid var(--
|
|
213
|
+
outline: 2px solid var(--agsc-accent);
|
|
223
214
|
outline-offset: 1px;
|
|
224
215
|
}
|
|
225
216
|
|
|
226
|
-
.
|
|
227
|
-
display: inline-flex;
|
|
228
|
-
align-items: center;
|
|
229
|
-
gap: 5px;
|
|
217
|
+
.statusPill {
|
|
230
218
|
flex: none;
|
|
231
|
-
font-size: 11px;
|
|
232
|
-
line-height: 18px;
|
|
233
|
-
padding: 0 7px;
|
|
234
|
-
border-radius: 999px;
|
|
235
|
-
border: 1px solid var(--dsw-alias-border-l1, rgba(0, 0, 0, 0.08));
|
|
236
|
-
background: var(--dsw-alias-bg-layer-1, rgba(0, 0, 0, 0.03));
|
|
237
|
-
color: var(--dsw-alias-label-secondary, #57606a);
|
|
238
|
-
white-space: nowrap;
|
|
239
219
|
}
|
|
240
220
|
|
|
241
221
|
.dot {
|
|
@@ -243,36 +223,24 @@
|
|
|
243
223
|
height: 7px;
|
|
244
224
|
border-radius: 50%;
|
|
245
225
|
flex: none;
|
|
246
|
-
background: var(--dsw-alias-label-tertiary
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
.dot[data-tone='success'] {
|
|
250
|
-
background: var(--dsw-alias-state-success-primary, #1a7f37);
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
.dot[data-tone='warn'] {
|
|
254
|
-
background: var(--dsw-alias-state-warn-primary, #9a6700);
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
.dot[data-tone='danger'] {
|
|
258
|
-
background: var(--dsw-alias-state-error-primary, #cf222e);
|
|
226
|
+
background: var(--dsw-alias-label-tertiary);
|
|
259
227
|
}
|
|
260
228
|
|
|
261
229
|
.dot[data-tone='neutral'] {
|
|
262
|
-
background: var(--dsw-alias-label-tertiary
|
|
230
|
+
background: var(--dsw-alias-label-tertiary);
|
|
263
231
|
}
|
|
264
232
|
|
|
265
233
|
.dot[data-tone='muted'] {
|
|
266
|
-
background: var(--
|
|
234
|
+
background: var(--agsc-fg-dimmed);
|
|
267
235
|
}
|
|
268
236
|
|
|
269
237
|
.attention {
|
|
270
238
|
font-size: 11px;
|
|
271
|
-
color: var(--
|
|
239
|
+
color: var(--agsc-warn);
|
|
272
240
|
}
|
|
273
241
|
|
|
274
242
|
.attention[data-kind='gap'] {
|
|
275
|
-
color: var(--
|
|
243
|
+
color: var(--agsc-err);
|
|
276
244
|
}
|
|
277
245
|
|
|
278
246
|
.sessionTitle {
|
|
@@ -284,24 +252,19 @@
|
|
|
284
252
|
|
|
285
253
|
.liveChip {
|
|
286
254
|
flex: none;
|
|
287
|
-
|
|
288
|
-
line-height: 16px;
|
|
289
|
-
padding: 0 6px;
|
|
290
|
-
border-radius: 999px;
|
|
291
|
-
color: var(--dsw-alias-state-success-primary, #1a7f37);
|
|
292
|
-
border: 1px solid var(--dsw-alias-state-success-primary, rgba(26, 127, 55, 0.4));
|
|
255
|
+
color: var(--agsc-ok);
|
|
293
256
|
}
|
|
294
257
|
|
|
295
258
|
.sessionId {
|
|
296
259
|
flex: none;
|
|
297
260
|
font-size: 11px;
|
|
298
|
-
font-family:
|
|
299
|
-
color: var(--
|
|
261
|
+
font-family: var(--agsc-font-mono);
|
|
262
|
+
color: var(--agsc-fg-dimmed);
|
|
300
263
|
}
|
|
301
264
|
|
|
302
265
|
.sessionTime {
|
|
303
266
|
flex: none;
|
|
304
267
|
margin-left: auto;
|
|
305
268
|
font-size: 11px;
|
|
306
|
-
color: var(--
|
|
269
|
+
color: var(--agsc-fg-dimmed);
|
|
307
270
|
}
|
|
@@ -14,15 +14,20 @@
|
|
|
14
14
|
* blanking data the user already has.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
+
import { Button, Pill, StateDot, type StateDotState } from '@deepseek-ai/dsh-client-ui-primitives'
|
|
18
|
+
import { useState } from 'react'
|
|
17
19
|
import type { ReactElement } from 'react'
|
|
18
20
|
import {
|
|
19
21
|
buildProjectViewModel,
|
|
22
|
+
LANE_SESSION_LIMIT,
|
|
20
23
|
PROJECT_VIEW_STRINGS,
|
|
21
24
|
type AgentLaneVM,
|
|
22
25
|
type DerivedProjectGroupVM,
|
|
23
26
|
type DerivedProjectSessionVM,
|
|
24
27
|
type ProjectGroupVM,
|
|
25
28
|
} from './project-view-logic.ts'
|
|
29
|
+
import { formatTemplate, sliceCardsForDisplay } from './logic.ts'
|
|
30
|
+
import { surfaceProps } from '../theme/parts.ts'
|
|
26
31
|
import styles from './project-view.module.css'
|
|
27
32
|
|
|
28
33
|
export interface ProjectViewProps {
|
|
@@ -36,11 +41,18 @@ export interface ProjectViewProps {
|
|
|
36
41
|
nowMs?: number
|
|
37
42
|
}
|
|
38
43
|
|
|
44
|
+
function sessionDotState(status: DerivedProjectSessionVM['badge']['status']): StateDotState | null {
|
|
45
|
+
if (status === 'working') return 'ongoing'
|
|
46
|
+
if (status === 'waiting') return 'warning'
|
|
47
|
+
return null
|
|
48
|
+
}
|
|
49
|
+
|
|
39
50
|
function SessionRow(props: {
|
|
40
51
|
session: DerivedProjectSessionVM
|
|
41
52
|
onSelect: (sessionId: string) => void
|
|
42
53
|
}): ReactElement {
|
|
43
54
|
const { session, onSelect } = props
|
|
55
|
+
const dotState = sessionDotState(session.badge.status)
|
|
44
56
|
return (
|
|
45
57
|
<button
|
|
46
58
|
type="button"
|
|
@@ -48,19 +60,21 @@ function SessionRow(props: {
|
|
|
48
60
|
onClick={() => onSelect(session.sessionId)}
|
|
49
61
|
data-testid="agent-sidecar-project-session"
|
|
50
62
|
>
|
|
51
|
-
<
|
|
52
|
-
|
|
63
|
+
<Pill className={styles['statusPill']}>
|
|
64
|
+
{dotState === null
|
|
65
|
+
? <span className={styles['dot']} data-tone={session.badge.tone} />
|
|
66
|
+
: <StateDot state={dotState} size={8} />}
|
|
53
67
|
{session.badge.label}
|
|
54
68
|
{session.badge.attention !== null && (
|
|
55
69
|
<span className={styles['attention']} data-kind={session.badge.attention}>
|
|
56
70
|
{session.badge.attentionLabel}
|
|
57
71
|
</span>
|
|
58
72
|
)}
|
|
59
|
-
</
|
|
73
|
+
</Pill>
|
|
60
74
|
<span className={styles['sessionTitle']} title={session.title}>
|
|
61
75
|
{session.displayTitle}
|
|
62
76
|
</span>
|
|
63
|
-
{session.live && <
|
|
77
|
+
{session.live && <Pill className={styles['liveChip']}>{PROJECT_VIEW_STRINGS.liveChip}</Pill>}
|
|
64
78
|
<span className={styles['sessionId']} title={session.sessionId}>
|
|
65
79
|
{session.shortId}
|
|
66
80
|
</span>
|
|
@@ -74,6 +88,11 @@ function AgentLane(props: {
|
|
|
74
88
|
onSelect: (sessionId: string) => void
|
|
75
89
|
}): ReactElement {
|
|
76
90
|
const { lane, onSelect } = props
|
|
91
|
+
// UX-20: lanes fold past the row limit — ephemeral view state, same
|
|
92
|
+
// pattern as the board's group truncation. Rows are status-sorted, so
|
|
93
|
+
// the fold never hides a leading working/waiting run (slice guard).
|
|
94
|
+
const [expanded, setExpanded] = useState(false)
|
|
95
|
+
const { shown, hiddenCount } = sliceCardsForDisplay(lane.sessions, LANE_SESSION_LIMIT, expanded)
|
|
77
96
|
return (
|
|
78
97
|
<div className={styles['lane']}>
|
|
79
98
|
<div className={styles['laneHead']}>
|
|
@@ -83,7 +102,7 @@ function AgentLane(props: {
|
|
|
83
102
|
{lane.agent}
|
|
84
103
|
</div>
|
|
85
104
|
<div className={styles['laneSessions']}>
|
|
86
|
-
{
|
|
105
|
+
{shown.map((session) => (
|
|
87
106
|
<SessionRow
|
|
88
107
|
key={`${session.agent}:${session.sessionId}`}
|
|
89
108
|
session={session}
|
|
@@ -91,6 +110,26 @@ function AgentLane(props: {
|
|
|
91
110
|
/>
|
|
92
111
|
))}
|
|
93
112
|
</div>
|
|
113
|
+
{hiddenCount > 0 && (
|
|
114
|
+
<Button
|
|
115
|
+
size="sm"
|
|
116
|
+
variant="outline"
|
|
117
|
+
className={styles['showMore']}
|
|
118
|
+
onClick={() => { setExpanded(true) }}
|
|
119
|
+
>
|
|
120
|
+
{formatTemplate(PROJECT_VIEW_STRINGS.showAllSessions, { n: lane.sessions.length })}
|
|
121
|
+
</Button>
|
|
122
|
+
)}
|
|
123
|
+
{expanded && lane.sessions.length > LANE_SESSION_LIMIT && (
|
|
124
|
+
<Button
|
|
125
|
+
size="sm"
|
|
126
|
+
variant="outline"
|
|
127
|
+
className={styles['showMore']}
|
|
128
|
+
onClick={() => { setExpanded(false) }}
|
|
129
|
+
>
|
|
130
|
+
{formatTemplate(PROJECT_VIEW_STRINGS.showLessSessions, { n: LANE_SESSION_LIMIT })}
|
|
131
|
+
</Button>
|
|
132
|
+
)}
|
|
94
133
|
</div>
|
|
95
134
|
)
|
|
96
135
|
}
|
|
@@ -142,13 +181,16 @@ export function ProjectView(props: ProjectViewProps): ReactElement {
|
|
|
142
181
|
const hasContent = vm.groups.length > 0
|
|
143
182
|
|
|
144
183
|
return (
|
|
145
|
-
<div
|
|
184
|
+
<div
|
|
185
|
+
{...surfaceProps('project-view', styles['root'])}
|
|
186
|
+
data-testid="agent-sidecar-project-view"
|
|
187
|
+
>
|
|
146
188
|
<header className={styles['topbar']}>
|
|
147
189
|
<span className={styles['title']}>{PROJECT_VIEW_STRINGS.title}</span>
|
|
148
190
|
<span className={styles['summary']}>{vm.summaryLabel}</span>
|
|
149
191
|
{props.loading && (
|
|
150
|
-
<span
|
|
151
|
-
{PROJECT_VIEW_STRINGS.loading}
|
|
192
|
+
<span role="status">
|
|
193
|
+
<Pill>{PROJECT_VIEW_STRINGS.loading}</Pill>
|
|
152
194
|
</span>
|
|
153
195
|
)}
|
|
154
196
|
</header>
|
|
@@ -1,112 +1,97 @@
|
|
|
1
|
-
|
|
2
|
-
* Centralized user-facing strings for the board view and the footer widget
|
|
3
|
-
* (design §5.1 view 1 / view 4, §5.3 honesty wording).
|
|
4
|
-
*
|
|
5
|
-
* Chinese is the primary UI language for now; every string the components
|
|
6
|
-
* render lives in this single table so the i18n skeleton (T2.3,
|
|
7
|
-
* `ctx.locale.register`) can take it over without hunting through JSX.
|
|
8
|
-
* Templates use `{name}` placeholders resolved by `formatTemplate` in
|
|
9
|
-
* `logic.ts` — plain message strings, locale-registry friendly.
|
|
10
|
-
*
|
|
11
|
-
* Pure data module: no imports, no logic.
|
|
12
|
-
*
|
|
13
|
-
* @module
|
|
14
|
-
*/
|
|
1
|
+
import { createLocaleView } from '../locales/view.ts'
|
|
15
2
|
|
|
16
|
-
|
|
17
|
-
|
|
3
|
+
/** Dynamic board vocabulary; template leaves are interpolated by `formatTemplate`. */
|
|
4
|
+
export const BOARD_STRINGS = createLocaleView({
|
|
18
5
|
status: {
|
|
19
|
-
working: '
|
|
20
|
-
waiting: '
|
|
21
|
-
idle: '
|
|
22
|
-
dead: '
|
|
23
|
-
unknown: '
|
|
6
|
+
working: 'board.status.working',
|
|
7
|
+
waiting: 'board.status.waiting',
|
|
8
|
+
idle: 'board.status.idle',
|
|
9
|
+
dead: 'board.status.dead',
|
|
10
|
+
unknown: 'board.status.unknown',
|
|
24
11
|
},
|
|
25
|
-
/** Badge attention markers; gap outranks stale (per-session > global). */
|
|
26
12
|
attention: {
|
|
27
|
-
gap: '
|
|
28
|
-
stale: '可能滞后',
|
|
13
|
+
gap: 'board.attention.gap',
|
|
29
14
|
},
|
|
30
|
-
/** Daemon supervisor state labels for the top-bar badge. */
|
|
31
15
|
daemon: {
|
|
32
|
-
probe: '
|
|
33
|
-
adopted: '
|
|
34
|
-
defer: '
|
|
35
|
-
reprobe: '
|
|
36
|
-
hosting: '
|
|
37
|
-
hosted: '
|
|
38
|
-
backoff: '
|
|
39
|
-
failed: '
|
|
16
|
+
probe: 'board.daemon.probe',
|
|
17
|
+
adopted: 'board.daemon.adopted',
|
|
18
|
+
defer: 'board.daemon.defer',
|
|
19
|
+
reprobe: 'board.daemon.reprobe',
|
|
20
|
+
hosting: 'board.daemon.hosting',
|
|
21
|
+
hosted: 'board.daemon.hosted',
|
|
22
|
+
backoff: 'board.daemon.backoff',
|
|
23
|
+
failed: 'board.daemon.failed',
|
|
40
24
|
},
|
|
41
|
-
/** Stream health indicator labels. */
|
|
42
25
|
stream: {
|
|
43
|
-
ok: '
|
|
44
|
-
degraded: '
|
|
45
|
-
unknown: '
|
|
26
|
+
ok: 'board.stream.ok',
|
|
27
|
+
degraded: 'board.stream.degraded',
|
|
28
|
+
unknown: 'board.stream.unknown',
|
|
46
29
|
},
|
|
47
|
-
/** Top banner texts (design §5.3: degraded warning / FAILED red banner). */
|
|
48
30
|
banner: {
|
|
49
|
-
daemonFailed: '
|
|
50
|
-
streamDegraded: '
|
|
31
|
+
daemonFailed: 'board.banner.daemonFailed',
|
|
32
|
+
streamDegraded: 'board.banner.streamDegraded',
|
|
51
33
|
},
|
|
52
|
-
/** Empty-state guidance blocks. */
|
|
53
34
|
empty: {
|
|
54
|
-
daemonFailedTitle: '
|
|
55
|
-
daemonFailedHint:
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
noSessionsTitle: '暂无被观测的会话',
|
|
63
|
-
noSessionsHint:
|
|
64
|
-
'本机 agent(claude / codex / cursor / dsh …)开始工作后会自动出现在这里。',
|
|
35
|
+
daemonFailedTitle: 'board.empty.daemonFailedTitle',
|
|
36
|
+
daemonFailedHint: 'board.empty.daemonFailedHint',
|
|
37
|
+
daemonDeferTitle: 'board.empty.daemonDeferTitle',
|
|
38
|
+
daemonDeferHint: 'board.empty.daemonDeferHint',
|
|
39
|
+
filteredTitle: 'board.empty.filteredTitle',
|
|
40
|
+
filteredHint: 'board.empty.filteredHint',
|
|
41
|
+
noSessionsTitle: 'board.empty.noSessionsTitle',
|
|
42
|
+
noSessionsHint: 'board.empty.noSessionsHint',
|
|
65
43
|
},
|
|
66
|
-
/** Top bar controls. */
|
|
67
44
|
topbar: {
|
|
68
|
-
title: '
|
|
69
|
-
refresh: '
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
45
|
+
title: 'board.topbar.title',
|
|
46
|
+
refresh: 'board.topbar.refresh',
|
|
47
|
+
refreshing: 'board.topbar.refreshing',
|
|
48
|
+
refreshTitle: 'board.topbar.refreshTitle',
|
|
49
|
+
refreshFailed: 'board.topbar.refreshFailed',
|
|
50
|
+
dismiss: 'board.topbar.dismiss',
|
|
51
|
+
showDead: 'board.topbar.showDead',
|
|
52
|
+
timeWindow: 'board.topbar.timeWindow',
|
|
53
|
+
countWorking: 'board.topbar.countWorking',
|
|
54
|
+
countWaiting: 'board.topbar.countWaiting',
|
|
55
|
+
countTotal: 'board.topbar.countTotal',
|
|
56
|
+
filterByStatusTitle: 'board.topbar.filterByStatusTitle',
|
|
57
|
+
clearStatusFilterTitle: 'board.topbar.clearStatusFilterTitle',
|
|
58
|
+
},
|
|
59
|
+
group: {
|
|
60
|
+
collapseTitle: 'board.group.collapseTitle',
|
|
61
|
+
expandTitle: 'board.group.expandTitle',
|
|
62
|
+
showAll: 'board.group.showAll',
|
|
63
|
+
showLess: 'board.group.showLess',
|
|
73
64
|
},
|
|
74
|
-
/** Session card texts. */
|
|
75
65
|
card: {
|
|
76
|
-
noEvent: '
|
|
77
|
-
untitled: '
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
66
|
+
noEvent: 'board.card.noEvent',
|
|
67
|
+
untitled: 'board.card.untitled',
|
|
68
|
+
observedDisclaimer: 'board.card.observedDisclaimer',
|
|
69
|
+
observedValue: 'board.card.observedValue',
|
|
70
|
+
lastReconcile: 'board.card.lastReconcile',
|
|
71
|
+
neverReconciled: 'board.card.neverReconciled',
|
|
72
|
+
copyId: 'board.card.copyId',
|
|
73
|
+
copied: 'board.card.copied',
|
|
83
74
|
},
|
|
84
|
-
/** Relative time templates. */
|
|
85
75
|
time: {
|
|
86
|
-
justNow: '
|
|
87
|
-
minutesAgo: '
|
|
88
|
-
hoursAgo: '
|
|
89
|
-
daysAgo: '{n} 天前',
|
|
76
|
+
justNow: 'board.time.justNow',
|
|
77
|
+
minutesAgo: 'board.time.minutesAgo',
|
|
78
|
+
hoursAgo: 'board.time.hoursAgo',
|
|
90
79
|
},
|
|
91
|
-
/** Time-window option templates. */
|
|
92
80
|
timeWindow: {
|
|
93
|
-
hours: '
|
|
94
|
-
days: '
|
|
81
|
+
hours: 'board.timeWindow.hours',
|
|
82
|
+
days: 'board.timeWindow.days',
|
|
95
83
|
},
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
/** Sessions with an empty `project` fall into this group (task spec). */
|
|
99
|
-
unknownProject: '未知项目',
|
|
100
|
-
/** Footer widget. */
|
|
84
|
+
groupCount: 'board.groupCount',
|
|
85
|
+
unknownProject: 'board.unknownProject',
|
|
101
86
|
widget: {
|
|
102
|
-
label: '
|
|
87
|
+
label: 'board.widget.label',
|
|
103
88
|
connection: {
|
|
104
|
-
ok: '
|
|
105
|
-
degraded: '
|
|
106
|
-
off: '
|
|
89
|
+
ok: 'board.widget.connection.ok',
|
|
90
|
+
degraded: 'board.widget.connection.degraded',
|
|
91
|
+
off: 'board.widget.connection.off',
|
|
107
92
|
},
|
|
108
|
-
working: '
|
|
93
|
+
working: 'board.widget.working',
|
|
109
94
|
},
|
|
110
|
-
} as const
|
|
95
|
+
} as const)
|
|
111
96
|
|
|
112
97
|
export type BoardStrings = typeof BOARD_STRINGS
|