@sentry/junior-dashboard 0.261.0 → 0.262.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/dist/app.js +2 -2
- package/dist/client.js +96 -74
- package/dist/index.js +2 -2
- package/dist/tailwind.css +1 -1
- package/package.json +4 -3
- package/src/client/components/BottomSheet.tsx +32 -0
- package/src/client/components/Button.tsx +2 -1
- package/src/client/components/Drawer.tsx +23 -109
- package/src/client/components/README.md +12 -3
- package/src/client/components/SegmentedTabs.tsx +23 -4
- package/src/client/components/Sheet.LICENSE +21 -0
- package/src/client/components/Sheet.tsx +141 -0
- package/src/client/conversations/{ConversationDetailsDrawer.tsx → ConversationDetails.tsx} +19 -38
- package/src/client/conversations/ConversationDetailsFrame.tsx +91 -0
- package/src/client/conversations/ConversationEventLog.tsx +0 -1
- package/src/client/conversations/ConversationHeader.tsx +20 -28
- package/src/client/conversations/ConversationHeaderActions.tsx +5 -1
- package/src/client/conversations/ConversationPage.tsx +49 -36
- package/src/client/conversations/ForkConversationButton.tsx +0 -1
- package/src/client/conversations/README.md +18 -2
- package/src/client/conversations/SubagentTranscriptDrawer.tsx +0 -1
- package/src/client/conversations/TelemetryMetrics.tsx +2 -2
- package/src/client/conversations/TranscriptSubagentView.tsx +1 -1
- package/src/client/conversations/TranscriptTurnContextView.tsx +0 -1
- package/src/client/pages/automations/AutomationDetailsDrawer.tsx +0 -1
- package/src/client/pages/dev/ComponentsPage.tsx +16 -2
- package/src/client/pages/dev/SegmentedFixtures.tsx +8 -7
- package/src/client/pages/memory/MemoryDetailsDrawer.tsx +0 -1
- package/LICENSE +0 -201
|
@@ -22,37 +22,33 @@ import {
|
|
|
22
22
|
TranscriptViewToggle,
|
|
23
23
|
type ConversationArchiveAction,
|
|
24
24
|
} from "./ConversationHeaderActions";
|
|
25
|
-
import { ConversationDetailsDrawer } from "./ConversationDetailsDrawer";
|
|
26
25
|
import type { TranscriptViewMode } from "./transcriptRenderModel";
|
|
27
|
-
import type { SubagentTranscriptTarget } from "./SubagentTranscriptDrawer";
|
|
28
26
|
|
|
29
27
|
/** Show the conversation title, search, and details. */
|
|
30
28
|
export function ConversationHeader(props: {
|
|
31
29
|
/** Parent chain of a child Conversation. Root Conversations omit it. */
|
|
32
30
|
agentTree?: ReactNode;
|
|
33
31
|
copyAction?: ReactNode;
|
|
34
|
-
annotations: ReactNode;
|
|
35
32
|
archive: ConversationArchiveAction;
|
|
36
|
-
brief?: ReactNode;
|
|
37
33
|
conversationId: string;
|
|
34
|
+
detailsOpen: boolean;
|
|
35
|
+
onDetailsClick(): void;
|
|
38
36
|
/** Links to the fork source and to forks of this conversation. */
|
|
39
37
|
forks?: ReactNode;
|
|
40
|
-
identity: ReactNode;
|
|
41
38
|
linkedWork?: ReactNode;
|
|
42
|
-
lastActivityAt?: string;
|
|
43
|
-
sentryConversationUrl?: string;
|
|
44
39
|
live: boolean;
|
|
45
40
|
meta?: ReactNode;
|
|
46
|
-
onOpenSubagent(target: SubagentTranscriptTarget): void;
|
|
47
41
|
onSearchChange(value: string): void;
|
|
48
42
|
onViewChange(value: TranscriptViewMode): void;
|
|
49
43
|
privacy: ReactNode;
|
|
50
44
|
search: string;
|
|
51
|
-
stats: ReactNode;
|
|
52
45
|
title: string;
|
|
53
46
|
view: TranscriptViewMode;
|
|
54
47
|
}) {
|
|
55
|
-
const
|
|
48
|
+
const detailsOpen = props.detailsOpen;
|
|
49
|
+
const detailsButtonRef = useRef<HTMLButtonElement>(null);
|
|
50
|
+
const menuButtonRef = useRef<HTMLButtonElement>(null);
|
|
51
|
+
const wasDetailsOpen = useRef(detailsOpen);
|
|
56
52
|
const [searchOpen, setSearchOpen] = useState(false);
|
|
57
53
|
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
|
|
58
54
|
const searchInputRef = useRef<HTMLInputElement>(null);
|
|
@@ -60,6 +56,16 @@ export function ConversationHeader(props: {
|
|
|
60
56
|
const searchOpenVisible = searchOpen || props.search.length > 0;
|
|
61
57
|
const openMobileNavigation = useOpenMobileNavigation();
|
|
62
58
|
|
|
59
|
+
useEffect(() => {
|
|
60
|
+
if (wasDetailsOpen.current && !detailsOpen) {
|
|
61
|
+
const trigger = window.matchMedia("(max-width: 767px)").matches
|
|
62
|
+
? menuButtonRef.current
|
|
63
|
+
: detailsButtonRef.current;
|
|
64
|
+
trigger?.focus({ preventScroll: true });
|
|
65
|
+
}
|
|
66
|
+
wasDetailsOpen.current = detailsOpen;
|
|
67
|
+
}, [detailsOpen]);
|
|
68
|
+
|
|
63
69
|
useEffect(() => {
|
|
64
70
|
if (!searchOpenVisible) return;
|
|
65
71
|
searchInputRef.current?.focus();
|
|
@@ -107,6 +113,7 @@ export function ConversationHeader(props: {
|
|
|
107
113
|
const mobileOverflow = (
|
|
108
114
|
<div className="relative" ref={mobileMenuRef}>
|
|
109
115
|
<HeaderIconButton
|
|
116
|
+
ref={menuButtonRef}
|
|
110
117
|
label={mobileMenuOpen ? "Close conversation menu" : "Conversation menu"}
|
|
111
118
|
onClick={() => setMobileMenuOpen((open) => !open)}
|
|
112
119
|
pressed={mobileMenuOpen}
|
|
@@ -123,7 +130,7 @@ export function ConversationHeader(props: {
|
|
|
123
130
|
layout="menu"
|
|
124
131
|
onDetailsClick={() => {
|
|
125
132
|
setMobileMenuOpen(false);
|
|
126
|
-
|
|
133
|
+
props.onDetailsClick();
|
|
127
134
|
}}
|
|
128
135
|
onSearchClick={() => {
|
|
129
136
|
setMobileMenuOpen(false);
|
|
@@ -205,7 +212,8 @@ export function ConversationHeader(props: {
|
|
|
205
212
|
conversationId={props.conversationId}
|
|
206
213
|
copyAction={props.copyAction}
|
|
207
214
|
detailsOpen={detailsOpen}
|
|
208
|
-
|
|
215
|
+
detailsButtonRef={detailsButtonRef}
|
|
216
|
+
onDetailsClick={props.onDetailsClick}
|
|
209
217
|
onSearchClick={toggleSearch}
|
|
210
218
|
onViewChange={props.onViewChange}
|
|
211
219
|
searchOpen={searchOpenVisible}
|
|
@@ -252,22 +260,6 @@ export function ConversationHeader(props: {
|
|
|
252
260
|
</div>
|
|
253
261
|
) : null}
|
|
254
262
|
</header>
|
|
255
|
-
|
|
256
|
-
{detailsOpen ? (
|
|
257
|
-
<ConversationDetailsDrawer
|
|
258
|
-
annotations={props.annotations}
|
|
259
|
-
brief={props.brief}
|
|
260
|
-
conversationId={props.conversationId}
|
|
261
|
-
identity={props.identity}
|
|
262
|
-
lastActivityAt={props.lastActivityAt}
|
|
263
|
-
sentryConversationUrl={props.sentryConversationUrl}
|
|
264
|
-
onClose={() => setDetailsOpen(false)}
|
|
265
|
-
onOpenSubagent={props.onOpenSubagent}
|
|
266
|
-
privacy={props.privacy}
|
|
267
|
-
stats={props.stats}
|
|
268
|
-
title={props.title}
|
|
269
|
-
/>
|
|
270
|
-
) : null}
|
|
271
263
|
</>
|
|
272
264
|
);
|
|
273
265
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ReactNode } from "react";
|
|
1
|
+
import type { ReactNode, Ref } from "react";
|
|
2
2
|
import {
|
|
3
3
|
Archive,
|
|
4
4
|
ArchiveRestore,
|
|
@@ -30,6 +30,7 @@ export function ConversationHeaderActions(props: {
|
|
|
30
30
|
conversationId: string;
|
|
31
31
|
copyAction?: ReactNode;
|
|
32
32
|
detailsOpen: boolean;
|
|
33
|
+
detailsButtonRef?: Ref<HTMLButtonElement>;
|
|
33
34
|
/** `bar` is the desktop icon row. `menu` stacks actions for mobile overflow. */
|
|
34
35
|
layout?: "bar" | "menu";
|
|
35
36
|
onDetailsClick(): void;
|
|
@@ -94,6 +95,7 @@ export function ConversationHeaderActions(props: {
|
|
|
94
95
|
{props.copyAction}
|
|
95
96
|
<ArchiveConversationButton {...props.archive} layout="bar" />
|
|
96
97
|
<Button
|
|
98
|
+
ref={props.detailsButtonRef}
|
|
97
99
|
aria-label="Conversation details"
|
|
98
100
|
aria-pressed={props.detailsOpen}
|
|
99
101
|
className="ml-2"
|
|
@@ -180,6 +182,7 @@ function ViewModeButton(props: {
|
|
|
180
182
|
/** Shared icon control used by conversation and subagent transcript headers. */
|
|
181
183
|
export function HeaderIconButton(props: {
|
|
182
184
|
children: ReactNode;
|
|
185
|
+
ref?: Ref<HTMLButtonElement>;
|
|
183
186
|
label: string;
|
|
184
187
|
onClick(): void;
|
|
185
188
|
pressed?: boolean;
|
|
@@ -187,6 +190,7 @@ export function HeaderIconButton(props: {
|
|
|
187
190
|
return (
|
|
188
191
|
<IconButtonTooltip label={props.label}>
|
|
189
192
|
<Button
|
|
193
|
+
ref={props.ref}
|
|
190
194
|
aria-label={props.label}
|
|
191
195
|
aria-pressed={props.pressed}
|
|
192
196
|
className={cn(
|
|
@@ -26,6 +26,8 @@ import {
|
|
|
26
26
|
} from "./conversationRoutes";
|
|
27
27
|
import { ConversationComposer } from "./ConversationComposer";
|
|
28
28
|
import { ConversationHeader } from "./ConversationHeader";
|
|
29
|
+
import { ConversationDetails } from "./ConversationDetails";
|
|
30
|
+
import { cn } from "../styles";
|
|
29
31
|
import { ConversationForkLinks } from "./ConversationForkLinks";
|
|
30
32
|
import { ConversationAgentTree } from "./ConversationAgentTree";
|
|
31
33
|
import { ConversationHeaderMeta } from "./ConversationHeaderMeta";
|
|
@@ -76,6 +78,7 @@ export function ConversationPage(props: {
|
|
|
76
78
|
onRead?(conversationId: string, lastReadAt: string): void;
|
|
77
79
|
pendingArchiveUpdate?: PendingArchiveConversationUpdate;
|
|
78
80
|
}) {
|
|
81
|
+
const [detailsOpen, setDetailsOpen] = useState(false);
|
|
79
82
|
const [subagentTarget, setSubagentTarget] =
|
|
80
83
|
useState<SubagentTranscriptTarget>();
|
|
81
84
|
const [searchParams, setSearchParams] = useSearchParams();
|
|
@@ -196,7 +199,12 @@ export function ConversationPage(props: {
|
|
|
196
199
|
);
|
|
197
200
|
|
|
198
201
|
return (
|
|
199
|
-
|
|
202
|
+
<div
|
|
203
|
+
className={cn(
|
|
204
|
+
"grid h-full min-h-0 min-w-0",
|
|
205
|
+
detailsOpen && "xl:grid-cols-[minmax(0,1fr)_25rem]",
|
|
206
|
+
)}
|
|
207
|
+
>
|
|
200
208
|
<ChatLayout
|
|
201
209
|
scrollMinTall
|
|
202
210
|
scrollAriaLabel="Conversation transcript"
|
|
@@ -208,6 +216,8 @@ export function ConversationPage(props: {
|
|
|
208
216
|
agentTree ? <ConversationAgentTree nodes={agentTree} /> : null
|
|
209
217
|
}
|
|
210
218
|
conversationId={conversationId}
|
|
219
|
+
detailsOpen={detailsOpen}
|
|
220
|
+
onDetailsClick={() => setDetailsOpen((open) => !open)}
|
|
211
221
|
forks={
|
|
212
222
|
detail.data?.forkedFromConversationId ||
|
|
213
223
|
detail.data?.forks?.length ? (
|
|
@@ -220,8 +230,6 @@ export function ConversationPage(props: {
|
|
|
220
230
|
/>
|
|
221
231
|
) : null
|
|
222
232
|
}
|
|
223
|
-
lastActivityAt={conversation?.lastSeenAt}
|
|
224
|
-
sentryConversationUrl={detail.data?.sentryConversationUrl}
|
|
225
233
|
copyAction={
|
|
226
234
|
<CopyMarkdownButton
|
|
227
235
|
key={conversationDetail?.conversationId ?? "loading"}
|
|
@@ -236,11 +244,6 @@ export function ConversationPage(props: {
|
|
|
236
244
|
}
|
|
237
245
|
/>
|
|
238
246
|
}
|
|
239
|
-
annotations={
|
|
240
|
-
hasConversationAnnotations(detail.data?.annotations) ? (
|
|
241
|
-
<ConversationAnnotations detail={detail.data} />
|
|
242
|
-
) : null
|
|
243
|
-
}
|
|
244
247
|
linkedWork={
|
|
245
248
|
hasConversationAnnotations(detail.data?.annotations) ? (
|
|
246
249
|
<ConversationAnnotations
|
|
@@ -260,15 +263,6 @@ export function ConversationPage(props: {
|
|
|
260
263
|
}),
|
|
261
264
|
pending: archive.isPending,
|
|
262
265
|
}}
|
|
263
|
-
brief={
|
|
264
|
-
detail.data?.brief ? (
|
|
265
|
-
<ConversationBrief
|
|
266
|
-
brief={detail.data.brief}
|
|
267
|
-
variant="summary"
|
|
268
|
-
/>
|
|
269
|
-
) : null
|
|
270
|
-
}
|
|
271
|
-
identity={identity}
|
|
272
266
|
live={live}
|
|
273
267
|
meta={
|
|
274
268
|
<ConversationHeaderMeta
|
|
@@ -288,7 +282,6 @@ export function ConversationPage(props: {
|
|
|
288
282
|
}
|
|
289
283
|
/>
|
|
290
284
|
}
|
|
291
|
-
onOpenSubagent={setSubagentTarget}
|
|
292
285
|
onSearchChange={setSearch}
|
|
293
286
|
onViewChange={onViewChange}
|
|
294
287
|
privacy={
|
|
@@ -297,23 +290,6 @@ export function ConversationPage(props: {
|
|
|
297
290
|
/>
|
|
298
291
|
}
|
|
299
292
|
search={search}
|
|
300
|
-
stats={
|
|
301
|
-
hasConversationStats({
|
|
302
|
-
conversation,
|
|
303
|
-
detail: detail.data,
|
|
304
|
-
}) ? (
|
|
305
|
-
<div className="grid min-w-0 gap-5">
|
|
306
|
-
<ConversationStats
|
|
307
|
-
conversation={conversation}
|
|
308
|
-
detail={detail.data}
|
|
309
|
-
/>
|
|
310
|
-
<ConversationUsageBreakdown
|
|
311
|
-
conversation={conversation}
|
|
312
|
-
detail={detail.data}
|
|
313
|
-
/>
|
|
314
|
-
</div>
|
|
315
|
-
) : null
|
|
316
|
-
}
|
|
317
293
|
title={title}
|
|
318
294
|
view={view}
|
|
319
295
|
/>
|
|
@@ -382,11 +358,48 @@ export function ConversationPage(props: {
|
|
|
382
358
|
) : undefined
|
|
383
359
|
}
|
|
384
360
|
/>
|
|
361
|
+
{detailsOpen ? (
|
|
362
|
+
<ConversationDetails
|
|
363
|
+
conversationId={conversationId}
|
|
364
|
+
identity={identity}
|
|
365
|
+
lastActivityAt={conversation?.lastSeenAt}
|
|
366
|
+
sentryConversationUrl={detail.data?.sentryConversationUrl}
|
|
367
|
+
onClose={() => setDetailsOpen(false)}
|
|
368
|
+
onOpenSubagent={setSubagentTarget}
|
|
369
|
+
annotations={
|
|
370
|
+
hasConversationAnnotations(detail.data?.annotations) ? (
|
|
371
|
+
<ConversationAnnotations detail={detail.data} />
|
|
372
|
+
) : null
|
|
373
|
+
}
|
|
374
|
+
brief={
|
|
375
|
+
detail.data?.brief ? (
|
|
376
|
+
<ConversationBrief brief={detail.data.brief} variant="summary" />
|
|
377
|
+
) : null
|
|
378
|
+
}
|
|
379
|
+
stats={
|
|
380
|
+
hasConversationStats({
|
|
381
|
+
conversation,
|
|
382
|
+
detail: detail.data,
|
|
383
|
+
}) ? (
|
|
384
|
+
<div className="grid min-w-0 gap-5">
|
|
385
|
+
<ConversationStats
|
|
386
|
+
conversation={conversation}
|
|
387
|
+
detail={detail.data}
|
|
388
|
+
/>
|
|
389
|
+
<ConversationUsageBreakdown
|
|
390
|
+
conversation={conversation}
|
|
391
|
+
detail={detail.data}
|
|
392
|
+
/>
|
|
393
|
+
</div>
|
|
394
|
+
) : null
|
|
395
|
+
}
|
|
396
|
+
/>
|
|
397
|
+
) : null}
|
|
385
398
|
<SubagentTranscriptDrawer
|
|
386
399
|
onClose={() => setSubagentTarget(undefined)}
|
|
387
400
|
target={subagentTarget}
|
|
388
401
|
/>
|
|
389
|
-
|
|
402
|
+
</div>
|
|
390
403
|
);
|
|
391
404
|
}
|
|
392
405
|
|
|
@@ -40,10 +40,26 @@ shared controls and overlays.
|
|
|
40
40
|
Automation. Render this text with `TranscriptText` or `MentionText`. Both
|
|
41
41
|
require `mentions`, so a caller cannot forget them. Do not resolve names in
|
|
42
42
|
the dashboard, and do not rewrite tokens into links on the server.
|
|
43
|
-
- `TranscriptTurnContextView` owns context content. The shared `Drawer`
|
|
44
|
-
|
|
43
|
+
- `TranscriptTurnContextView` owns context content. The shared `Drawer` adds
|
|
44
|
+
its header and layout to the Base UI `Sheet` focus and dismissal boundary.
|
|
45
45
|
|
|
46
46
|
Review these components together in `/dev/transcripts`. Use the conversation
|
|
47
47
|
fixture for messages, events, activity, context, and typing. Use the attachment
|
|
48
48
|
fixture for file wrapping. Keep visual checks out of automated assertions;
|
|
49
49
|
browser tests cover opening, closing, and keyboard interaction.
|
|
50
|
+
|
|
51
|
+
## Conversation details
|
|
52
|
+
|
|
53
|
+
`ConversationPage` owns whether details are open. The page is keyed by the
|
|
54
|
+
Conversation, so switching Conversations closes details. `ConversationHeader`
|
|
55
|
+
only toggles details and restores focus through explicit control refs after
|
|
56
|
+
dismissal.
|
|
57
|
+
|
|
58
|
+
`ConversationDetails` owns one set of tabs and content. `ConversationDetailsFrame`
|
|
59
|
+
uses a 400px aside at 1280px and above. The chat and aside scroll separately.
|
|
60
|
+
Smaller windows use `BottomSheet` with a plain accessible label. The sheet and
|
|
61
|
+
side drawers share the Base UI `Sheet` focus rules and the app's document
|
|
62
|
+
scroll lock. A resize changes the frame and keeps the selected tab. Tabs stay fixed above the scrolling content in both
|
|
63
|
+
layouts. Only the swipe handle sits above the sheet tabs. Swipe down, tap the
|
|
64
|
+
backdrop, or press Escape to dismiss it. Base UI owns the transition lifecycle.
|
|
65
|
+
A pending exit cannot close a new panel or a different Conversation.
|
|
@@ -354,7 +354,7 @@ export function CostMetric(props: {
|
|
|
354
354
|
align?: "left" | "right";
|
|
355
355
|
auxiliaryCosts?: ConversationAuxiliaryCosts;
|
|
356
356
|
childAgentTotals?: ConversationChildAgentTotals;
|
|
357
|
-
/** Point to
|
|
357
|
+
/** Point to conversation details, which has the full breakdown. */
|
|
358
358
|
detailsHint?: boolean;
|
|
359
359
|
live?: boolean;
|
|
360
360
|
liveModelId?: string;
|
|
@@ -398,7 +398,7 @@ export function TokenMetric(props: {
|
|
|
398
398
|
align?: "left" | "right";
|
|
399
399
|
childAgentTotals?: ConversationChildAgentTotals;
|
|
400
400
|
compactionCount?: number;
|
|
401
|
-
/** Point to
|
|
401
|
+
/** Point to conversation details, which has the full breakdown. */
|
|
402
402
|
detailsHint?: boolean;
|
|
403
403
|
live?: boolean;
|
|
404
404
|
liveModelId?: string;
|
|
@@ -9,7 +9,7 @@ import { HighlightText } from "./transcriptSearch";
|
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Render a child agent as a compact card in the transcript stream. The card
|
|
12
|
-
* matches the child agent rows in
|
|
12
|
+
* matches the child agent rows in conversation details: name, status, and run
|
|
13
13
|
* time first, then the task title. It opens the child transcript.
|
|
14
14
|
*/
|
|
15
15
|
export function TranscriptSubagentView(props: {
|
|
@@ -37,6 +37,7 @@ import {
|
|
|
37
37
|
} from "../../components/controls/TimeRangeSelector";
|
|
38
38
|
import { EmptyTelemetry } from "../../components/EmptyTelemetry";
|
|
39
39
|
import { Drawer } from "../../components/Drawer";
|
|
40
|
+
import { BottomSheet } from "../../components/BottomSheet";
|
|
40
41
|
import { Field } from "../../components/Field";
|
|
41
42
|
import { Card } from "../../components/layout/Card";
|
|
42
43
|
import { DashboardChromeProvider } from "../../components/layout/DashboardChrome";
|
|
@@ -193,6 +194,7 @@ function FoundationsGalleryPage() {
|
|
|
193
194
|
const [pressed, setPressed] = useState(true);
|
|
194
195
|
const [navigationOpen, setNavigationOpen] = useState(false);
|
|
195
196
|
const [drawerOpen, setDrawerOpen] = useState(false);
|
|
197
|
+
const [sheetOpen, setSheetOpen] = useState(false);
|
|
196
198
|
const [nestedDrawerOpen, setNestedDrawerOpen] = useState(false);
|
|
197
199
|
const [searchQuery, setSearchQuery] = useState("deploy failure");
|
|
198
200
|
|
|
@@ -257,7 +259,6 @@ function FoundationsGalleryPage() {
|
|
|
257
259
|
{drawerOpen ? (
|
|
258
260
|
<Drawer
|
|
259
261
|
closeLabel="Close gallery details"
|
|
260
|
-
dismissLabel="Dismiss gallery details"
|
|
261
262
|
header={
|
|
262
263
|
<h2
|
|
263
264
|
id="gallery-drawer-title"
|
|
@@ -281,7 +282,6 @@ function FoundationsGalleryPage() {
|
|
|
281
282
|
? createPortal(
|
|
282
283
|
<Drawer
|
|
283
284
|
closeLabel="Close nested details"
|
|
284
|
-
dismissLabel="Dismiss nested details"
|
|
285
285
|
header={
|
|
286
286
|
<h2
|
|
287
287
|
id="gallery-nested-title"
|
|
@@ -305,6 +305,20 @@ function FoundationsGalleryPage() {
|
|
|
305
305
|
</Drawer>
|
|
306
306
|
) : null}
|
|
307
307
|
</Fixture>
|
|
308
|
+
<Fixture title="Bottom sheet">
|
|
309
|
+
<Button onClick={() => setSheetOpen(true)}>Open bottom sheet</Button>
|
|
310
|
+
{sheetOpen ? (
|
|
311
|
+
<BottomSheet
|
|
312
|
+
label="Gallery details"
|
|
313
|
+
onClose={() => setSheetOpen(false)}
|
|
314
|
+
>
|
|
315
|
+
<p className="m-0 p-4 pb-[max(1rem,env(safe-area-inset-bottom))] text-sm text-dashboard-text-muted">
|
|
316
|
+
Swipe the handle down, tap outside, or press Escape to return to
|
|
317
|
+
the page.
|
|
318
|
+
</p>
|
|
319
|
+
</BottomSheet>
|
|
320
|
+
) : null}
|
|
321
|
+
</Fixture>
|
|
308
322
|
<Fixture title="Theme">
|
|
309
323
|
<ThemeSwitcher className="max-w-64" />
|
|
310
324
|
</Fixture>
|
|
@@ -5,25 +5,26 @@ import { SegmentedTabs } from "../../components/SegmentedTabs";
|
|
|
5
5
|
|
|
6
6
|
/** Show panel tabs switching the panel below them. */
|
|
7
7
|
export function SegmentedTabsFixture() {
|
|
8
|
-
const [tab, setTab] = useState<"
|
|
8
|
+
const [tab, setTab] = useState<"summary" | "memories" | "agents">("summary");
|
|
9
9
|
return (
|
|
10
|
-
<div className="max-w-
|
|
10
|
+
<div className="h-64 max-w-xs">
|
|
11
11
|
<SegmentedTabs
|
|
12
|
+
scroll
|
|
12
13
|
items={[
|
|
13
|
-
{ label: "
|
|
14
|
+
{ label: "Summary", value: "summary" },
|
|
14
15
|
{ count: 3, label: "Memories", value: "memories" },
|
|
15
|
-
{ label: "
|
|
16
|
+
{ count: 2, label: "Subagents", value: "agents" },
|
|
16
17
|
]}
|
|
17
18
|
label="Conversation panels"
|
|
18
19
|
onChange={setTab}
|
|
19
20
|
value={tab}
|
|
20
21
|
>
|
|
21
22
|
<p className="m-0 text-sm leading-relaxed text-dashboard-text-muted">
|
|
22
|
-
{tab === "
|
|
23
|
-
? "
|
|
23
|
+
{tab === "summary"
|
|
24
|
+
? "Checkout latency increased after the latest release. Compare traces from before and after the deployment, then inspect the slow serialization span. The public brief links the affected repository, the release, and the open code change. Keep the rollout paused until the comparison confirms the cause. The next check is to run the same request against the previous release and compare its timings. Use the linked work to record the result and any rollback decision. The participants can continue this investigation in the conversation while keeping these details open. Model usage and related child conversations remain available in their own sections."
|
|
24
25
|
: tab === "memories"
|
|
25
26
|
? "What Junior learned from this conversation."
|
|
26
|
-
: "
|
|
27
|
+
: "Child conversations and their current work."}
|
|
27
28
|
</p>
|
|
28
29
|
</SegmentedTabs>
|
|
29
30
|
</div>
|