@aphrody/m3-ai 3.3.2 → 3.3.4
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 +7 -7
- package/dist/A2APeerInspector.d.ts +41 -0
- package/dist/A2APeerInspector.d.ts.map +1 -0
- package/dist/A2ATaskTimeline.d.ts +28 -0
- package/dist/A2ATaskTimeline.d.ts.map +1 -0
- package/dist/AgUiEventStream.d.ts +87 -0
- package/dist/AgUiEventStream.d.ts.map +1 -0
- package/dist/GeminiAccountMenu.d.ts +55 -0
- package/dist/GeminiAccountMenu.d.ts.map +1 -0
- package/dist/GeminiPersonalContextEdit.d.ts +29 -0
- package/dist/GeminiPersonalContextEdit.d.ts.map +1 -0
- package/dist/GoogleSpotlightBar.d.ts +32 -0
- package/dist/GoogleSpotlightBar.d.ts.map +1 -0
- package/dist/Markdown.d.ts +2 -1
- package/dist/Markdown.d.ts.map +1 -1
- package/dist/McpAppCard.d.ts +24 -0
- package/dist/McpAppCard.d.ts.map +1 -0
- package/dist/ModelSelector.d.ts.map +1 -1
- package/dist/TaskRow.d.ts.map +1 -1
- package/dist/ToolCall.d.ts.map +1 -1
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3629 -1950
- package/dist/index.js.map +32 -1
- package/package.json +17 -20
- package/src/A2APeerInspector.tsx +378 -0
- package/src/A2ATaskTimeline.tsx +218 -0
- package/src/AgUiEventStream.ts +488 -0
- package/src/ChatComposer.tsx +239 -0
- package/src/GeminiAccountMenu.tsx +212 -0
- package/src/GeminiPersonalContextEdit.tsx +147 -0
- package/src/GoogleSpotlightBar.tsx +165 -0
- package/src/InlineBranchCard.tsx +97 -0
- package/src/InlineWorkerCard.tsx +182 -0
- package/src/Markdown.tsx +701 -0
- package/src/McpAppCard.tsx +161 -0
- package/src/MessageBubble.tsx +95 -0
- package/src/ModelSelector.tsx +300 -0
- package/src/TaskCreateForm.tsx +123 -0
- package/src/TaskDetail.tsx +224 -0
- package/src/TaskList.tsx +136 -0
- package/src/TaskPriorityIcon.tsx +39 -0
- package/src/TaskRow.tsx +177 -0
- package/src/TaskStatusIcon.tsx +39 -0
- package/src/ToolCall.tsx +639 -0
- package/src/index.ts +107 -0
- package/src/internal.tsx +107 -0
- package/src/types.ts +212 -0
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// Benchmark: assets/ui/google-desktop-spotlight-bar.png (manifest item 1)
|
|
3
|
+
// Dimensions: ~1266x450px, border-radius 32dp (BRAND_CORNER_PROMPT_BAR_PX), Alt + Space capsule
|
|
4
|
+
|
|
5
|
+
"use client";
|
|
6
|
+
|
|
7
|
+
import { CircleButton, Icon } from "@aphrody/m3-primitives";
|
|
8
|
+
import { clsx } from "clsx";
|
|
9
|
+
import { type ReactNode, useState } from "react";
|
|
10
|
+
|
|
11
|
+
export interface GoogleSpotlightBarProps {
|
|
12
|
+
/** Placeholder text */
|
|
13
|
+
placeholder?: string;
|
|
14
|
+
/** Active query draft */
|
|
15
|
+
query?: string;
|
|
16
|
+
/** On change callback */
|
|
17
|
+
onQueryChange?: (val: string) => void;
|
|
18
|
+
/** Triggered on submit */
|
|
19
|
+
onSubmit?: (query: string) => void;
|
|
20
|
+
/** File/attachment action handler */
|
|
21
|
+
onAttach?: () => void;
|
|
22
|
+
/** Screen capture / share window handler */
|
|
23
|
+
onScreenCapture?: () => void;
|
|
24
|
+
/** Google Lens multimodal query handler */
|
|
25
|
+
onGoogleLens?: () => void;
|
|
26
|
+
/** User avatar element or image URL */
|
|
27
|
+
avatar?: ReactNode;
|
|
28
|
+
/** Additional container classes */
|
|
29
|
+
className?: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Floating Spotlight Assistant Bar & Search Capsule (Alt + Space).
|
|
34
|
+
* Benchmark parity: `google-desktop-spotlight-bar.png` (manifest item 1).
|
|
35
|
+
* Features:
|
|
36
|
+
* - Floating translucent frameless capsule with 32dp corner radius (--gemini-corner-prompt-bar).
|
|
37
|
+
* - Soft desktop elevation shadow.
|
|
38
|
+
* - Global shortcut pill indicator ('Alt + Space').
|
|
39
|
+
* - Multimodal action buttons (+ for attach, screen capture, Google Lens).
|
|
40
|
+
*/
|
|
41
|
+
export function GoogleSpotlightBar({
|
|
42
|
+
placeholder = "Ask anything or start typing...",
|
|
43
|
+
query: controlledQuery,
|
|
44
|
+
onQueryChange,
|
|
45
|
+
onSubmit,
|
|
46
|
+
onAttach,
|
|
47
|
+
onScreenCapture,
|
|
48
|
+
onGoogleLens,
|
|
49
|
+
avatar,
|
|
50
|
+
className,
|
|
51
|
+
}: GoogleSpotlightBarProps) {
|
|
52
|
+
const [internalQuery, setInternalQuery] = useState("");
|
|
53
|
+
const query = controlledQuery !== undefined ? controlledQuery : internalQuery;
|
|
54
|
+
|
|
55
|
+
const handleChange = (val: string) => {
|
|
56
|
+
if (controlledQuery === undefined) {
|
|
57
|
+
setInternalQuery(val);
|
|
58
|
+
}
|
|
59
|
+
onQueryChange?.(val);
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
|
63
|
+
if (e.key === "Enter" && query.trim().length > 0) {
|
|
64
|
+
e.preventDefault();
|
|
65
|
+
onSubmit?.(query);
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
return (
|
|
70
|
+
<search
|
|
71
|
+
aria-label="Google Spotlight Assistant"
|
|
72
|
+
className={clsx(
|
|
73
|
+
"google-spotlight-capsule relative mx-auto flex w-full max-w-[840px] items-center gap-3",
|
|
74
|
+
"rounded-[var(--gemini-corner-prompt-bar,32px)] bg-surface-container-high/90 px-5 py-3.5 backdrop-blur-xl",
|
|
75
|
+
"border border-outline-variant/30 text-on-surface shadow-md3-3 transition-all duration-200",
|
|
76
|
+
"focus-within:border-primary/50 focus-within:bg-surface-container-highest/95 focus-within:shadow-md3-4",
|
|
77
|
+
className,
|
|
78
|
+
)}
|
|
79
|
+
>
|
|
80
|
+
{/* Google Colorful G Logo */}
|
|
81
|
+
<div className="flex h-9 w-9 shrink-0 items-center justify-center">
|
|
82
|
+
<svg viewBox="0 0 24 24" width="24" height="24" aria-hidden="true">
|
|
83
|
+
<path
|
|
84
|
+
fill="#4285F4"
|
|
85
|
+
d="M23.745 12.27c0-.7-.06-1.4-.19-2.07H12v4.51h6.6c-.29 1.52-1.14 2.8-2.4 3.64v3.03h3.88c2.27-2.09 3.665-5.17 3.665-9.11z"
|
|
86
|
+
/>
|
|
87
|
+
<path
|
|
88
|
+
fill="#34A853"
|
|
89
|
+
d="M12 24c3.24 0 5.95-1.08 7.93-2.91l-3.88-3.03c-1.08.72-2.45 1.16-4.05 1.16-3.12 0-5.77-2.1-6.72-4.93H1.25v3.13C3.28 21.43 7.33 24 12 24z"
|
|
90
|
+
/>
|
|
91
|
+
<path
|
|
92
|
+
fill="#FBBC05"
|
|
93
|
+
d="M5.28 14.29c-.25-.72-.38-1.49-.38-2.29s.14-1.57.38-2.29V6.57H1.25C.45 8.16 0 9.98 0 12s.45 3.84 1.25 5.43l4.03-3.14z"
|
|
94
|
+
/>
|
|
95
|
+
<path
|
|
96
|
+
fill="#EA4335"
|
|
97
|
+
d="M12 4.75c1.77 0 3.35.61 4.6 1.8l3.42-3.42C17.95 1.19 15.24 0 12 0 7.33 0 3.28 2.57 1.25 6.57l4.03 3.14c.95-2.83 3.6-4.96 6.72-4.96z"
|
|
98
|
+
/>
|
|
99
|
+
</svg>
|
|
100
|
+
</div>
|
|
101
|
+
|
|
102
|
+
{/* Main multimodal text input */}
|
|
103
|
+
<input
|
|
104
|
+
type="text"
|
|
105
|
+
value={query}
|
|
106
|
+
onChange={(e) => handleChange(e.target.value)}
|
|
107
|
+
onKeyDown={handleKeyDown}
|
|
108
|
+
placeholder={placeholder}
|
|
109
|
+
aria-label={placeholder}
|
|
110
|
+
className="block flex-1 border-0 bg-transparent px-1 text-body-large text-on-surface caret-primary outline-none placeholder:text-on-surface-variant focus:outline-none focus:ring-0"
|
|
111
|
+
/>
|
|
112
|
+
|
|
113
|
+
{/* Multimodal Actions (Attach, Screen, Lens) */}
|
|
114
|
+
<div className="flex shrink-0 items-center gap-1.5">
|
|
115
|
+
{onAttach && (
|
|
116
|
+
<CircleButton
|
|
117
|
+
variant="standard"
|
|
118
|
+
size="sm"
|
|
119
|
+
onClick={onAttach}
|
|
120
|
+
aria-label="Add attachments"
|
|
121
|
+
title="Attach file"
|
|
122
|
+
>
|
|
123
|
+
<Icon name="add" size={20} />
|
|
124
|
+
</CircleButton>
|
|
125
|
+
)}
|
|
126
|
+
|
|
127
|
+
{onScreenCapture && (
|
|
128
|
+
<CircleButton
|
|
129
|
+
variant="standard"
|
|
130
|
+
size="sm"
|
|
131
|
+
onClick={onScreenCapture}
|
|
132
|
+
aria-label="Capture screen"
|
|
133
|
+
title="Share or capture screen"
|
|
134
|
+
>
|
|
135
|
+
<Icon name="screenshot" size={20} />
|
|
136
|
+
</CircleButton>
|
|
137
|
+
)}
|
|
138
|
+
|
|
139
|
+
{onGoogleLens && (
|
|
140
|
+
<CircleButton
|
|
141
|
+
variant="standard"
|
|
142
|
+
size="sm"
|
|
143
|
+
onClick={onGoogleLens}
|
|
144
|
+
aria-label="Google Lens"
|
|
145
|
+
title="Search with Google Lens"
|
|
146
|
+
className="hover:text-primary"
|
|
147
|
+
>
|
|
148
|
+
<Icon name="center_focus_strong" size={20} />
|
|
149
|
+
</CircleButton>
|
|
150
|
+
)}
|
|
151
|
+
|
|
152
|
+
{/* Global Shortcut Pill: Alt + Space */}
|
|
153
|
+
<div
|
|
154
|
+
aria-hidden="true"
|
|
155
|
+
className="ml-1 hidden select-none items-center rounded-full bg-surface-container px-2.5 py-1 text-label-small font-medium text-on-surface-variant sm:flex"
|
|
156
|
+
>
|
|
157
|
+
Alt + Space
|
|
158
|
+
</div>
|
|
159
|
+
|
|
160
|
+
{/* User avatar if provided */}
|
|
161
|
+
{avatar && <div className="ml-1 shrink-0">{avatar}</div>}
|
|
162
|
+
</div>
|
|
163
|
+
</search>
|
|
164
|
+
);
|
|
165
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0 AND MIT
|
|
2
|
+
// Derived from SpaceUI (packages/aphrody-spaceui/packages/ai/src/InlineBranchCard.tsx), MIT, rebuilt on Material Design 3.
|
|
3
|
+
|
|
4
|
+
import { useId, useState } from "react";
|
|
5
|
+
import { clsx } from "clsx";
|
|
6
|
+
import { AnimatePresence, motion } from "motion/react";
|
|
7
|
+
import { Icon } from "@aphrody/m3-primitives";
|
|
8
|
+
import { Markdown } from "./Markdown";
|
|
9
|
+
import { CircularIndicator, StatusChip, expandMotion, focusRing } from "./internal";
|
|
10
|
+
|
|
11
|
+
interface InlineBranchCardProps {
|
|
12
|
+
description: string;
|
|
13
|
+
completedAt: string | null;
|
|
14
|
+
conclusion?: string | null;
|
|
15
|
+
className?: string;
|
|
16
|
+
/** Start expanded (uncontrolled). */
|
|
17
|
+
defaultExpanded?: boolean;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** A thinking branch as an M3 filled card: header, status chip, expandable conclusion. */
|
|
21
|
+
function InlineBranchCard({
|
|
22
|
+
description,
|
|
23
|
+
completedAt,
|
|
24
|
+
conclusion,
|
|
25
|
+
className,
|
|
26
|
+
defaultExpanded = false,
|
|
27
|
+
}: InlineBranchCardProps) {
|
|
28
|
+
const [expanded, setExpanded] = useState(defaultExpanded);
|
|
29
|
+
const isRunning = !completedAt;
|
|
30
|
+
const contentId = useId();
|
|
31
|
+
const canExpand = !!conclusion;
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<div className={clsx("group flex min-w-0 flex-col items-start", className)}>
|
|
35
|
+
<div className="min-w-0 max-w-full overflow-hidden rounded-medium bg-surface-container text-on-surface">
|
|
36
|
+
<button
|
|
37
|
+
type="button"
|
|
38
|
+
onClick={() => canExpand && setExpanded((v) => !v)}
|
|
39
|
+
aria-expanded={canExpand ? expanded : undefined}
|
|
40
|
+
aria-controls={canExpand ? contentId : undefined}
|
|
41
|
+
className={clsx(
|
|
42
|
+
"flex w-full items-start gap-3 rounded-medium px-4 py-3 text-left transition-colors duration-200 ease-standard",
|
|
43
|
+
canExpand && "hover:bg-on-surface/8 active:bg-on-surface/10",
|
|
44
|
+
focusRing,
|
|
45
|
+
)}
|
|
46
|
+
>
|
|
47
|
+
<div className="flex size-8 shrink-0 items-center justify-center">
|
|
48
|
+
{isRunning ? (
|
|
49
|
+
<CircularIndicator size={20} label="Thinking" />
|
|
50
|
+
) : (
|
|
51
|
+
<div className="flex size-8 items-center justify-center rounded-full bg-primary-container text-on-primary-container">
|
|
52
|
+
<Icon name="alt_route" size={18} />
|
|
53
|
+
</div>
|
|
54
|
+
)}
|
|
55
|
+
</div>
|
|
56
|
+
<div className="min-w-0 flex-1 self-center">
|
|
57
|
+
<div className="flex items-center gap-2">
|
|
58
|
+
<div className="line-clamp-2 min-w-0 flex-1 text-title-small text-on-surface">
|
|
59
|
+
{description}
|
|
60
|
+
</div>
|
|
61
|
+
<StatusChip tone={isRunning ? "primary" : "success"}>
|
|
62
|
+
{isRunning ? "thinking" : "done"}
|
|
63
|
+
</StatusChip>
|
|
64
|
+
</div>
|
|
65
|
+
</div>
|
|
66
|
+
{canExpand && (
|
|
67
|
+
<Icon
|
|
68
|
+
name="expand_more"
|
|
69
|
+
size={20}
|
|
70
|
+
className={clsx(
|
|
71
|
+
"mt-1.5 shrink-0 text-on-surface-variant transition-transform duration-300 ease-emphasized",
|
|
72
|
+
expanded ? "rotate-180" : "",
|
|
73
|
+
)}
|
|
74
|
+
/>
|
|
75
|
+
)}
|
|
76
|
+
</button>
|
|
77
|
+
<AnimatePresence initial={false}>
|
|
78
|
+
{expanded && conclusion ? (
|
|
79
|
+
<motion.div
|
|
80
|
+
id={contentId}
|
|
81
|
+
key="conclusion"
|
|
82
|
+
{...expandMotion}
|
|
83
|
+
className="overflow-hidden"
|
|
84
|
+
>
|
|
85
|
+
<div className="border-t border-outline-variant px-4 py-3">
|
|
86
|
+
<Markdown content={conclusion} size="small" className="text-on-surface-variant" />
|
|
87
|
+
</div>
|
|
88
|
+
</motion.div>
|
|
89
|
+
) : null}
|
|
90
|
+
</AnimatePresence>
|
|
91
|
+
</div>
|
|
92
|
+
</div>
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export { InlineBranchCard };
|
|
97
|
+
export type { InlineBranchCardProps };
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0 AND MIT
|
|
2
|
+
// Derived from SpaceUI (packages/aphrody-spaceui/packages/ai/src/InlineWorkerCard.tsx), MIT, rebuilt on Material Design 3.
|
|
3
|
+
|
|
4
|
+
import { useId, useMemo, useState } from "react";
|
|
5
|
+
import { clsx } from "clsx";
|
|
6
|
+
import { AnimatePresence, motion } from "motion/react";
|
|
7
|
+
import { Button, Icon } from "@aphrody/m3-primitives";
|
|
8
|
+
import { ToolCall } from "./ToolCall";
|
|
9
|
+
import { Markdown } from "./Markdown";
|
|
10
|
+
import type { TranscriptStep } from "./types";
|
|
11
|
+
import { pairTranscriptSteps } from "./types";
|
|
12
|
+
import { CircularIndicator, StatusChip, type ChipTone, expandMotion, focusRing } from "./internal";
|
|
13
|
+
|
|
14
|
+
/** Collapse runs of 3+ spaces to 2, preventing Markdown 4-space code blocks. */
|
|
15
|
+
function stripExcessWhitespace(text: string): string {
|
|
16
|
+
return text.replace(/ {3,}/g, " ");
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
interface InlineWorkerCardProps {
|
|
20
|
+
title: string;
|
|
21
|
+
status: string;
|
|
22
|
+
toolCallCount: number;
|
|
23
|
+
liveStatus?: string | null;
|
|
24
|
+
transcript: TranscriptStep[];
|
|
25
|
+
isTranscriptLoading?: boolean;
|
|
26
|
+
onCopyLogs?: () => void;
|
|
27
|
+
onCancel?: () => void;
|
|
28
|
+
className?: string;
|
|
29
|
+
/** Start expanded (uncontrolled). */
|
|
30
|
+
defaultExpanded?: boolean;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function statusTone(status: string): ChipTone {
|
|
34
|
+
switch (status) {
|
|
35
|
+
case "running":
|
|
36
|
+
return "primary";
|
|
37
|
+
case "completed":
|
|
38
|
+
return "success";
|
|
39
|
+
case "failed":
|
|
40
|
+
case "error":
|
|
41
|
+
case "cancelled":
|
|
42
|
+
return "error";
|
|
43
|
+
default:
|
|
44
|
+
return "neutral";
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** A worker run as an M3 filled card: header, status chip, expandable transcript. */
|
|
49
|
+
function InlineWorkerCard({
|
|
50
|
+
title,
|
|
51
|
+
status,
|
|
52
|
+
toolCallCount,
|
|
53
|
+
liveStatus,
|
|
54
|
+
transcript,
|
|
55
|
+
isTranscriptLoading,
|
|
56
|
+
onCopyLogs,
|
|
57
|
+
onCancel,
|
|
58
|
+
className,
|
|
59
|
+
defaultExpanded = false,
|
|
60
|
+
}: InlineWorkerCardProps) {
|
|
61
|
+
const [expanded, setExpanded] = useState(defaultExpanded);
|
|
62
|
+
const contentId = useId();
|
|
63
|
+
|
|
64
|
+
const items = useMemo(() => pairTranscriptSteps(transcript), [transcript]);
|
|
65
|
+
|
|
66
|
+
const isRunning = status === "running";
|
|
67
|
+
const isDone = status === "completed";
|
|
68
|
+
const tone = statusTone(status);
|
|
69
|
+
|
|
70
|
+
return (
|
|
71
|
+
<div className={clsx("group flex min-w-0 flex-col items-start", className)}>
|
|
72
|
+
<div className="min-w-0 max-w-full overflow-hidden rounded-medium bg-surface-container text-on-surface">
|
|
73
|
+
<button
|
|
74
|
+
type="button"
|
|
75
|
+
onClick={() => setExpanded((v) => !v)}
|
|
76
|
+
aria-expanded={expanded}
|
|
77
|
+
aria-controls={contentId}
|
|
78
|
+
className={clsx(
|
|
79
|
+
"flex w-full items-start gap-3 rounded-medium px-4 py-3 text-left transition-colors duration-200 ease-standard",
|
|
80
|
+
"hover:bg-on-surface/8 active:bg-on-surface/10",
|
|
81
|
+
focusRing,
|
|
82
|
+
)}
|
|
83
|
+
>
|
|
84
|
+
<div className="flex size-8 shrink-0 items-center justify-center">
|
|
85
|
+
{isRunning ? (
|
|
86
|
+
<CircularIndicator size={20} label="Worker running" />
|
|
87
|
+
) : isDone ? (
|
|
88
|
+
<div className="flex size-8 items-center justify-center rounded-full bg-success-container text-on-success-container">
|
|
89
|
+
<Icon name="check_circle" size={18} filled />
|
|
90
|
+
</div>
|
|
91
|
+
) : tone === "error" ? (
|
|
92
|
+
<div className="flex size-8 items-center justify-center rounded-full bg-error-container text-on-error-container">
|
|
93
|
+
<Icon name="error" size={18} filled />
|
|
94
|
+
</div>
|
|
95
|
+
) : (
|
|
96
|
+
<div className="flex size-8 items-center justify-center rounded-full bg-surface-container-highest text-on-surface-variant">
|
|
97
|
+
<Icon name="build" size={18} />
|
|
98
|
+
</div>
|
|
99
|
+
)}
|
|
100
|
+
</div>
|
|
101
|
+
|
|
102
|
+
<div className="min-w-0 flex-1">
|
|
103
|
+
<div className="flex items-center gap-2">
|
|
104
|
+
<div className="line-clamp-2 min-w-0 flex-1 text-title-small text-on-surface">
|
|
105
|
+
{title}
|
|
106
|
+
</div>
|
|
107
|
+
<StatusChip tone={tone}>{status}</StatusChip>
|
|
108
|
+
</div>
|
|
109
|
+
<div className="mt-1 flex items-center gap-2 text-body-small text-on-surface-variant">
|
|
110
|
+
<span>{toolCallCount} tool calls</span>
|
|
111
|
+
{liveStatus ? <span className="truncate text-outline">{liveStatus}</span> : null}
|
|
112
|
+
</div>
|
|
113
|
+
</div>
|
|
114
|
+
|
|
115
|
+
<Icon
|
|
116
|
+
name="expand_more"
|
|
117
|
+
size={20}
|
|
118
|
+
className={clsx(
|
|
119
|
+
"mt-1.5 shrink-0 text-on-surface-variant transition-transform duration-300 ease-emphasized",
|
|
120
|
+
expanded ? "rotate-180" : "",
|
|
121
|
+
)}
|
|
122
|
+
/>
|
|
123
|
+
</button>
|
|
124
|
+
|
|
125
|
+
<AnimatePresence initial={false}>
|
|
126
|
+
{expanded ? (
|
|
127
|
+
<motion.div
|
|
128
|
+
id={contentId}
|
|
129
|
+
key="transcript"
|
|
130
|
+
{...expandMotion}
|
|
131
|
+
className="overflow-hidden"
|
|
132
|
+
>
|
|
133
|
+
<div className="flex flex-col gap-2 border-t border-outline-variant px-4 py-3">
|
|
134
|
+
{isTranscriptLoading ? (
|
|
135
|
+
<div className="flex items-center gap-2 text-body-small text-on-surface-variant">
|
|
136
|
+
<CircularIndicator size={16} label="Loading transcript" />
|
|
137
|
+
Loading worker transcript...
|
|
138
|
+
</div>
|
|
139
|
+
) : items.length > 0 ? (
|
|
140
|
+
items.map((item) =>
|
|
141
|
+
item.kind === "tool" ? (
|
|
142
|
+
<ToolCall key={item.pair.id} pair={item.pair} />
|
|
143
|
+
) : (
|
|
144
|
+
<Markdown
|
|
145
|
+
key={`text-${item.text}`}
|
|
146
|
+
content={stripExcessWhitespace(item.text)}
|
|
147
|
+
size="small"
|
|
148
|
+
className="text-on-surface-variant"
|
|
149
|
+
/>
|
|
150
|
+
),
|
|
151
|
+
)
|
|
152
|
+
) : (
|
|
153
|
+
<div className="text-body-small text-on-surface-variant">No tool calls yet.</div>
|
|
154
|
+
)}
|
|
155
|
+
</div>
|
|
156
|
+
</motion.div>
|
|
157
|
+
) : null}
|
|
158
|
+
</AnimatePresence>
|
|
159
|
+
</div>
|
|
160
|
+
|
|
161
|
+
{(onCopyLogs || (onCancel && isRunning)) && (
|
|
162
|
+
<div className="mt-1 flex gap-1 opacity-0 transition-opacity duration-200 ease-standard group-focus-within:opacity-100 group-hover:opacity-100">
|
|
163
|
+
{onCopyLogs && (
|
|
164
|
+
<Button variant="text" size="xs" onClick={onCopyLogs} title="Copy logs">
|
|
165
|
+
<Icon name="content_copy" size={18} />
|
|
166
|
+
Copy
|
|
167
|
+
</Button>
|
|
168
|
+
)}
|
|
169
|
+
{onCancel && isRunning && (
|
|
170
|
+
<Button variant="text" size="xs" onClick={onCancel} title="Cancel">
|
|
171
|
+
<Icon name="stop" size={18} />
|
|
172
|
+
Cancel
|
|
173
|
+
</Button>
|
|
174
|
+
)}
|
|
175
|
+
</div>
|
|
176
|
+
)}
|
|
177
|
+
</div>
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export { InlineWorkerCard };
|
|
182
|
+
export type { InlineWorkerCardProps };
|