@tulipfarm/sprout 0.2.0 → 0.3.1
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 +111 -0
- package/dist/components/agent-fanout.d.ts +46 -0
- package/dist/components/agent-fanout.js +228 -0
- package/dist/components/chat-confirmation.d.ts +4 -2
- package/dist/components/chat-confirmation.js +2 -2
- package/dist/components/chat-sources.js +11 -2
- package/dist/components/presence-cursor.d.ts +19 -0
- package/dist/components/presence-cursor.js +14 -0
- package/dist/components/result-card.d.ts +6 -1
- package/dist/components/result-card.js +2 -2
- package/dist/components/schema-diagram.js +5 -78
- package/dist/components/sql-viewer.d.ts +76 -0
- package/dist/components/sql-viewer.js +409 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/internal/flow.d.ts +22 -0
- package/dist/internal/flow.js +82 -0
- package/dist/styles/agent-fanout.css +529 -0
- package/dist/styles/index.css +3 -0
- package/dist/styles/message.css +1 -1
- package/dist/styles/presence-cursor.css +68 -0
- package/dist/styles/result-card.css +14 -0
- package/dist/styles/sql-viewer.css +406 -0
- package/dist/styles/themes/tulipfarm.css +1 -1
- package/package.json +19 -7
package/README.md
CHANGED
|
@@ -94,6 +94,117 @@ Each component imports from `@tulipfarm/sprout/<name>`, for example
|
|
|
94
94
|
`@tulipfarm/sprout/dialog`. See the [component reference](https://github.com/TulipFarm/sprout/blob/main/docs/components.md)
|
|
95
95
|
for notes on each one.
|
|
96
96
|
|
|
97
|
+
### Bundle size
|
|
98
|
+
|
|
99
|
+
<!-- bundle-size:start -->
|
|
100
|
+
|
|
101
|
+
Full library: **786.4 kB** gzip. Each component path is smaller. Sizes are minified, with React and other peer dependencies left out.
|
|
102
|
+
|
|
103
|
+
<details>
|
|
104
|
+
<summary>Size of each component</summary>
|
|
105
|
+
|
|
106
|
+
| Component | Raw | Gzip | Brotli |
|
|
107
|
+
| ------------------- | -------: | -------: | -------: |
|
|
108
|
+
| `accordion` | 64.1 kB | 21.9 kB | 19.3 kB |
|
|
109
|
+
| `agent-fanout` | 260.9 kB | 86.7 kB | 74.6 kB |
|
|
110
|
+
| `alert` | 35.2 kB | 11.5 kB | 10.1 kB |
|
|
111
|
+
| `alert-dialog` | 169.1 kB | 57.0 kB | 49.8 kB |
|
|
112
|
+
| `aspect-ratio` | 30.2 kB | 9.7 kB | 8.5 kB |
|
|
113
|
+
| `attachment` | 459.9 kB | 155.6 kB | 128.6 kB |
|
|
114
|
+
| `avatar` | 45.5 kB | 15.4 kB | 13.6 kB |
|
|
115
|
+
| `badge` | 34.9 kB | 11.4 kB | 10.0 kB |
|
|
116
|
+
| `brand-icon` | 195.1 kB | 70.2 kB | 61.2 kB |
|
|
117
|
+
| `breadcrumb` | 40.5 kB | 13.4 kB | 11.7 kB |
|
|
118
|
+
| `button` | 50.5 kB | 16.7 kB | 14.6 kB |
|
|
119
|
+
| `button-group` | 37.9 kB | 12.1 kB | 10.7 kB |
|
|
120
|
+
| `calendar` | 312.3 kB | 103.5 kB | 88.2 kB |
|
|
121
|
+
| `canvas` | 235.1 kB | 80.8 kB | 69.2 kB |
|
|
122
|
+
| `card` | 35.4 kB | 11.6 kB | 10.1 kB |
|
|
123
|
+
| `carousel` | 60.4 kB | 19.9 kB | 17.6 kB |
|
|
124
|
+
| `chart` | 393.4 kB | 119.0 kB | 101.3 kB |
|
|
125
|
+
| `chat-confirmation` | 76.7 kB | 25.8 kB | 22.4 kB |
|
|
126
|
+
| `chat-plan` | 42.3 kB | 13.4 kB | 11.7 kB |
|
|
127
|
+
| `chat-sources` | 206.2 kB | 70.5 kB | 60.3 kB |
|
|
128
|
+
| `chat-input` | 962.4 kB | 317.0 kB | 261.5 kB |
|
|
129
|
+
| `checkbox` | 60.0 kB | 21.0 kB | 18.5 kB |
|
|
130
|
+
| `choice-list` | 43.0 kB | 14.5 kB | 12.6 kB |
|
|
131
|
+
| `code-block` | 74.4 kB | 25.9 kB | 22.3 kB |
|
|
132
|
+
| `collapsible` | 55.5 kB | 18.7 kB | 16.5 kB |
|
|
133
|
+
| `combobox` | 268.6 kB | 90.9 kB | 77.7 kB |
|
|
134
|
+
| `command` | 181.9 kB | 61.6 kB | 53.5 kB |
|
|
135
|
+
| `context-menu` | 242.8 kB | 81.0 kB | 69.7 kB |
|
|
136
|
+
| `dialog` | 170.7 kB | 57.5 kB | 50.2 kB |
|
|
137
|
+
| `direction` | 1.4 kB | 0.7 kB | 0.7 kB |
|
|
138
|
+
| `dropdown-menu` | 239.6 kB | 80.0 kB | 69.2 kB |
|
|
139
|
+
| `field` | 97.9 kB | 34.2 kB | 29.5 kB |
|
|
140
|
+
| `file-explorer` | 527.9 kB | 180.1 kB | 147.5 kB |
|
|
141
|
+
| `file-viewer` | 459.5 kB | 155.5 kB | 128.4 kB |
|
|
142
|
+
| `form` | 109.7 kB | 37.3 kB | 32.2 kB |
|
|
143
|
+
| `glass-banner` | 33.0 kB | 10.6 kB | 9.4 kB |
|
|
144
|
+
| `gradient-banner` | 32.2 kB | 10.3 kB | 9.1 kB |
|
|
145
|
+
| `hover-card` | 163.2 kB | 56.3 kB | 49.1 kB |
|
|
146
|
+
| `input` | 108.2 kB | 37.4 kB | 32.3 kB |
|
|
147
|
+
| `input-group` | 119.1 kB | 40.5 kB | 34.8 kB |
|
|
148
|
+
| `input-otp` | 65.3 kB | 22.9 kB | 20.3 kB |
|
|
149
|
+
| `kbd` | 30.4 kB | 9.7 kB | 8.5 kB |
|
|
150
|
+
| `label` | 30.2 kB | 9.6 kB | 8.4 kB |
|
|
151
|
+
| `mention-list` | 52.6 kB | 17.2 kB | 15.2 kB |
|
|
152
|
+
| `menubar` | 244.9 kB | 82.2 kB | 70.7 kB |
|
|
153
|
+
| `message` | 36.2 kB | 11.7 kB | 10.3 kB |
|
|
154
|
+
| `message-actions` | 71.4 kB | 24.9 kB | 20.8 kB |
|
|
155
|
+
| `message-response` | 232.7 kB | 74.2 kB | 64.2 kB |
|
|
156
|
+
| `message-scroller` | 69.0 kB | 22.1 kB | 19.6 kB |
|
|
157
|
+
| `navigation-menu` | 182.3 kB | 63.0 kB | 54.7 kB |
|
|
158
|
+
| `notice-banner` | 30.4 kB | 9.7 kB | 8.5 kB |
|
|
159
|
+
| `page-content` | 30.2 kB | 9.6 kB | 8.4 kB |
|
|
160
|
+
| `page-header` | 54.6 kB | 17.6 kB | 15.5 kB |
|
|
161
|
+
| `pagination` | 55.3 kB | 17.9 kB | 15.6 kB |
|
|
162
|
+
| `plan-outline` | 79.9 kB | 26.4 kB | 22.9 kB |
|
|
163
|
+
| `popover` | 178.0 kB | 60.7 kB | 52.6 kB |
|
|
164
|
+
| `presence-cursor` | 30.8 kB | 9.9 kB | 8.6 kB |
|
|
165
|
+
| `progress` | 39.8 kB | 13.2 kB | 11.6 kB |
|
|
166
|
+
| `prompt-list` | 199.0 kB | 71.9 kB | 62.6 kB |
|
|
167
|
+
| `questionnaire` | 69.5 kB | 23.3 kB | 20.4 kB |
|
|
168
|
+
| `radio-group` | 80.8 kB | 27.9 kB | 24.7 kB |
|
|
169
|
+
| `reasoning-effort` | 35.5 kB | 12.0 kB | 10.5 kB |
|
|
170
|
+
| `resizable` | 39.4 kB | 13.4 kB | 11.8 kB |
|
|
171
|
+
| `result-card` | 240.3 kB | 85.1 kB | 72.1 kB |
|
|
172
|
+
| `review-card` | 61.6 kB | 21.0 kB | 18.4 kB |
|
|
173
|
+
| `rich-text-editor` | 442.5 kB | 140.2 kB | 119.8 kB |
|
|
174
|
+
| `schema-diagram` | 247.9 kB | 82.9 kB | 71.2 kB |
|
|
175
|
+
| `sql-viewer` | 265.6 kB | 89.1 kB | 76.3 kB |
|
|
176
|
+
| `scroll-area` | 57.3 kB | 19.7 kB | 17.4 kB |
|
|
177
|
+
| `search-dialog` | 190.2 kB | 64.6 kB | 55.5 kB |
|
|
178
|
+
| `search-field` | 122.2 kB | 41.4 kB | 35.5 kB |
|
|
179
|
+
| `select` | 230.4 kB | 79.3 kB | 67.8 kB |
|
|
180
|
+
| `selection-slot` | 31.5 kB | 10.2 kB | 8.9 kB |
|
|
181
|
+
| `separator` | 36.9 kB | 11.9 kB | 10.5 kB |
|
|
182
|
+
| `settings-list` | 31.1 kB | 9.9 kB | 8.7 kB |
|
|
183
|
+
| `sheet` | 213.4 kB | 72.9 kB | 63.2 kB |
|
|
184
|
+
| `sidebar` | 251.1 kB | 85.8 kB | 72.8 kB |
|
|
185
|
+
| `skeleton` | 35.1 kB | 11.5 kB | 10.1 kB |
|
|
186
|
+
| `slider` | 83.6 kB | 29.5 kB | 25.9 kB |
|
|
187
|
+
| `spinner` | 36.5 kB | 11.6 kB | 10.1 kB |
|
|
188
|
+
| `state-panel` | 459.7 kB | 155.6 kB | 128.5 kB |
|
|
189
|
+
| `state-sample` | 33.5 kB | 10.5 kB | 9.2 kB |
|
|
190
|
+
| `switch` | 50.3 kB | 17.3 kB | 15.3 kB |
|
|
191
|
+
| `table` | 536.6 kB | 171.9 kB | 144.4 kB |
|
|
192
|
+
| `table-nuqs` | 2.1 kB | 1.1 kB | 1.0 kB |
|
|
193
|
+
| `tabs` | 80.2 kB | 27.6 kB | 24.4 kB |
|
|
194
|
+
| `textarea` | 98.2 kB | 34.2 kB | 29.5 kB |
|
|
195
|
+
| `toast` | 189.5 kB | 65.2 kB | 56.7 kB |
|
|
196
|
+
| `toggle` | 46.1 kB | 15.7 kB | 13.8 kB |
|
|
197
|
+
| `toggle-group` | 67.1 kB | 23.0 kB | 20.3 kB |
|
|
198
|
+
| `tool-activity` | 86.7 kB | 29.2 kB | 25.2 kB |
|
|
199
|
+
| `toolbar-status` | 189.3 kB | 64.3 kB | 55.6 kB |
|
|
200
|
+
| `tooltip` | 164.7 kB | 56.7 kB | 49.4 kB |
|
|
201
|
+
| `typed-text` | 31.2 kB | 10.1 kB | 8.8 kB |
|
|
202
|
+
|
|
203
|
+
</details>
|
|
204
|
+
<!-- bundle-size:end -->
|
|
205
|
+
|
|
206
|
+
CI compares sizes on each pull request. Run `pnpm size` to see them locally.
|
|
207
|
+
|
|
97
208
|
| Group | Components |
|
|
98
209
|
| ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
99
210
|
| **Actions** | Button · Button Group · Toggle · Toggle Group · Toolbar Status · Kbd |
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
export declare const agentFanoutStatusNames: readonly ["queued", "working", "done", "failed", "stopped"];
|
|
3
|
+
export declare const agentFanoutStateNames: readonly ["running", "done", "stopped"];
|
|
4
|
+
export type AgentFanoutStatus = (typeof agentFanoutStatusNames)[number];
|
|
5
|
+
export type AgentFanoutState = (typeof agentFanoutStateNames)[number];
|
|
6
|
+
export interface AgentFanoutAgent {
|
|
7
|
+
id: string;
|
|
8
|
+
name: string;
|
|
9
|
+
/** A 16px icon for the subagent's role. */
|
|
10
|
+
icon: ReactNode;
|
|
11
|
+
/** What the subagent was asked to do, such as “Web search”. */
|
|
12
|
+
task: string;
|
|
13
|
+
/** What it found. Replaces `task` once the subagent is done or failed. */
|
|
14
|
+
result?: string | undefined;
|
|
15
|
+
status: AgentFanoutStatus;
|
|
16
|
+
/** How long the subagent took, in ms. Shown once it is done. */
|
|
17
|
+
duration?: number | undefined;
|
|
18
|
+
/** Any CSS color. Defaults to the next of five `--sprout-chart-*` colors. */
|
|
19
|
+
color?: string | undefined;
|
|
20
|
+
/** Subagents this agent calls in turn. They take its color. */
|
|
21
|
+
agents?: AgentFanoutAgent[] | undefined;
|
|
22
|
+
}
|
|
23
|
+
export interface AgentFanoutProps {
|
|
24
|
+
/** The parent agent's task, shown above the graph. */
|
|
25
|
+
task: ReactNode;
|
|
26
|
+
agents: AgentFanoutAgent[];
|
|
27
|
+
/** The parent agent. `done` once it has merged the results into an answer. */
|
|
28
|
+
state?: AgentFanoutState | undefined;
|
|
29
|
+
/** Time since the parent started, in ms. */
|
|
30
|
+
elapsed?: number | undefined;
|
|
31
|
+
/** Replaces the footer text that names the current phase. */
|
|
32
|
+
label?: ReactNode;
|
|
33
|
+
/** Shows the per-agent list at first. It starts closed. */
|
|
34
|
+
defaultOpen?: boolean | undefined;
|
|
35
|
+
/** Shows a Stop button while the parent is running. */
|
|
36
|
+
onStop?: (() => void) | undefined;
|
|
37
|
+
/** Shows a Run again button once the parent is done or stopped. */
|
|
38
|
+
onRestart?: (() => void) | undefined;
|
|
39
|
+
className?: string | undefined;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* A parent agent that hands one task to several subagents and merges what
|
|
43
|
+
* they find. Each status change sends a pulse along the subagent's line:
|
|
44
|
+
* down when it starts, back up when it finishes.
|
|
45
|
+
*/
|
|
46
|
+
export declare function AgentFanout({ task, agents, state, elapsed, label, defaultOpen, onStop, onRestart, className, }: AgentFanoutProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import { BaseEdge, EdgeLabelRenderer, Handle, Position, ReactFlow, } from "@xyflow/react";
|
|
4
|
+
import { useLayoutEffect, useRef, useState, } from "react";
|
|
5
|
+
import { Check } from "reicon-react/icons/Check";
|
|
6
|
+
import { ChevronDown } from "reicon-react/icons/ChevronDown";
|
|
7
|
+
import { Danger } from "reicon-react/icons/Danger";
|
|
8
|
+
import { MinusCircle } from "reicon-react/icons/MinusCircle";
|
|
9
|
+
import { RotateLeft } from "reicon-react/icons/RotateLeft";
|
|
10
|
+
import { Sparkle } from "reicon-react/icons/Sparkle";
|
|
11
|
+
import { cn } from "../internal/classes.js";
|
|
12
|
+
import { Button } from "./button.js";
|
|
13
|
+
import { Collapsible, CollapsibleContent, CollapsibleTrigger, } from "./collapsible.js";
|
|
14
|
+
export const agentFanoutStatusNames = [
|
|
15
|
+
"queued",
|
|
16
|
+
"working",
|
|
17
|
+
"done",
|
|
18
|
+
"failed",
|
|
19
|
+
"stopped",
|
|
20
|
+
];
|
|
21
|
+
export const agentFanoutStateNames = ["running", "done", "stopped"];
|
|
22
|
+
const rootTop = 12;
|
|
23
|
+
const rootSize = 44;
|
|
24
|
+
const levelTop = 120;
|
|
25
|
+
const levelGap = 108;
|
|
26
|
+
const nodeLabel = 22;
|
|
27
|
+
const bottomGap = 36;
|
|
28
|
+
const colorCount = 5;
|
|
29
|
+
const statusText = {
|
|
30
|
+
queued: "Queued",
|
|
31
|
+
working: "Working",
|
|
32
|
+
done: "Done",
|
|
33
|
+
failed: "Failed",
|
|
34
|
+
stopped: "Stopped",
|
|
35
|
+
};
|
|
36
|
+
const seconds = (ms) => `${(ms / 1000).toFixed(1)}s`;
|
|
37
|
+
function phaseText(agents, state, elapsed) {
|
|
38
|
+
const time = elapsed === undefined ? "" : seconds(elapsed);
|
|
39
|
+
if (state === "done") {
|
|
40
|
+
const merged = agents.filter((agent) => agent.status === "done").length;
|
|
41
|
+
return {
|
|
42
|
+
text: `Merged ${merged} ${merged === 1 ? "result" : "results"}${time && ` in ${time}`}`,
|
|
43
|
+
time: "",
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
if (state === "stopped")
|
|
47
|
+
return { text: time ? `Stopped after ${time}` : "Stopped", time: "" };
|
|
48
|
+
const settled = agents.filter((agent) => agent.status === "done" || agent.status === "failed").length;
|
|
49
|
+
const text = agents.every((agent) => agent.status === "queued")
|
|
50
|
+
? "Delegating work"
|
|
51
|
+
: settled === agents.length
|
|
52
|
+
? "Composing answer"
|
|
53
|
+
: settled > 0
|
|
54
|
+
? "Merging results"
|
|
55
|
+
: "Specialists working";
|
|
56
|
+
return { text, time };
|
|
57
|
+
}
|
|
58
|
+
function useWidth() {
|
|
59
|
+
const ref = useRef(null);
|
|
60
|
+
const [width, setWidth] = useState(0);
|
|
61
|
+
useLayoutEffect(() => {
|
|
62
|
+
const element = ref.current;
|
|
63
|
+
if (!element)
|
|
64
|
+
return;
|
|
65
|
+
setWidth(element.clientWidth);
|
|
66
|
+
const observer = new ResizeObserver(() => setWidth(element.clientWidth));
|
|
67
|
+
observer.observe(element);
|
|
68
|
+
return () => observer.disconnect();
|
|
69
|
+
}, []);
|
|
70
|
+
return [ref, width];
|
|
71
|
+
}
|
|
72
|
+
/** Every agent in tree order, with the parent it reports to. */
|
|
73
|
+
function flatten(agents, initial, parent = rootId, depth = 1, color) {
|
|
74
|
+
return agents.flatMap((agent, index) => {
|
|
75
|
+
const own = agent.color ?? color ?? `var(--sprout-chart-${(index % colorCount) + 1})`;
|
|
76
|
+
const changed = initial.get(agent.id) !== agent.status;
|
|
77
|
+
const line = {
|
|
78
|
+
agent,
|
|
79
|
+
parent,
|
|
80
|
+
depth,
|
|
81
|
+
changed: changed || undefined,
|
|
82
|
+
trip: !changed
|
|
83
|
+
? null
|
|
84
|
+
: agent.status === "working"
|
|
85
|
+
? "out"
|
|
86
|
+
: agent.status === "done"
|
|
87
|
+
? "back"
|
|
88
|
+
: null,
|
|
89
|
+
style: {
|
|
90
|
+
"--sprout-agent-fanout-color": own,
|
|
91
|
+
"--sprout-agent-fanout-index": index,
|
|
92
|
+
"--sprout-agent-fanout-depth": depth - 1,
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
return [
|
|
96
|
+
line,
|
|
97
|
+
...flatten(agent.agents ?? [], initial, agent.id, depth + 1, own),
|
|
98
|
+
];
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
const rootId = "agent-fanout-root";
|
|
102
|
+
// Fresh node objects every render drop React Flow's measured handles, which
|
|
103
|
+
// unmounts the edges and restarts their pulses. Declared handles survive.
|
|
104
|
+
const handle = (type, x, y) => ({
|
|
105
|
+
type,
|
|
106
|
+
position: type === "source" ? Position.Bottom : Position.Top,
|
|
107
|
+
x,
|
|
108
|
+
y,
|
|
109
|
+
width: 0,
|
|
110
|
+
height: 0,
|
|
111
|
+
});
|
|
112
|
+
/** Leaves share the width; each parent sits over the middle of its children. */
|
|
113
|
+
function layout(lines, width, state, rootChanged) {
|
|
114
|
+
const children = new Map();
|
|
115
|
+
for (const line of lines)
|
|
116
|
+
children.set(line.parent, [...(children.get(line.parent) ?? []), line]);
|
|
117
|
+
const leaves = lines.filter((line) => !children.has(line.agent.id)).length;
|
|
118
|
+
const column = width / Math.max(leaves, 1);
|
|
119
|
+
const centers = new Map();
|
|
120
|
+
let leaf = 0;
|
|
121
|
+
const place = (id) => {
|
|
122
|
+
const kids = children.get(id);
|
|
123
|
+
const xs = kids?.map((kid) => place(kid.agent.id));
|
|
124
|
+
const x = xs ? (xs[0] + xs.at(-1)) / 2 : (leaf++ + 0.5) * column;
|
|
125
|
+
centers.set(id, x);
|
|
126
|
+
return x;
|
|
127
|
+
};
|
|
128
|
+
place(rootId);
|
|
129
|
+
const depth = Math.max(0, ...lines.map((line) => line.depth));
|
|
130
|
+
const dot = width ? Math.min(40, Math.max(28, column - 12)) : 40;
|
|
131
|
+
const nodes = [
|
|
132
|
+
{
|
|
133
|
+
id: rootId,
|
|
134
|
+
type: "root",
|
|
135
|
+
position: { x: width / 2 - rootSize / 2, y: rootTop },
|
|
136
|
+
width: rootSize,
|
|
137
|
+
height: rootSize,
|
|
138
|
+
handles: [handle("source", rootSize / 2, rootSize)],
|
|
139
|
+
data: { state, changed: rootChanged },
|
|
140
|
+
},
|
|
141
|
+
...lines.map((line) => ({
|
|
142
|
+
id: line.agent.id,
|
|
143
|
+
type: "agent",
|
|
144
|
+
position: {
|
|
145
|
+
x: centers.get(line.agent.id) - column / 2,
|
|
146
|
+
y: levelTop + (line.depth - 1) * levelGap,
|
|
147
|
+
},
|
|
148
|
+
width: column,
|
|
149
|
+
height: dot + nodeLabel,
|
|
150
|
+
handles: [
|
|
151
|
+
handle("target", column / 2, 0),
|
|
152
|
+
handle("source", column / 2, dot),
|
|
153
|
+
],
|
|
154
|
+
data: line,
|
|
155
|
+
})),
|
|
156
|
+
];
|
|
157
|
+
const edges = lines.map((line) => ({
|
|
158
|
+
id: `${line.parent}-${line.agent.id}`,
|
|
159
|
+
type: "trip",
|
|
160
|
+
source: line.parent,
|
|
161
|
+
target: line.agent.id,
|
|
162
|
+
data: { trip: line.trip, style: line.style },
|
|
163
|
+
}));
|
|
164
|
+
return {
|
|
165
|
+
nodes,
|
|
166
|
+
edges,
|
|
167
|
+
dot,
|
|
168
|
+
height: levelTop +
|
|
169
|
+
Math.max(depth - 1, 0) * levelGap +
|
|
170
|
+
dot +
|
|
171
|
+
nodeLabel +
|
|
172
|
+
bottomGap,
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
function RootMark({ data: { state, changed } }) {
|
|
176
|
+
return (_jsxs("span", { "data-slot": "agent-fanout-root", "data-state": state, "data-changed": changed, className: "sprout-agent-fanout-root", children: [state === "done" ? (_jsx(Check, { size: 18 })) : state === "stopped" ? (_jsx(MinusCircle, { size: 18 })) : (_jsx(Sparkle, { size: 18 })), _jsx(Handle, { type: "source", position: Position.Bottom, isConnectable: false, className: "sprout-agent-fanout-handle" })] }));
|
|
177
|
+
}
|
|
178
|
+
function AgentMark({ data: { agent, changed, style } }) {
|
|
179
|
+
return (_jsxs("div", { "data-slot": "agent-fanout-node", "data-status": agent.status, "data-changed": changed, className: "sprout-agent-fanout-node", style: style, children: [_jsxs("span", { className: "sprout-agent-fanout-dot", children: [_jsx(NodeMark, { agent: agent }), _jsx(Handle, { type: "target", position: Position.Top, isConnectable: false, className: "sprout-agent-fanout-handle" }), agent.agents && agent.agents.length > 0 && (_jsx(Handle, { type: "source", position: Position.Bottom, isConnectable: false, className: "sprout-agent-fanout-handle" }))] }), _jsx("span", { className: "sprout-agent-fanout-node-name", children: agent.name })] }));
|
|
180
|
+
}
|
|
181
|
+
function TripLine({ id, sourceX, sourceY, targetX, targetY, data, }) {
|
|
182
|
+
const drop = targetY - sourceY;
|
|
183
|
+
const d = `M ${sourceX} ${sourceY} C ${sourceX} ${sourceY + drop * 0.5}, ${targetX} ${sourceY + drop * 0.2}, ${targetX} ${targetY}`;
|
|
184
|
+
const trip = data?.trip;
|
|
185
|
+
return (_jsxs("g", { style: data?.style, children: [_jsx(BaseEdge, { id: id, path: d, className: "sprout-agent-fanout-edge" }), trip && (_jsxs(_Fragment, { children: [_jsx("path", { d: d, pathLength: 1, "data-trip": trip, className: "sprout-agent-fanout-trail" }, trip), _jsx(EdgeLabelRenderer, { children: _jsx("span", { "data-trip": trip, className: "sprout-agent-fanout-comet", style: { ...data.style, offsetPath: `path("${d}")` } }, trip) })] }))] }));
|
|
186
|
+
}
|
|
187
|
+
const nodeTypes = { root: RootMark, agent: AgentMark };
|
|
188
|
+
const edgeTypes = { trip: TripLine };
|
|
189
|
+
const viewport = { x: 0, y: 0, zoom: 1 };
|
|
190
|
+
function NodeMark({ agent }) {
|
|
191
|
+
if (agent.status === "done")
|
|
192
|
+
return _jsx(Check, { size: 16, "aria-hidden": "true" });
|
|
193
|
+
if (agent.status === "failed")
|
|
194
|
+
return _jsx(Danger, { size: 16, "aria-hidden": "true" });
|
|
195
|
+
return _jsx("span", { "aria-hidden": "true", children: agent.icon });
|
|
196
|
+
}
|
|
197
|
+
function StatusMark({ agent }) {
|
|
198
|
+
if (agent.status === "done")
|
|
199
|
+
return (_jsxs(_Fragment, { children: [_jsx(Check, { size: 12, "aria-hidden": "true" }), agent.duration === undefined ? "Done" : seconds(agent.duration)] }));
|
|
200
|
+
if (agent.status === "working")
|
|
201
|
+
return (_jsxs(_Fragment, { children: [_jsx("span", { "aria-hidden": "true", className: "sprout-agent-fanout-pulse" }), "Working"] }));
|
|
202
|
+
return _jsx(_Fragment, { children: statusText[agent.status] });
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* A parent agent that hands one task to several subagents and merges what
|
|
206
|
+
* they find. Each status change sends a pulse along the subagent's line:
|
|
207
|
+
* down when it starts, back up when it finishes.
|
|
208
|
+
*/
|
|
209
|
+
export function AgentFanout({ task, agents, state = "running", elapsed, label, defaultOpen = false, onStop, onRestart, className, }) {
|
|
210
|
+
const [graphRef, width] = useWidth();
|
|
211
|
+
// Only changes after the first render animate, so a finished fanout
|
|
212
|
+
// renders still instead of replaying every pulse.
|
|
213
|
+
const [initial] = useState(() => ({
|
|
214
|
+
state,
|
|
215
|
+
statuses: new Map(flatten(agents, new Map()).map(({ agent }) => [agent.id, agent.status])),
|
|
216
|
+
}));
|
|
217
|
+
const phase = phaseText(agents, state, elapsed);
|
|
218
|
+
const lines = flatten(agents, initial.statuses);
|
|
219
|
+
const graph = layout(lines, width, state, initial.state !== state || undefined);
|
|
220
|
+
const finished = lines.filter(({ agent }) => agent.status === "done" || agent.status === "failed").length;
|
|
221
|
+
return (_jsxs("section", { "data-slot": "agent-fanout", "data-state": state, "aria-label": "Subagents", className: cn("sprout-agent-fanout", className), children: [_jsxs("div", { className: "sprout-agent-fanout-panel", children: [_jsx("div", { className: "sprout-agent-fanout-task", children: _jsx("span", { className: "sprout-agent-fanout-task-text", children: task }) }), _jsx("div", { ref: graphRef, "aria-hidden": "true", "data-slot": "agent-fanout-graph", className: "sprout-agent-fanout-graph", style: {
|
|
222
|
+
height: graph.height,
|
|
223
|
+
"--sprout-agent-fanout-dot": `${graph.dot}px`,
|
|
224
|
+
}, children: width > 0 && (_jsx(ReactFlow, { className: "sprout-agent-fanout-flow", nodes: graph.nodes, edges: graph.edges, nodeTypes: nodeTypes, edgeTypes: edgeTypes, defaultViewport: viewport, minZoom: 1, maxZoom: 1, panOnDrag: false, panOnScroll: false, zoomOnScroll: false, zoomOnPinch: false, zoomOnDoubleClick: false, preventScrolling: false, nodesDraggable: false, nodesConnectable: false, nodesFocusable: false, edgesFocusable: false, elementsSelectable: false, proOptions: { hideAttribution: true } })) }), _jsxs(Collapsible, { defaultOpen: defaultOpen, className: "sprout-agent-fanout-details", children: [_jsxs(CollapsibleTrigger, { className: "sprout-agent-fanout-trigger", children: [_jsx("span", { className: "sprout-agent-fanout-trigger-label", children: "Details" }), _jsx("span", { className: "sprout-agent-fanout-trigger-count", children: `${finished} of ${lines.length} finished` }), _jsx(ChevronDown, { size: 14, "aria-hidden": "true", "data-slot": "collapsible-indicator" })] }), _jsx(CollapsibleContent, { keepMounted: true, children: _jsx("ul", { "aria-label": "Subagent status", className: "sprout-agent-fanout-rows", children: lines.map(({ agent, changed, style }) => {
|
|
225
|
+
const settled = agent.status === "done" || agent.status === "failed";
|
|
226
|
+
return (_jsxs("li", { "data-slot": "agent-fanout-row", "data-status": agent.status, className: "sprout-agent-fanout-row", style: style, children: [_jsx("span", { className: "sprout-agent-fanout-row-name", children: agent.name }), _jsx("span", { "data-changed": changed, className: "sprout-agent-fanout-row-detail", children: settled && agent.result ? agent.result : agent.task }, settled && agent.result ? "result" : "task"), _jsx("span", { "data-changed": changed, className: "sprout-agent-fanout-row-status", children: _jsx(StatusMark, { agent: agent }) }, agent.status)] }, agent.id));
|
|
227
|
+
}) }) })] })] }), _jsxs("div", { className: "sprout-agent-fanout-footer", children: [_jsxs("p", { className: "sprout-agent-fanout-phase", children: [_jsx("span", { role: "status", children: label ?? phase.text }), !label && phase.time && (_jsx("span", { className: "sprout-agent-fanout-time", children: phase.time }))] }), state === "running" && onStop && (_jsxs(Button, { variant: "outline", size: "sm", onClick: onStop, className: "sprout-agent-fanout-action", children: [_jsx(MinusCircle, { size: 14, "aria-hidden": "true" }), "Stop"] })), state !== "running" && onRestart && (_jsxs(Button, { variant: "outline", size: "sm", onClick: onRestart, className: "sprout-agent-fanout-action", children: [_jsx(RotateLeft, { size: 14, "aria-hidden": "true" }), "Run again"] }))] })] }));
|
|
228
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type ComponentProps } from "react";
|
|
1
|
+
import { type ComponentProps, type ReactNode } from "react";
|
|
2
2
|
export declare const chatConfirmationStateNames: readonly ["pending", "submitting", "approved", "rejected", "expired", "cancelled", "error"];
|
|
3
3
|
export type ChatConfirmationDecision = "approve" | "reject";
|
|
4
4
|
export type ConfirmationState = {
|
|
@@ -33,10 +33,12 @@ export interface ChatConfirmationProps extends Omit<ComponentProps<"div">, "titl
|
|
|
33
33
|
onDecision: (decision: ChatConfirmationDecision) => void;
|
|
34
34
|
/** Locks both buttons, such as while the request is refreshing or cannot be verified. */
|
|
35
35
|
disabled?: boolean | undefined;
|
|
36
|
+
/** Names the action on the approve button, such as “Approve 3 drafts”. */
|
|
37
|
+
approveLabel?: ReactNode;
|
|
36
38
|
}
|
|
37
39
|
/**
|
|
38
40
|
* Asks someone in chat to approve or decline an action an agent proposed.
|
|
39
41
|
* Built on `ReviewCard`: it folds once decided, and when the decision was
|
|
40
42
|
* made from inside the card, focus moves to the outcome.
|
|
41
43
|
*/
|
|
42
|
-
export declare function ChatConfirmation({ title, description, details, state, onDecision, disabled, className, ...props }: ChatConfirmationProps): import("react").JSX.Element;
|
|
44
|
+
export declare function ChatConfirmation({ title, description, details, state, onDecision, disabled, approveLabel, className, ...props }: ChatConfirmationProps): import("react").JSX.Element;
|
|
@@ -41,7 +41,7 @@ const outcomes = {
|
|
|
41
41
|
* Built on `ReviewCard`: it folds once decided, and when the decision was
|
|
42
42
|
* made from inside the card, focus moves to the outcome.
|
|
43
43
|
*/
|
|
44
|
-
export function ChatConfirmation({ title, description, details, state, onDecision, disabled = false, className, ...props }) {
|
|
44
|
+
export function ChatConfirmation({ title, description, details, state, onDecision, disabled = false, approveLabel = "Approve action", className, ...props }) {
|
|
45
45
|
const done = settledKinds.has(state.kind);
|
|
46
46
|
const section = useRef(null);
|
|
47
47
|
const result = useRef(null);
|
|
@@ -58,5 +58,5 @@ export function ChatConfirmation({ title, description, details, state, onDecisio
|
|
|
58
58
|
const submitting = state.kind === "submitting";
|
|
59
59
|
const busy = (decision) => state.kind === "submitting" && state.decision === decision;
|
|
60
60
|
const outcome = outcomes[state.kind];
|
|
61
|
-
return (_jsx("div", { ref: section, "data-slot": "chat-confirmation", "data-state": state.kind, ...props, className: cn("sprout-chat-confirmation", className), children: _jsxs(ReviewCard, { icon: _jsx(ShieldUser, { size: 16 }), title: title, badge: badges[state.kind], settled: done, status: state.kind === "error" || outcome ? (_jsxs(_Fragment, { children: [state.kind === "error" && (_jsx("p", { role: "alert", className: "sprout-chat-confirmation-error", children: state.message })), outcome && (_jsx("p", { ref: result, tabIndex: -1, role: "status", className: "sprout-chat-confirmation-result", children: outcome }))] })) : undefined, actions: done ? undefined : (_jsxs(_Fragment, { children: [_jsxs(Button, { disabled: disabled || submitting, "aria-busy": busy("approve"), onClick: () => onDecision("approve"), children: [busy("approve") && _jsx(Spinner, { "aria-hidden": "true" }),
|
|
61
|
+
return (_jsx("div", { ref: section, "data-slot": "chat-confirmation", "data-state": state.kind, ...props, className: cn("sprout-chat-confirmation", className), children: _jsxs(ReviewCard, { icon: _jsx(ShieldUser, { size: 16 }), title: title, badge: badges[state.kind], settled: done, status: state.kind === "error" || outcome ? (_jsxs(_Fragment, { children: [state.kind === "error" && (_jsx("p", { role: "alert", className: "sprout-chat-confirmation-error", children: state.message })), outcome && (_jsx("p", { ref: result, tabIndex: -1, role: "status", className: "sprout-chat-confirmation-result", children: outcome }))] })) : undefined, actions: done ? undefined : (_jsxs(_Fragment, { children: [_jsxs(Button, { disabled: disabled || submitting, "aria-busy": busy("approve"), onClick: () => onDecision("approve"), children: [busy("approve") && _jsx(Spinner, { "aria-hidden": "true" }), approveLabel] }), _jsxs(Button, { variant: "ghost", disabled: disabled || submitting, "aria-busy": busy("reject"), onClick: () => onDecision("reject"), children: [busy("reject") && _jsx(Spinner, { "aria-hidden": "true" }), "Decline"] })] })), children: [_jsx("p", { className: "sprout-chat-confirmation-description", children: description }), _jsx(ReviewRows, { children: details.map((detail) => (_jsx(ReviewRow, { label: detail.label, children: detail.value }, detail.label))) })] }) }));
|
|
62
62
|
}
|
|
@@ -53,9 +53,18 @@ export const citationStateKindNames = [
|
|
|
53
53
|
*/
|
|
54
54
|
export function CheckedCitation({ label, state, onOpen, onRetry, }) {
|
|
55
55
|
const popup = useRef(null);
|
|
56
|
-
|
|
57
|
-
|
|
56
|
+
const openReason = useRef(undefined);
|
|
57
|
+
return (_jsxs(Popover, { onOpenChange: (open, details) => {
|
|
58
|
+
if (open) {
|
|
59
|
+
openReason.current = details.reason;
|
|
58
60
|
onOpen();
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
// Base UI keeps a hover-close timer from before a reopen; a passage
|
|
64
|
+
// opened by click or keyboard closes only by click, Escape, or outside press.
|
|
65
|
+
if (details.reason === "trigger-hover" &&
|
|
66
|
+
openReason.current === "trigger-press")
|
|
67
|
+
details.cancel();
|
|
59
68
|
}, children: [_jsx(PopoverTrigger, { openOnHover: true, delay: hoverDelay, closeDelay: hoverCloseDelay, "data-slot": "checked-citation", className: "sprout-chat-sources-checked", render: _jsx(Button, { variant: "link", size: "task-link", "aria-label": `Source: ${label}` }), children: label }), _jsx(PopoverContent, { ref: popup, "data-citation-state": state.kind, className: "sprout-chat-sources-preview", children: state.kind === "idle" || state.kind === "loading" ? (_jsxs("p", { role: "status", className: "sprout-chat-sources-checking", children: [_jsx(Spinner, { "aria-hidden": "true" }), "Checking access to this source\u2026"] })) : state.kind === "ready" ? (_jsxs("div", { "data-slot": "chat-source", className: "sprout-chat-sources-source", children: [_jsx("p", { className: "sprout-chat-sources-title", children: state.title }), _jsx("p", { className: "sprout-chat-sources-location", children: state.location }), _jsx("p", { className: "sprout-chat-sources-excerpt", "data-preserve-lines": "", children: state.excerpt })] })) : (_jsxs("div", { className: "sprout-chat-sources-source", children: [_jsx("p", { className: "sprout-chat-sources-title", children: state.kind === "unavailable"
|
|
60
69
|
? "Source unavailable"
|
|
61
70
|
: "Couldn’t load this source" }), _jsx("p", { role: "alert", className: "sprout-chat-sources-excerpt", children: state.message }), state.kind === "error" && (_jsx(Button, { variant: "outline", size: "sm", className: "sprout-chat-sources-retry", onClick: () => {
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ComponentProps, ReactNode } from "react";
|
|
2
|
+
export interface PresenceCursorProps extends Omit<ComponentProps<"div">, "children" | "color"> {
|
|
3
|
+
/** The person's name or email. Shown in the tag, or read out with an avatar. */
|
|
4
|
+
name: string;
|
|
5
|
+
/** Left offset of the arrow tip in px, from the positioned parent. */
|
|
6
|
+
x: number;
|
|
7
|
+
/** Top offset of the arrow tip in px, from the positioned parent. */
|
|
8
|
+
y: number;
|
|
9
|
+
/** Any CSS color. White text sits on it, so keep 4.5:1 contrast. */
|
|
10
|
+
color: string;
|
|
11
|
+
/** Shows this, such as an `Avatar`, next to the arrow instead of the name tag. */
|
|
12
|
+
avatar?: ReactNode;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Another person's pointer on a shared surface, with a name tag or an avatar.
|
|
16
|
+
* Put it in a parent with `position: relative`. It ignores pointer events and
|
|
17
|
+
* glides between positions unless reduced motion is requested.
|
|
18
|
+
*/
|
|
19
|
+
export declare function PresenceCursor({ name, x, y, color, avatar, className, style, ...props }: PresenceCursorProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from "../internal/classes.js";
|
|
3
|
+
/**
|
|
4
|
+
* Another person's pointer on a shared surface, with a name tag or an avatar.
|
|
5
|
+
* Put it in a parent with `position: relative`. It ignores pointer events and
|
|
6
|
+
* glides between positions unless reduced motion is requested.
|
|
7
|
+
*/
|
|
8
|
+
export function PresenceCursor({ name, x, y, color, avatar, className, style, ...props }) {
|
|
9
|
+
return (_jsxs("div", { "data-slot": "presence-cursor", role: "img", "aria-label": name, className: cn("sprout-presence-cursor", className), style: {
|
|
10
|
+
"--sprout-presence-color": color,
|
|
11
|
+
transform: `translate(${x}px, ${y}px)`,
|
|
12
|
+
...style,
|
|
13
|
+
}, ...props, children: [_jsx("svg", { className: "sprout-presence-cursor-arrow", viewBox: "0 0 16 16", "aria-hidden": "true", children: _jsx("path", { d: "M1.5 1.5 14.5 6.6 8.4 8.4 6.6 14.5Z" }) }), avatar != null ? (_jsx("span", { "data-slot": "presence-cursor-avatar", className: "sprout-presence-cursor-avatar", "aria-hidden": "true", children: avatar })) : (_jsx("span", { "data-slot": "presence-cursor-label", className: "sprout-presence-cursor-label", "aria-hidden": "true", children: name }))] }));
|
|
14
|
+
}
|
|
@@ -10,10 +10,15 @@ export type ResultAppAction = {
|
|
|
10
10
|
label: string;
|
|
11
11
|
state: ResultAppActionState;
|
|
12
12
|
};
|
|
13
|
-
/**
|
|
13
|
+
/**
|
|
14
|
+
* One app that took part in a result. `brand: null` shows `icon`, or a
|
|
15
|
+
* generic plug when no icon is given.
|
|
16
|
+
*/
|
|
14
17
|
export type ResultApp = {
|
|
15
18
|
brand: Brand | null;
|
|
16
19
|
name: string;
|
|
20
|
+
/** Shown when `brand` is null, such as a table icon for your own data. */
|
|
21
|
+
icon?: ReactNode;
|
|
17
22
|
actions: readonly ResultAppAction[];
|
|
18
23
|
};
|
|
19
24
|
export type ResultCardAction = {
|
|
@@ -77,12 +77,12 @@ function AppActionsPopover({ apps, overflow = false, disabled, }) {
|
|
|
77
77
|
const title = overflow
|
|
78
78
|
? `${apps.length} more ${apps.length === 1 ? "app" : "apps"}`
|
|
79
79
|
: `${first.name} actions`;
|
|
80
|
-
return (_jsxs(Popover, { children: [_jsx(PopoverTrigger, { openOnHover: true, delay: 800, closeDelay: 200, render: _jsx(BrandTile, { size: "action", render: _jsx(Button, { variant: "ghost", size: "icon", disabled: disabled }) }), "aria-label": title, children: overflow ? (_jsxs("span", { className: "sprout-result-card-overflow", children: ["+", apps.length] })) : first.brand ? (_jsx(BrandIcon, { brand: first.brand, size: "fill" })) : (_jsx(Plug, { size: 18, "aria-hidden": "true" })) }), _jsxs(PopoverContent, { size: "app-actions", align: "start", sideOffset: 8, initialFocus: (interaction) => interaction !== "mouse", children: [_jsx(PopoverTitle, { children: title }), _jsx(ResultAppActions, { apps: apps, showAppNames: overflow })] })] }));
|
|
80
|
+
return (_jsxs(Popover, { children: [_jsx(PopoverTrigger, { openOnHover: true, delay: 800, closeDelay: 200, render: _jsx(BrandTile, { size: "action", render: _jsx(Button, { variant: "ghost", size: "icon", disabled: disabled }) }), "aria-label": title, children: overflow ? (_jsxs("span", { className: "sprout-result-card-overflow", children: ["+", apps.length] })) : first.brand ? (_jsx(BrandIcon, { brand: first.brand, size: "fill" })) : first.icon ? (_jsx("span", { "aria-hidden": "true", className: "sprout-result-app-icon", children: first.icon })) : (_jsx(Plug, { size: 18, "aria-hidden": "true" })) }), _jsxs(PopoverContent, { size: "app-actions", align: "start", sideOffset: 8, initialFocus: (interaction) => interaction !== "mouse", children: [_jsx(PopoverTitle, { children: title }), _jsx(ResultAppActions, { apps: apps, showAppNames: overflow })] })] }));
|
|
81
81
|
}
|
|
82
82
|
/**
|
|
83
83
|
* The actions each app took, grouped by app. A check marks completed
|
|
84
84
|
* actions; every other state is spelled out, such as “Unconfirmed:”.
|
|
85
85
|
*/
|
|
86
86
|
export function ResultAppActions({ apps, showAppNames = true, className, ...props }) {
|
|
87
|
-
return (_jsx("div", { "data-slot": "result-app-actions", ...props, className: cn("sprout-result-app-actions", className), children: apps.map((app) => (_jsxs("section", { "aria-label": app.name, className: "sprout-result-app", children: [showAppNames && (_jsxs("div", { className: "sprout-result-app-name", children: [app.brand ? (_jsx(BrandIcon, { brand: app.brand, size: "glyph" })) : (_jsx(Plug, { size: 20, "aria-hidden": "true" })), _jsx("h4", { children: app.name })] })), app.actions.length === 0 ? (_jsx("p", { className: "sprout-result-app-empty", children: "No action details were recorded." })) : (_jsx("ul", { className: "sprout-result-app-list", children: app.actions.map((action, index) => (_jsxs("li", { "data-state": action.state, className: "sprout-result-app-action", children: [action.state === "completed" ? (_jsxs("span", { className: "sprout-result-app-check", children: [_jsx(Check, { size: 16, "aria-hidden": "true" }), _jsx("span", { className: "sprout-result-sr-only", children: "Completed:" })] })) : (_jsx("span", { className: "sprout-result-app-prefix", children: actionPrefixes[action.state] })), _jsx("span", { className: "sprout-result-app-label", children: action.label })] }, `${action.label}:${index}`))) }))] }, app.brand ?? app.name))) }));
|
|
87
|
+
return (_jsx("div", { "data-slot": "result-app-actions", ...props, className: cn("sprout-result-app-actions", className), children: apps.map((app) => (_jsxs("section", { "aria-label": app.name, className: "sprout-result-app", children: [showAppNames && (_jsxs("div", { className: "sprout-result-app-name", children: [app.brand ? (_jsx(BrandIcon, { brand: app.brand, size: "glyph" })) : app.icon ? (_jsx("span", { "aria-hidden": "true", className: "sprout-result-app-icon", children: app.icon })) : (_jsx(Plug, { size: 20, "aria-hidden": "true" })), _jsx("h4", { children: app.name })] })), app.actions.length === 0 ? (_jsx("p", { className: "sprout-result-app-empty", children: "No action details were recorded." })) : (_jsx("ul", { className: "sprout-result-app-list", children: app.actions.map((action, index) => (_jsxs("li", { "data-state": action.state, className: "sprout-result-app-action", children: [action.state === "completed" ? (_jsxs("span", { className: "sprout-result-app-check", children: [_jsx(Check, { size: 16, "aria-hidden": "true" }), _jsx("span", { className: "sprout-result-sr-only", children: "Completed:" })] })) : (_jsx("span", { className: "sprout-result-app-prefix", children: actionPrefixes[action.state] })), _jsx("span", { className: "sprout-result-app-label", children: action.label })] }, `${action.label}:${index}`))) }))] }, app.brand ?? app.name))) }));
|
|
88
88
|
}
|