@shendeguize/dsh-agent-sidecar 0.1.1 → 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 +8659 -7589
- 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 +115 -61
- package/src/client/board/board.module.css +72 -151
- package/src/client/board/project-view-logic.ts +21 -34
- package/src/client/board/project-view.module.css +41 -96
- package/src/client/board/project-view.tsx +29 -13
- package/src/client/board/strings.ts +68 -107
- package/src/client/commands.ts +30 -15
- package/src/client/detail/SessionDetail.tsx +92 -58
- package/src/client/detail/detail.module.css +62 -218
- package/src/client/detail/strings.ts +64 -88
- package/src/client/detail-view.module.css +30 -55
- package/src/client/detail-view.tsx +80 -108
- 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 +50 -139
- package/src/client/dsh-tools/strings.ts +42 -77
- package/src/client/index.ts +285 -124
- package/src/client/inject/InjectPanel.tsx +31 -64
- package/src/client/inject/inject.module.css +97 -198
- package/src/client/lifecycle/handoff.ts +83 -0
- package/src/client/locales/en.ts +74 -2
- 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 +194 -134
- package/src/client/mount.tsx +72 -38
- 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 +9 -8
- package/src/client/inject/overlay.module.css +0 -22
|
@@ -1,11 +1,3 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Detail-view container + board-tab view-switcher chrome (T5.10b, design
|
|
3
|
-
* §5.1): every color rides a --dsw-alias-* design token with a neutral
|
|
4
|
-
* fallback. Same posture as board.module.css / dsh-tools.module.css.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
/* ------------------------------------------------ board-tab view switcher */
|
|
8
|
-
|
|
9
1
|
.switcherBar {
|
|
10
2
|
display: flex;
|
|
11
3
|
align-items: center;
|
|
@@ -19,31 +11,26 @@
|
|
|
19
11
|
border-radius: 999px;
|
|
20
12
|
border: 1px solid transparent;
|
|
21
13
|
background: transparent;
|
|
22
|
-
color: var(--dsw-alias-label-secondary
|
|
14
|
+
color: var(--dsw-alias-label-secondary);
|
|
23
15
|
cursor: pointer;
|
|
24
16
|
}
|
|
25
17
|
|
|
26
18
|
.switcherButton:hover {
|
|
27
|
-
background: var(--dsw-alias-bg-layer-1
|
|
19
|
+
background: var(--dsw-alias-bg-layer-1);
|
|
28
20
|
}
|
|
29
21
|
|
|
30
22
|
.switcherButton[data-active='true'] {
|
|
31
23
|
font-weight: 600;
|
|
32
|
-
color: var(--dsw-alias-label-primary
|
|
33
|
-
background: var(--dsw-alias-bg-layer-2
|
|
34
|
-
border-color: var(--dsw-alias-border-l2
|
|
24
|
+
color: var(--dsw-alias-label-primary);
|
|
25
|
+
background: var(--dsw-alias-bg-layer-2);
|
|
26
|
+
border-color: var(--dsw-alias-border-l2);
|
|
35
27
|
}
|
|
36
28
|
|
|
37
|
-
/* --------------------------------------------------- detail-view chrome */
|
|
38
|
-
|
|
39
29
|
.detailRoot {
|
|
40
30
|
display: flex;
|
|
41
31
|
flex-direction: column;
|
|
42
32
|
gap: 12px;
|
|
43
33
|
min-width: 0;
|
|
44
|
-
/* Own the scroll like the board/project roots do (UX-12): without a
|
|
45
|
-
* bounded height the inner timeline's overflow-y can never engage, and
|
|
46
|
-
* the UX-04 landing / listen-mode tail pinning would silently no-op. */
|
|
47
34
|
height: 100%;
|
|
48
35
|
overflow-y: auto;
|
|
49
36
|
box-sizing: border-box;
|
|
@@ -54,59 +41,47 @@
|
|
|
54
41
|
display: flex;
|
|
55
42
|
align-items: center;
|
|
56
43
|
gap: 8px;
|
|
44
|
+
flex-wrap: wrap;
|
|
57
45
|
}
|
|
58
46
|
|
|
59
|
-
.
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
background: var(--dsw-alias-bg-layer-1, rgba(0, 0, 0, 0.02));
|
|
65
|
-
color: var(--dsw-alias-label-primary, #1f2328);
|
|
66
|
-
cursor: pointer;
|
|
47
|
+
.analysisDisabledReason {
|
|
48
|
+
max-width: 360px;
|
|
49
|
+
font-size: 11px;
|
|
50
|
+
line-height: 16px;
|
|
51
|
+
color: var(--agsc-fg-secondary);
|
|
67
52
|
}
|
|
68
53
|
|
|
69
|
-
.actionButton:hover {
|
|
70
|
-
background: var(--dsw-alias-bg-layer-2, rgba(0, 0, 0, 0.04));
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
.actionButton:disabled {
|
|
74
|
-
opacity: 0.5;
|
|
75
|
-
cursor: not-allowed;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/* dsh deep-query tools: collapsible segment between the actions row and
|
|
79
|
-
* the timeline (UX-09 — previously buried below a possibly very long
|
|
80
|
-
* list, outside the viewport on long sessions). */
|
|
81
54
|
.toolsSection {
|
|
82
55
|
display: flex;
|
|
83
56
|
flex-direction: column;
|
|
84
57
|
gap: 16px;
|
|
85
|
-
border: 1px solid var(--
|
|
86
|
-
border-radius:
|
|
58
|
+
border: 1px solid var(--agsc-border);
|
|
59
|
+
border-radius: var(--agsc-radius-control);
|
|
87
60
|
padding: 8px 12px;
|
|
88
61
|
}
|
|
89
62
|
|
|
90
63
|
.toolsToggle {
|
|
91
64
|
align-self: flex-start;
|
|
92
|
-
display: inline-flex;
|
|
93
|
-
align-items: center;
|
|
94
|
-
gap: 6px;
|
|
95
|
-
font-size: 12px;
|
|
96
|
-
font-family: inherit;
|
|
97
|
-
color: var(--dsw-alias-label-secondary, #57606a);
|
|
98
|
-
background: transparent;
|
|
99
|
-
border: none;
|
|
100
|
-
padding: 0;
|
|
101
|
-
cursor: pointer;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
.toolsToggle:hover {
|
|
105
|
-
color: var(--dsw-alias-label-primary, #1f2328);
|
|
106
65
|
}
|
|
107
66
|
|
|
108
67
|
.toolsToggleGlyph {
|
|
109
68
|
flex: none;
|
|
110
69
|
font-size: 10px;
|
|
111
|
-
color: var(--
|
|
70
|
+
color: var(--agsc-fg-dimmed);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.injectDialog {
|
|
74
|
+
gap: 0;
|
|
75
|
+
width: min(560px, 100%);
|
|
76
|
+
max-height: min(85vh, 720px);
|
|
77
|
+
padding: 0;
|
|
78
|
+
overflow: auto;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.injectDialog > [data-dsh-part='inject-panel'] {
|
|
82
|
+
width: 100%;
|
|
83
|
+
max-width: none;
|
|
84
|
+
border: 0;
|
|
85
|
+
border-radius: inherit;
|
|
86
|
+
background: transparent;
|
|
112
87
|
}
|
|
@@ -1,27 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Session-detail container
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* stores:
|
|
2
|
+
* Session-detail React container. It composes the timeline, lineage,
|
|
3
|
+
* search, analysis, and optional injection surfaces over stores supplied
|
|
4
|
+
* by {@link DetailUiPort}.
|
|
6
5
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* AI 分析 (AnalysisPanel over AnalysisStore; disabled with an honest
|
|
11
|
-
* hint while `analysis.enabled` is off);
|
|
12
|
-
* - dsh 会话专属区: LineageTree ← the DetailStore lineage slice (non-dsh
|
|
13
|
-
* sessions degrade client-side, no dialing) and SearchPanel ←
|
|
14
|
-
* SearchStore (full-text or filter-only degradation; a result click
|
|
15
|
-
* navigates the detail view to that session).
|
|
16
|
-
*
|
|
17
|
-
* The stores live in component state (one set per opened session id — the
|
|
18
|
-
* owner keys this component by session id) and are disposed on unmount.
|
|
19
|
-
* SSE coupling: the controller's subscribe seam notifies the DetailStore
|
|
20
|
-
* on every state frame (header refresh + listen-mode refetch trigger).
|
|
6
|
+
* Stores are scoped to one opened session and disposed on unmount. Each
|
|
7
|
+
* controller state frame refreshes the header hint and drives the
|
|
8
|
+
* detail store's bounded listen-mode refetch.
|
|
21
9
|
*
|
|
22
10
|
* @module
|
|
23
11
|
*/
|
|
24
12
|
|
|
13
|
+
import { Button, Modal } from '@deepseek-ai/dsh-client-ui-primitives'
|
|
25
14
|
import { useEffect, useState, useSyncExternalStore } from 'react'
|
|
26
15
|
import type { ReactElement } from 'react'
|
|
27
16
|
import { SessionDetail } from './detail/SessionDetail.tsx'
|
|
@@ -29,58 +18,28 @@ import { LineageTree } from './dsh-tools/LineageTree.tsx'
|
|
|
29
18
|
import { SearchPanel } from './dsh-tools/SearchPanel.tsx'
|
|
30
19
|
import { AnalysisPanel } from './analysis/AnalysisPanel.tsx'
|
|
31
20
|
import { InjectPanel } from './inject/InjectPanel.tsx'
|
|
32
|
-
import {
|
|
33
|
-
import { SearchStore } from './search-glue.ts'
|
|
34
|
-
import { AnalysisStore } from './analysis-glue.ts'
|
|
35
|
-
import { ProjectsStore } from './project-glue.ts'
|
|
21
|
+
import { findCardHint, type DetailHeaderHint } from './detail-glue.ts'
|
|
36
22
|
import type { SidecarController } from './controller.ts'
|
|
37
|
-
import { isDeliveredResult
|
|
23
|
+
import { isDeliveredResult } from './inject/logic.ts'
|
|
38
24
|
import type { InjectActions } from './inject-glue.ts'
|
|
25
|
+
import type {
|
|
26
|
+
AnalysisStorePort,
|
|
27
|
+
DetailStorePort,
|
|
28
|
+
DetailUiPort,
|
|
29
|
+
SearchStorePort,
|
|
30
|
+
} from './ui-integration.ts'
|
|
39
31
|
import { t } from './locales/index.ts'
|
|
32
|
+
import { surfaceProps } from './theme/parts.ts'
|
|
40
33
|
import css from './detail-view.module.css'
|
|
41
|
-
import overlay from './inject/overlay.module.css'
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Everything the integrated M3 surfaces need from the entry (index.ts
|
|
45
|
-
* builds one; mount.tsx and this container share it). Store factories are
|
|
46
|
-
* seams so tests/materialization can substitute injected transports.
|
|
47
|
-
*/
|
|
48
|
-
export interface SidecarUiIntegration {
|
|
49
|
-
/** M2 injection wiring; absent = injection not assembled (entry hidden). */
|
|
50
|
-
inject?: {
|
|
51
|
-
actions: InjectActions
|
|
52
|
-
getDefaultMode: () => InjectMode
|
|
53
|
-
}
|
|
54
|
-
/** Live `analysis.enabled` reader (settings scope box; default false). */
|
|
55
|
-
getAnalysisEnabled: () => boolean
|
|
56
|
-
createDetailStore: (sessionId: string, hint: DetailHeaderHint | null) => DetailStore
|
|
57
|
-
createSearchStore: () => SearchStore
|
|
58
|
-
createAnalysisStore: () => AnalysisStore
|
|
59
|
-
createProjectsStore: () => ProjectsStore
|
|
60
|
-
}
|
|
61
34
|
|
|
62
|
-
|
|
63
|
-
export function createDefaultIntegration(
|
|
64
|
-
base: Omit<
|
|
65
|
-
SidecarUiIntegration,
|
|
66
|
-
'createDetailStore' | 'createSearchStore' | 'createAnalysisStore' | 'createProjectsStore'
|
|
67
|
-
>,
|
|
68
|
-
): SidecarUiIntegration {
|
|
69
|
-
return {
|
|
70
|
-
...base,
|
|
71
|
-
createDetailStore: (sessionId, hint) => new DetailStore(sessionId, { hint }),
|
|
72
|
-
createSearchStore: () => new SearchStore(),
|
|
73
|
-
createAnalysisStore: () => new AnalysisStore(),
|
|
74
|
-
createProjectsStore: () => new ProjectsStore(),
|
|
75
|
-
}
|
|
76
|
-
}
|
|
35
|
+
const ANALYSIS_DISABLED_REASON_ID = 'agent-sidecar-analysis-disabled-reason'
|
|
77
36
|
|
|
78
37
|
export interface SidecarDetailViewProps {
|
|
79
38
|
sessionId: string
|
|
80
39
|
/** Header seed from the opening surface (board card / project row). */
|
|
81
40
|
hint: DetailHeaderHint | null
|
|
82
41
|
controller: SidecarController
|
|
83
|
-
integration:
|
|
42
|
+
integration: DetailUiPort
|
|
84
43
|
onClose: () => void
|
|
85
44
|
/** Provenance/search jump: navigate the detail view to another session. */
|
|
86
45
|
onSelectSession: (sessionId: string) => void
|
|
@@ -92,9 +51,11 @@ export interface SidecarDetailViewProps {
|
|
|
92
51
|
*/
|
|
93
52
|
export function SidecarDetailView(props: SidecarDetailViewProps): ReactElement {
|
|
94
53
|
const { controller, integration, sessionId } = props
|
|
95
|
-
const [detailStore] = useState(
|
|
96
|
-
|
|
97
|
-
|
|
54
|
+
const [detailStore] = useState<DetailStorePort>(
|
|
55
|
+
() => integration.createDetailStore(sessionId, props.hint),
|
|
56
|
+
)
|
|
57
|
+
const [searchStore] = useState<SearchStorePort>(() => integration.createSearchStore())
|
|
58
|
+
const [analysisStore] = useState<AnalysisStorePort>(() => integration.createAnalysisStore())
|
|
98
59
|
const [injectOpen, setInjectOpen] = useState(false)
|
|
99
60
|
const [analysisOpen, setAnalysisOpen] = useState(false)
|
|
100
61
|
const [toolsOpen, setToolsOpen] = useState(false)
|
|
@@ -127,15 +88,14 @@ export function SidecarDetailView(props: SidecarDetailViewProps): ReactElement {
|
|
|
127
88
|
)
|
|
128
89
|
|
|
129
90
|
const analysisEnabled = integration.getAnalysisEnabled()
|
|
91
|
+
const analysisDisabledHint =
|
|
92
|
+
analysisEnabled ? undefined : t('detail.actions.analyzeDisabledHint')
|
|
130
93
|
const injectIntegration = props.integration.inject
|
|
131
94
|
const closeInject = (): void => { setInjectOpen(false) }
|
|
132
95
|
const title = detail.header.title.trim()
|
|
133
96
|
|
|
134
|
-
//
|
|
135
|
-
//
|
|
136
|
-
// stale pre-injection timeline. Wraps (not replaces) the integration
|
|
137
|
-
// callback — the two-phase flow and the owner's onDelivered hook (board
|
|
138
|
-
// snapshot refresh) stay untouched.
|
|
97
|
+
// A delivered execute refreshes the newest timeline while preserving
|
|
98
|
+
// the integration-owned two-phase flow and delivery callback.
|
|
139
99
|
const injectActions: InjectActions | undefined =
|
|
140
100
|
injectIntegration === undefined
|
|
141
101
|
? undefined
|
|
@@ -148,43 +108,55 @@ export function SidecarDetailView(props: SidecarDetailViewProps): ReactElement {
|
|
|
148
108
|
},
|
|
149
109
|
}
|
|
150
110
|
|
|
151
|
-
// UX-05 part 2: the delivered result page offers「开启监听观察反应」—
|
|
152
|
-
// flip listen mode on (if off) and hand the view back to the timeline.
|
|
153
111
|
const observeReaction = (): void => {
|
|
154
112
|
if (!detailStore.getState().listening) detailStore.toggleListen()
|
|
155
113
|
closeInject()
|
|
156
114
|
}
|
|
157
115
|
|
|
158
116
|
return (
|
|
159
|
-
<div
|
|
117
|
+
<div
|
|
118
|
+
{...surfaceProps('detail', css['detailRoot'])}
|
|
119
|
+
data-testid="agent-sidecar-detail-view"
|
|
120
|
+
>
|
|
160
121
|
<div className={css['actionsRow']}>
|
|
161
122
|
{injectIntegration !== undefined && (
|
|
162
|
-
<
|
|
163
|
-
|
|
164
|
-
|
|
123
|
+
<Button
|
|
124
|
+
size="sm"
|
|
125
|
+
variant="outline"
|
|
165
126
|
onClick={() => { setInjectOpen(true) }}
|
|
166
127
|
data-testid="agent-sidecar-detail-inject"
|
|
167
128
|
>
|
|
168
129
|
{t('detail.actions.inject')}
|
|
169
|
-
</
|
|
130
|
+
</Button>
|
|
170
131
|
)}
|
|
171
|
-
<
|
|
172
|
-
|
|
173
|
-
|
|
132
|
+
<Button
|
|
133
|
+
size="sm"
|
|
134
|
+
variant="outline"
|
|
174
135
|
disabled={!analysisEnabled}
|
|
175
|
-
title={
|
|
136
|
+
title={analysisDisabledHint}
|
|
137
|
+
aria-describedby={analysisEnabled ? undefined : ANALYSIS_DISABLED_REASON_ID}
|
|
176
138
|
onClick={() => { setAnalysisOpen(true) }}
|
|
177
139
|
data-testid="agent-sidecar-detail-analyze"
|
|
178
140
|
>
|
|
179
141
|
{t('detail.actions.analyze')}
|
|
180
|
-
</
|
|
142
|
+
</Button>
|
|
143
|
+
{!analysisEnabled && (
|
|
144
|
+
<span
|
|
145
|
+
id={ANALYSIS_DISABLED_REASON_ID}
|
|
146
|
+
className={css['analysisDisabledReason']}
|
|
147
|
+
>
|
|
148
|
+
{analysisDisabledHint}
|
|
149
|
+
</span>
|
|
150
|
+
)}
|
|
181
151
|
</div>
|
|
182
152
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
153
|
+
<div
|
|
154
|
+
{...surfaceProps('dsh-tools', css['toolsSection'])}
|
|
155
|
+
data-testid="agent-sidecar-detail-tools"
|
|
156
|
+
>
|
|
157
|
+
<Button
|
|
158
|
+
size="sm"
|
|
159
|
+
variant="ghost"
|
|
188
160
|
className={css['toolsToggle']}
|
|
189
161
|
aria-expanded={toolsOpen}
|
|
190
162
|
onClick={() => { setToolsOpen((open) => !open) }}
|
|
@@ -196,7 +168,7 @@ export function SidecarDetailView(props: SidecarDetailViewProps): ReactElement {
|
|
|
196
168
|
<span className={css['toolsToggleGlyph']}>
|
|
197
169
|
{toolsOpen ? t('detail.tools.hide') : t('detail.tools.show')}
|
|
198
170
|
</span>
|
|
199
|
-
</
|
|
171
|
+
</Button>
|
|
200
172
|
{toolsOpen && (
|
|
201
173
|
<>
|
|
202
174
|
<LineageTree
|
|
@@ -252,29 +224,29 @@ export function SidecarDetailView(props: SidecarDetailViewProps): ReactElement {
|
|
|
252
224
|
/>
|
|
253
225
|
)}
|
|
254
226
|
|
|
255
|
-
{injectIntegration !== undefined && injectActions !== undefined &&
|
|
256
|
-
<
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
}}
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
</
|
|
227
|
+
{injectIntegration !== undefined && injectActions !== undefined && (
|
|
228
|
+
<Modal
|
|
229
|
+
open={injectOpen}
|
|
230
|
+
onClose={closeInject}
|
|
231
|
+
title={t('inject.title')}
|
|
232
|
+
closeLabel={t('inject.close')}
|
|
233
|
+
className={css['injectDialog']}
|
|
234
|
+
headless
|
|
235
|
+
>
|
|
236
|
+
<InjectPanel
|
|
237
|
+
capability={{ inject: view.injectCapability }}
|
|
238
|
+
target={{
|
|
239
|
+
agent: detail.header.agent,
|
|
240
|
+
sessionId,
|
|
241
|
+
...(title !== '' ? { title } : {}),
|
|
242
|
+
}}
|
|
243
|
+
defaultMode={injectIntegration.getDefaultMode()}
|
|
244
|
+
onPrepare={injectActions.onPrepare}
|
|
245
|
+
onExecute={injectActions.onExecute}
|
|
246
|
+
onClose={closeInject}
|
|
247
|
+
onObserve={observeReaction}
|
|
248
|
+
/>
|
|
249
|
+
</Modal>
|
|
278
250
|
)}
|
|
279
251
|
</div>
|
|
280
252
|
)
|
|
@@ -13,8 +13,11 @@
|
|
|
13
13
|
* ./logic.ts and is unit-tested there.
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
|
+
import { Button, Pill } from '@deepseek-ai/dsh-client-ui-primitives'
|
|
16
17
|
import { useState } from 'react'
|
|
17
18
|
import type { ReactElement } from 'react'
|
|
19
|
+
import { StaticPill } from '../primitives/StaticPill.tsx'
|
|
20
|
+
import { surfaceProps } from '../theme/parts.ts'
|
|
18
21
|
import css from './dsh-tools.module.css'
|
|
19
22
|
import {
|
|
20
23
|
deriveLineageView,
|
|
@@ -64,20 +67,24 @@ function TreeRow(props: {
|
|
|
64
67
|
data-role={node.role}
|
|
65
68
|
>
|
|
66
69
|
{node.hasChildren ? (
|
|
67
|
-
<
|
|
70
|
+
<Button
|
|
68
71
|
type="button"
|
|
72
|
+
size="sm"
|
|
73
|
+
variant="ghost"
|
|
69
74
|
className={css['toggle']}
|
|
70
75
|
aria-expanded={!collapsed}
|
|
71
76
|
aria-label={collapsed ? S.expand : S.collapse}
|
|
72
77
|
onClick={() => props.onToggle(node.id)}
|
|
73
78
|
>
|
|
74
79
|
{collapsed ? '▸' : '▾'}
|
|
75
|
-
</
|
|
80
|
+
</Button>
|
|
76
81
|
) : (
|
|
77
82
|
<span className={css['toggleSpacer']} aria-hidden />
|
|
78
83
|
)}
|
|
79
|
-
<
|
|
84
|
+
<Button
|
|
80
85
|
type="button"
|
|
86
|
+
size="sm"
|
|
87
|
+
variant="ghost"
|
|
81
88
|
className={css['node']}
|
|
82
89
|
data-current={node.isCurrent ? 'true' : 'false'}
|
|
83
90
|
aria-current={node.isCurrent ? 'true' : undefined}
|
|
@@ -91,17 +98,17 @@ function TreeRow(props: {
|
|
|
91
98
|
{node.shortId}
|
|
92
99
|
</span>
|
|
93
100
|
{node.isCurrent && (
|
|
94
|
-
<
|
|
101
|
+
<StaticPill className={css['nodeBadge']} data-kind="current">
|
|
95
102
|
{S.currentBadge}
|
|
96
|
-
</
|
|
103
|
+
</StaticPill>
|
|
97
104
|
)}
|
|
98
105
|
{node.live && (
|
|
99
|
-
<
|
|
106
|
+
<StaticPill className={css['nodeBadge']} data-kind="live">
|
|
100
107
|
{S.liveBadge}
|
|
101
|
-
</
|
|
108
|
+
</StaticPill>
|
|
102
109
|
)}
|
|
103
|
-
{!node.persisted && <
|
|
104
|
-
</
|
|
110
|
+
{!node.persisted && <Pill className={css['nodeBadge']}>{S.notPersistedBadge}</Pill>}
|
|
111
|
+
</Button>
|
|
105
112
|
</div>
|
|
106
113
|
)
|
|
107
114
|
}
|
|
@@ -163,17 +170,19 @@ export function LineageTree(props: LineageTreeProps): ReactElement {
|
|
|
163
170
|
})
|
|
164
171
|
|
|
165
172
|
return (
|
|
166
|
-
<section
|
|
173
|
+
<section {...surfaceProps('dsh-tools', css['panel'])} data-testid="agent-sidecar-lineage">
|
|
167
174
|
<div className={css['panelHead']}>
|
|
168
175
|
<span className={css['panelTitle']}>{S.title}</span>
|
|
169
176
|
{view.kind === 'tree' && (
|
|
170
|
-
<
|
|
177
|
+
<Pill className={css['panelCount']}>
|
|
171
178
|
{formatTemplate(S.nodeCount, { n: view.tree.nodeCount })}
|
|
172
|
-
</
|
|
179
|
+
</Pill>
|
|
173
180
|
)}
|
|
174
181
|
</div>
|
|
175
182
|
|
|
176
|
-
{view.kind === 'loading' &&
|
|
183
|
+
{view.kind === 'loading' && (
|
|
184
|
+
<div className={css['mutedLine']} role="status">{view.text}</div>
|
|
185
|
+
)}
|
|
177
186
|
|
|
178
187
|
{view.kind === 'error' && (
|
|
179
188
|
<div className={css['errorCard']} role="alert">
|
|
@@ -14,7 +14,10 @@
|
|
|
14
14
|
* injection); full-text hits carry them, filter hits render without.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
+
import { Button, Input } from '@deepseek-ai/dsh-client-ui-primitives'
|
|
17
18
|
import type { ReactElement } from 'react'
|
|
19
|
+
import { StaticPill } from '../primitives/StaticPill.tsx'
|
|
20
|
+
import { surfaceProps } from '../theme/parts.ts'
|
|
18
21
|
import css from './dsh-tools.module.css'
|
|
19
22
|
import {
|
|
20
23
|
deriveSearchView,
|
|
@@ -50,8 +53,10 @@ function ResultItem(props: {
|
|
|
50
53
|
}): ReactElement {
|
|
51
54
|
const { item } = props
|
|
52
55
|
return (
|
|
53
|
-
<
|
|
56
|
+
<Button
|
|
54
57
|
type="button"
|
|
58
|
+
size="sm"
|
|
59
|
+
variant="ghost"
|
|
55
60
|
className={css['resultItem']}
|
|
56
61
|
onClick={() => props.onSelectSession(item.sessionId)}
|
|
57
62
|
data-testid="agent-sidecar-search-item"
|
|
@@ -61,9 +66,9 @@ function ResultItem(props: {
|
|
|
61
66
|
<span className={css['resultTitle']} title={item.title}>
|
|
62
67
|
{item.titleLabel}
|
|
63
68
|
</span>
|
|
64
|
-
<
|
|
69
|
+
<StaticPill className={css['matchTag']} data-kind={item.matchedBy}>
|
|
65
70
|
{item.matchedByLabel}
|
|
66
|
-
</
|
|
71
|
+
</StaticPill>
|
|
67
72
|
</span>
|
|
68
73
|
<span className={css['resultMeta']}>
|
|
69
74
|
<span className={css['resultProject']} title={item.project}>
|
|
@@ -86,7 +91,7 @@ function ResultItem(props: {
|
|
|
86
91
|
)}
|
|
87
92
|
</span>
|
|
88
93
|
)}
|
|
89
|
-
</
|
|
94
|
+
</Button>
|
|
90
95
|
)
|
|
91
96
|
}
|
|
92
97
|
|
|
@@ -102,7 +107,7 @@ export function SearchPanel(props: SearchPanelProps): ReactElement {
|
|
|
102
107
|
const project = props.project ?? null
|
|
103
108
|
|
|
104
109
|
return (
|
|
105
|
-
<section
|
|
110
|
+
<section {...surfaceProps('dsh-tools', css['panel'])} data-testid="agent-sidecar-search">
|
|
106
111
|
<div className={css['panelHead']}>
|
|
107
112
|
<span className={css['panelTitle']}>{S.title}</span>
|
|
108
113
|
</div>
|
|
@@ -114,22 +119,22 @@ export function SearchPanel(props: SearchPanelProps): ReactElement {
|
|
|
114
119
|
props.onSubmit()
|
|
115
120
|
}}
|
|
116
121
|
>
|
|
117
|
-
<
|
|
122
|
+
<Input
|
|
118
123
|
type="search"
|
|
119
124
|
className={css['searchInput']}
|
|
120
125
|
value={props.query}
|
|
121
126
|
placeholder={S.placeholder}
|
|
122
127
|
onChange={(ev) => props.onQueryChange(ev.target.value)}
|
|
123
128
|
/>
|
|
124
|
-
<
|
|
129
|
+
<Button type="submit" size="sm" variant="outline">
|
|
125
130
|
{S.submit}
|
|
126
|
-
</
|
|
131
|
+
</Button>
|
|
127
132
|
</form>
|
|
128
133
|
|
|
129
134
|
{project !== null && project !== '' && (
|
|
130
|
-
<
|
|
135
|
+
<StaticPill className={css['projectChip']} title={project}>
|
|
131
136
|
{formatTemplate(S.projectFilter, { project })}
|
|
132
|
-
</
|
|
137
|
+
</StaticPill>
|
|
133
138
|
)}
|
|
134
139
|
|
|
135
140
|
{view.notice !== null && (
|
|
@@ -142,7 +147,9 @@ export function SearchPanel(props: SearchPanelProps): ReactElement {
|
|
|
142
147
|
</div>
|
|
143
148
|
)}
|
|
144
149
|
|
|
145
|
-
{view.body === 'loading' &&
|
|
150
|
+
{view.body === 'loading' && (
|
|
151
|
+
<div className={css['mutedLine']} role="status">{view.text}</div>
|
|
152
|
+
)}
|
|
146
153
|
|
|
147
154
|
{view.body === 'error' && (
|
|
148
155
|
<div className={css['errorCard']} role="alert">
|