@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.
@@ -0,0 +1,22 @@
1
+ import { type FitViewOptions, type Node } from "@xyflow/react";
2
+ import { type KeyboardEvent } from "react";
3
+ export declare const flowMinZoom = 0.2;
4
+ export declare const flowMaxZoom = 1.5;
5
+ export declare function useViewportMobile(): boolean;
6
+ export declare const motion: (ms: number) => number;
7
+ /**
8
+ * Refits whenever the pane resizes or the nodes change. The canvas panel
9
+ * animates open, so the first fit sees a narrower pane than the final one.
10
+ */
11
+ export declare function FitOnResize({ nodes, fit, }: {
12
+ nodes: readonly Node[];
13
+ fit: FitViewOptions;
14
+ }): null;
15
+ export declare function ZoomControls({ position, fit, className, slot, }: {
16
+ position: "top-right" | "bottom-right";
17
+ fit: FitViewOptions;
18
+ className: string;
19
+ slot: string;
20
+ }): import("react").JSX.Element;
21
+ /** Arrow keys pan, plus and minus zoom, and 0 fits, from the pane or a node. */
22
+ export declare function useKeyboardView(fit: FitViewOptions): (event: KeyboardEvent<HTMLDivElement>) => void;
@@ -0,0 +1,82 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { Panel, useNodesInitialized, useReactFlow, useStore, } from "@xyflow/react";
4
+ import { useEffect, useSyncExternalStore } from "react";
5
+ import { Add } from "reicon-react/icons/Add";
6
+ import { Minus } from "reicon-react/icons/Minus";
7
+ import { Scan } from "reicon-react/icons/Scan";
8
+ import { Button } from "../components/button.js";
9
+ export const flowMinZoom = 0.2;
10
+ export const flowMaxZoom = 1.5;
11
+ const panStep = 48;
12
+ const mobileQuery = "(max-width: 767px)";
13
+ const motionQuery = "(prefers-reduced-motion: reduce)";
14
+ function subscribeMobile(callback) {
15
+ const media = window.matchMedia(mobileQuery);
16
+ media.addEventListener("change", callback);
17
+ return () => media.removeEventListener("change", callback);
18
+ }
19
+ export function useViewportMobile() {
20
+ return useSyncExternalStore(subscribeMobile, () => window.matchMedia(mobileQuery).matches, () => false);
21
+ }
22
+ export const motion = (ms) => typeof window !== "undefined" && window.matchMedia(motionQuery).matches
23
+ ? 0
24
+ : ms;
25
+ /**
26
+ * Refits whenever the pane resizes or the nodes change. The canvas panel
27
+ * animates open, so the first fit sees a narrower pane than the final one.
28
+ */
29
+ export function FitOnResize({ nodes, fit, }) {
30
+ const { fitView } = useReactFlow();
31
+ const width = useStore((state) => state.width);
32
+ const height = useStore((state) => state.height);
33
+ const measured = useNodesInitialized();
34
+ useEffect(() => {
35
+ if (!measured)
36
+ return;
37
+ const frame = requestAnimationFrame(() => void fitView(fit));
38
+ return () => cancelAnimationFrame(frame);
39
+ }, [fitView, fit, width, height, measured, nodes]);
40
+ return null;
41
+ }
42
+ export function ZoomControls({ position, fit, className, slot, }) {
43
+ const { zoomIn, zoomOut, fitView } = useReactFlow();
44
+ const zoom = useStore((state) => state.transform[2]);
45
+ return (_jsxs(Panel, { position: position, className: className, "data-slot": slot, role: "toolbar", "aria-label": "Diagram view", "aria-orientation": "vertical", children: [_jsx(Button, { variant: "ghost", size: "icon-sm", "aria-label": "Zoom in", title: "Zoom in", disabled: zoom >= flowMaxZoom, onClick: () => void zoomIn({ duration: motion(160) }), children: _jsx(Add, { size: 16, "aria-hidden": "true" }) }), _jsx(Button, { variant: "ghost", size: "icon-sm", "aria-label": "Zoom out", title: "Zoom out", disabled: zoom <= flowMinZoom, onClick: () => void zoomOut({ duration: motion(160) }), children: _jsx(Minus, { size: 16, "aria-hidden": "true" }) }), _jsx(Button, { variant: "ghost", size: "icon-sm", "aria-label": "Fit to view", title: "Fit to view", onClick: () => void fitView({ ...fit, duration: motion(180) }), children: _jsx(Scan, { size: 16, "aria-hidden": "true" }) })] }));
46
+ }
47
+ /** Arrow keys pan, plus and minus zoom, and 0 fits, from the pane or a node. */
48
+ export function useKeyboardView(fit) {
49
+ const { getViewport, setViewport, zoomIn, zoomOut, fitView } = useReactFlow();
50
+ return (event) => {
51
+ const target = event.target;
52
+ if (event.altKey || event.ctrlKey || event.metaKey)
53
+ return;
54
+ if (target !== event.currentTarget &&
55
+ !target.classList.contains("react-flow__node"))
56
+ return;
57
+ const step = panStep * (event.shiftKey ? 4 : 1);
58
+ const pan = {
59
+ ArrowLeft: [step, 0],
60
+ ArrowRight: [-step, 0],
61
+ ArrowUp: [0, step],
62
+ ArrowDown: [0, -step],
63
+ };
64
+ const move = pan[event.key];
65
+ if (move) {
66
+ const { x, y, zoom } = getViewport();
67
+ void setViewport({ x: x + move[0], y: y + move[1], zoom }, { duration: motion(120) });
68
+ }
69
+ else if (event.key === "+" || event.key === "=") {
70
+ void zoomIn({ duration: motion(160) });
71
+ }
72
+ else if (event.key === "-" || event.key === "_") {
73
+ void zoomOut({ duration: motion(160) });
74
+ }
75
+ else if (event.key === "0") {
76
+ void fitView({ ...fit, duration: motion(180) });
77
+ }
78
+ else
79
+ return;
80
+ event.preventDefault();
81
+ };
82
+ }
@@ -0,0 +1,529 @@
1
+ @layer base {
2
+ /* React Flow puts .light or .dark on its root. Base outranks components, so
3
+ this lives here to keep the page's tokens inside the graph. */
4
+ .sprout-agent-fanout-flow.light,
5
+ .sprout-agent-fanout-flow.dark {
6
+ --sprout-surface: inherit;
7
+ --sprout-hover: inherit;
8
+ --sprout-text: inherit;
9
+ --sprout-muted: inherit;
10
+ --sprout-line: inherit;
11
+ --sprout-border: inherit;
12
+ --sprout-primary: inherit;
13
+ --sprout-on-primary: inherit;
14
+ --sprout-danger: inherit;
15
+ --sprout-danger-subtle: inherit;
16
+ --sprout-chart-1: inherit;
17
+ --sprout-chart-2: inherit;
18
+ --sprout-chart-3: inherit;
19
+ --sprout-chart-4: inherit;
20
+ --sprout-chart-5: inherit;
21
+ }
22
+ }
23
+
24
+ @layer components {
25
+ .sprout-agent-fanout {
26
+ --sprout-agent-fanout-trip: 720ms;
27
+ --sprout-agent-fanout-trip-ease: cubic-bezier(0.5, 0, 0.2, 1);
28
+ container-type: inline-size;
29
+ box-sizing: border-box;
30
+ width: 100%;
31
+ min-width: 0;
32
+ overflow: hidden;
33
+ border: 1px solid var(--sprout-line);
34
+ border-radius: var(--sprout-radius-panel);
35
+ background: var(--sprout-surface);
36
+ color: var(--sprout-text);
37
+ font-family: var(--sprout-font);
38
+ }
39
+
40
+ .sprout-agent-fanout-task {
41
+ display: flex;
42
+ min-width: 0;
43
+ padding: 12px 16px;
44
+ border-bottom: 1px solid var(--sprout-line);
45
+ }
46
+ .sprout-agent-fanout-task-text {
47
+ min-width: 0;
48
+ font-size: var(--sprout-text-sm);
49
+ font-weight: 500;
50
+ line-height: 1.25rem;
51
+ overflow-wrap: anywhere;
52
+ }
53
+
54
+ .sprout-agent-fanout-graph {
55
+ position: relative;
56
+ }
57
+
58
+ /* The structural rules React Flow needs, scoped to the graph. */
59
+ .sprout-agent-fanout-flow {
60
+ position: relative;
61
+ direction: ltr;
62
+ width: 100%;
63
+ height: 100%;
64
+ overflow: hidden;
65
+ pointer-events: none;
66
+ }
67
+ .sprout-agent-fanout-flow .react-flow__container,
68
+ .sprout-agent-fanout-flow .react-flow__edgelabel-renderer {
69
+ position: absolute;
70
+ top: 0;
71
+ left: 0;
72
+ width: 100%;
73
+ height: 100%;
74
+ }
75
+ .sprout-agent-fanout-flow .react-flow__viewport,
76
+ .sprout-agent-fanout-flow .react-flow__nodes,
77
+ .sprout-agent-fanout-flow .react-flow__node {
78
+ transform-origin: 0 0;
79
+ }
80
+ .sprout-agent-fanout-flow .react-flow__edges svg {
81
+ position: absolute;
82
+ overflow: visible;
83
+ fill: none;
84
+ }
85
+ .sprout-agent-fanout-flow .react-flow__node {
86
+ position: absolute;
87
+ box-sizing: border-box;
88
+ }
89
+ .sprout-agent-fanout-handle.react-flow__handle {
90
+ position: absolute;
91
+ left: 50%;
92
+ width: 1px;
93
+ height: 1px;
94
+ min-width: 0;
95
+ min-height: 0;
96
+ border: 0;
97
+ opacity: 0;
98
+ translate: -50% 0;
99
+ }
100
+ .sprout-agent-fanout-handle.react-flow__handle-top {
101
+ top: 0;
102
+ }
103
+ .sprout-agent-fanout-handle.react-flow__handle-bottom {
104
+ bottom: 0;
105
+ }
106
+
107
+ .sprout-agent-fanout-edge {
108
+ stroke: var(--sprout-line);
109
+ stroke-width: 1;
110
+ }
111
+ .sprout-agent-fanout-trail {
112
+ stroke: var(--sprout-agent-fanout-color);
113
+ stroke-width: 2.5;
114
+ stroke-linecap: round;
115
+ stroke-dasharray: 0.3 2;
116
+ stroke-dashoffset: 0.3;
117
+ opacity: 0;
118
+ }
119
+ .sprout-agent-fanout-comet {
120
+ position: absolute;
121
+ top: 0;
122
+ left: 0;
123
+ width: 8px;
124
+ height: 8px;
125
+ border-radius: 50%;
126
+ background: var(--sprout-agent-fanout-color);
127
+ offset-rotate: 0deg;
128
+ opacity: 0;
129
+ pointer-events: none;
130
+ }
131
+
132
+ .sprout-agent-fanout-root {
133
+ position: relative;
134
+ display: grid;
135
+ place-items: center;
136
+ width: 44px;
137
+ height: 44px;
138
+ border-radius: 50%;
139
+ background: var(--sprout-primary);
140
+ color: var(--sprout-on-primary);
141
+ }
142
+ .sprout-agent-fanout-root[data-state="stopped"] {
143
+ background: var(--sprout-hover);
144
+ color: var(--sprout-muted);
145
+ box-shadow: inset 0 0 0 1px var(--sprout-line);
146
+ }
147
+
148
+ .sprout-agent-fanout-node {
149
+ display: flex;
150
+ flex-direction: column;
151
+ align-items: center;
152
+ gap: 6px;
153
+ width: 100%;
154
+ min-width: 0;
155
+ }
156
+ .sprout-agent-fanout-dot {
157
+ position: relative;
158
+ display: grid;
159
+ place-items: center;
160
+ box-sizing: border-box;
161
+ width: var(--sprout-agent-fanout-dot);
162
+ height: var(--sprout-agent-fanout-dot);
163
+ border: 1px solid var(--sprout-line);
164
+ border-radius: 50%;
165
+ background: var(--sprout-surface);
166
+ color: var(--sprout-muted);
167
+ }
168
+ .sprout-agent-fanout-dot > span {
169
+ display: contents;
170
+ }
171
+ .sprout-agent-fanout-dot::before {
172
+ position: absolute;
173
+ z-index: -1;
174
+ inset: calc(var(--sprout-agent-fanout-dot) * -0.27);
175
+ border-radius: 50%;
176
+ background: color-mix(
177
+ in oklab,
178
+ var(--sprout-agent-fanout-color) 14%,
179
+ transparent
180
+ );
181
+ content: "";
182
+ opacity: 0;
183
+ }
184
+ .sprout-agent-fanout-node {
185
+ isolation: isolate;
186
+ }
187
+ .sprout-agent-fanout-node[data-status="queued"] .sprout-agent-fanout-dot {
188
+ color: color-mix(in oklab, var(--sprout-muted) 70%, var(--sprout-surface));
189
+ }
190
+ .sprout-agent-fanout-node[data-status="working"] .sprout-agent-fanout-dot {
191
+ border-color: var(--sprout-agent-fanout-color);
192
+ background: color-mix(
193
+ in oklab,
194
+ var(--sprout-agent-fanout-color) 6%,
195
+ var(--sprout-surface)
196
+ );
197
+ color: color-mix(
198
+ in oklab,
199
+ var(--sprout-agent-fanout-color) 78%,
200
+ var(--sprout-text)
201
+ );
202
+ }
203
+ .sprout-agent-fanout-node[data-status="working"]
204
+ .sprout-agent-fanout-dot::before {
205
+ opacity: 1;
206
+ }
207
+ .sprout-agent-fanout-node[data-status="done"] .sprout-agent-fanout-dot {
208
+ border-color: var(--sprout-agent-fanout-color);
209
+ background: var(--sprout-agent-fanout-color);
210
+ color: var(--sprout-surface);
211
+ }
212
+ .sprout-agent-fanout-node[data-status="failed"] .sprout-agent-fanout-dot {
213
+ border-color: var(--sprout-danger);
214
+ background: var(--sprout-danger-subtle);
215
+ color: var(--sprout-danger);
216
+ }
217
+ .sprout-agent-fanout-node[data-status="stopped"] .sprout-agent-fanout-dot {
218
+ border-color: var(--sprout-border);
219
+ opacity: 0.6;
220
+ }
221
+ .sprout-agent-fanout-node-name {
222
+ max-width: calc(100% - 8px);
223
+ overflow: hidden;
224
+ color: var(--sprout-muted);
225
+ font-size: var(--sprout-text-xs);
226
+ line-height: 1rem;
227
+ text-overflow: ellipsis;
228
+ white-space: nowrap;
229
+ }
230
+
231
+ .sprout-agent-fanout-details {
232
+ border-top: 1px solid var(--sprout-line);
233
+ }
234
+ .sprout-agent-fanout-trigger {
235
+ display: flex;
236
+ align-items: center;
237
+ gap: 8px;
238
+ box-sizing: border-box;
239
+ width: 100%;
240
+ min-height: 40px;
241
+ padding: 0 16px;
242
+ border: 0;
243
+ background: transparent;
244
+ color: var(--sprout-muted);
245
+ font: inherit;
246
+ font-size: var(--sprout-text-sm);
247
+ text-align: start;
248
+ cursor: pointer;
249
+ }
250
+ .sprout-agent-fanout-trigger:hover {
251
+ background: var(--sprout-hover);
252
+ color: var(--sprout-text);
253
+ }
254
+ .sprout-agent-fanout-trigger:focus-visible {
255
+ outline: var(--sprout-focus-width) solid var(--sprout-focus-ring);
256
+ outline-offset: -2px;
257
+ }
258
+ .sprout-agent-fanout-trigger-label {
259
+ font-weight: 500;
260
+ }
261
+ .sprout-agent-fanout-trigger-count {
262
+ flex: 1;
263
+ font-size: var(--sprout-text-xs);
264
+ font-variant-numeric: tabular-nums;
265
+ }
266
+
267
+ .sprout-agent-fanout-rows {
268
+ margin: 0;
269
+ padding: 0;
270
+ list-style: none;
271
+ }
272
+ .sprout-agent-fanout-row {
273
+ display: grid;
274
+ grid-template-columns: minmax(0, 8rem) minmax(0, 1fr) auto;
275
+ align-items: baseline;
276
+ gap: 12px;
277
+ padding: 11px 16px;
278
+ border-top: 1px solid var(--sprout-line);
279
+ font-size: var(--sprout-text-sm);
280
+ line-height: 1.25rem;
281
+ }
282
+ .sprout-agent-fanout-row-name {
283
+ padding-inline-start: calc(var(--sprout-agent-fanout-depth, 0) * 12px);
284
+ overflow: hidden;
285
+ font-weight: 500;
286
+ text-overflow: ellipsis;
287
+ white-space: nowrap;
288
+ }
289
+ .sprout-agent-fanout-row-detail {
290
+ overflow: hidden;
291
+ color: var(--sprout-muted);
292
+ text-overflow: ellipsis;
293
+ white-space: nowrap;
294
+ }
295
+ .sprout-agent-fanout-row-status {
296
+ display: inline-flex;
297
+ align-items: center;
298
+ gap: 6px;
299
+ color: var(--sprout-muted);
300
+ font-size: var(--sprout-text-xs);
301
+ font-variant-numeric: tabular-nums;
302
+ white-space: nowrap;
303
+ }
304
+ .sprout-agent-fanout-row[data-status="working"]
305
+ .sprout-agent-fanout-row-status {
306
+ color: color-mix(
307
+ in oklab,
308
+ var(--sprout-agent-fanout-color) 72%,
309
+ var(--sprout-text)
310
+ );
311
+ }
312
+ .sprout-agent-fanout-row[data-status="done"] .sprout-agent-fanout-row-status {
313
+ color: var(--sprout-text);
314
+ }
315
+ .sprout-agent-fanout-row[data-status="failed"]
316
+ .sprout-agent-fanout-row-status {
317
+ color: var(--sprout-danger);
318
+ }
319
+ .sprout-agent-fanout-pulse {
320
+ width: 6px;
321
+ height: 6px;
322
+ border-radius: 50%;
323
+ background: var(--sprout-agent-fanout-color);
324
+ }
325
+
326
+ .sprout-agent-fanout-footer {
327
+ display: flex;
328
+ align-items: center;
329
+ justify-content: space-between;
330
+ gap: 12px;
331
+ padding: 8px 8px 8px 16px;
332
+ border-top: 1px solid var(--sprout-line);
333
+ background: color-mix(in oklab, var(--sprout-hover) 50%, transparent);
334
+ }
335
+ .sprout-agent-fanout-phase {
336
+ display: flex;
337
+ align-items: baseline;
338
+ gap: 8px;
339
+ min-width: 0;
340
+ margin: 0;
341
+ color: var(--sprout-muted);
342
+ font-size: var(--sprout-text-sm);
343
+ line-height: 1.25rem;
344
+ }
345
+ .sprout-agent-fanout-time {
346
+ font-size: var(--sprout-text-xs);
347
+ font-variant-numeric: tabular-nums;
348
+ }
349
+ .sprout-agent-fanout-action {
350
+ flex-shrink: 0;
351
+ }
352
+
353
+ @container (max-width: 420px) {
354
+ .sprout-agent-fanout-row {
355
+ grid-template-columns: minmax(0, 5.5rem) minmax(0, 1fr) auto;
356
+ gap: 8px;
357
+ padding-inline: 12px;
358
+ }
359
+ .sprout-agent-fanout-task,
360
+ .sprout-agent-fanout-trigger {
361
+ padding-inline: 12px;
362
+ }
363
+ }
364
+
365
+ @media (prefers-reduced-motion: no-preference) {
366
+ .sprout-agent-fanout-trail[data-trip="out"] {
367
+ animation: sprout-agent-fanout-out var(--sprout-agent-fanout-trip)
368
+ var(--sprout-agent-fanout-trip-ease) both;
369
+ }
370
+ .sprout-agent-fanout-trail[data-trip="back"] {
371
+ animation: sprout-agent-fanout-back var(--sprout-agent-fanout-trip)
372
+ var(--sprout-agent-fanout-trip-ease) both;
373
+ }
374
+ .sprout-agent-fanout-comet[data-trip="out"] {
375
+ animation: sprout-agent-fanout-comet-out var(--sprout-agent-fanout-trip)
376
+ var(--sprout-agent-fanout-trip-ease) both;
377
+ }
378
+ .sprout-agent-fanout-comet[data-trip="back"] {
379
+ animation: sprout-agent-fanout-comet-back var(--sprout-agent-fanout-trip)
380
+ var(--sprout-agent-fanout-trip-ease) both;
381
+ }
382
+
383
+ .sprout-agent-fanout-dot,
384
+ .sprout-agent-fanout-dot::before {
385
+ transition:
386
+ background-color 240ms var(--sprout-motion-ease),
387
+ border-color 240ms var(--sprout-motion-ease),
388
+ color 240ms var(--sprout-motion-ease),
389
+ opacity 240ms var(--sprout-motion-ease);
390
+ }
391
+ /* The node lights up when the pulse lands, not when it leaves. */
392
+ .sprout-agent-fanout-node[data-status="working"][data-changed]
393
+ .sprout-agent-fanout-dot,
394
+ .sprout-agent-fanout-node[data-status="working"][data-changed]
395
+ .sprout-agent-fanout-dot::before {
396
+ transition-delay: calc(var(--sprout-agent-fanout-trip) * 0.85);
397
+ }
398
+ .sprout-agent-fanout-node[data-status="working"]
399
+ .sprout-agent-fanout-dot::before {
400
+ animation: sprout-agent-fanout-breathe 1.6s ease-in-out infinite alternate;
401
+ animation-delay: calc(var(--sprout-agent-fanout-index) * -0.55s);
402
+ }
403
+ .sprout-agent-fanout-node[data-status="done"][data-changed]
404
+ .sprout-agent-fanout-dot {
405
+ animation: sprout-agent-fanout-pop 420ms var(--sprout-motion-ease);
406
+ }
407
+ .sprout-agent-fanout-root[data-changed] {
408
+ animation: sprout-agent-fanout-ring 900ms var(--sprout-motion-ease);
409
+ }
410
+ .sprout-agent-fanout-root[data-changed] > svg {
411
+ animation: sprout-agent-fanout-pop 420ms var(--sprout-motion-ease);
412
+ }
413
+ .sprout-agent-fanout-pulse {
414
+ animation: sprout-agent-fanout-pulse 1.4s ease-out infinite;
415
+ }
416
+ .sprout-agent-fanout-row-detail[data-changed],
417
+ .sprout-agent-fanout-row-status[data-changed] {
418
+ animation: sprout-agent-fanout-enter 320ms var(--sprout-motion-ease) both;
419
+ }
420
+ }
421
+
422
+ @media (forced-colors: active) {
423
+ .sprout-agent-fanout-edge,
424
+ .sprout-agent-fanout-trail {
425
+ stroke: CanvasText;
426
+ }
427
+ .sprout-agent-fanout-dot,
428
+ .sprout-agent-fanout-root {
429
+ border: 1px solid CanvasText;
430
+ }
431
+ }
432
+ }
433
+
434
+ @keyframes sprout-agent-fanout-out {
435
+ 0% {
436
+ stroke-dashoffset: 0.3;
437
+ opacity: 1;
438
+ }
439
+ 80% {
440
+ opacity: 1;
441
+ }
442
+ 100% {
443
+ stroke-dashoffset: -0.7;
444
+ opacity: 0;
445
+ }
446
+ }
447
+ @keyframes sprout-agent-fanout-back {
448
+ 0% {
449
+ stroke-dashoffset: -1;
450
+ opacity: 1;
451
+ }
452
+ 80% {
453
+ opacity: 1;
454
+ }
455
+ 100% {
456
+ stroke-dashoffset: 0;
457
+ opacity: 0;
458
+ }
459
+ }
460
+ @keyframes sprout-agent-fanout-comet-out {
461
+ 0% {
462
+ offset-distance: 0%;
463
+ opacity: 1;
464
+ }
465
+ 90% {
466
+ opacity: 1;
467
+ }
468
+ 100% {
469
+ offset-distance: 100%;
470
+ opacity: 0;
471
+ }
472
+ }
473
+ @keyframes sprout-agent-fanout-comet-back {
474
+ 0% {
475
+ offset-distance: 100%;
476
+ opacity: 1;
477
+ }
478
+ 90% {
479
+ opacity: 1;
480
+ }
481
+ 100% {
482
+ offset-distance: 0%;
483
+ opacity: 0;
484
+ }
485
+ }
486
+ @keyframes sprout-agent-fanout-breathe {
487
+ from {
488
+ scale: 0.82;
489
+ }
490
+ to {
491
+ scale: 1.08;
492
+ }
493
+ }
494
+ @keyframes sprout-agent-fanout-pop {
495
+ 0% {
496
+ scale: 0.82;
497
+ }
498
+ 60% {
499
+ scale: 1.08;
500
+ }
501
+ 100% {
502
+ scale: 1;
503
+ }
504
+ }
505
+ @keyframes sprout-agent-fanout-ring {
506
+ from {
507
+ box-shadow: 0 0 0 0
508
+ color-mix(in oklab, var(--sprout-primary) 28%, transparent);
509
+ }
510
+ to {
511
+ box-shadow: 0 0 0 14px transparent;
512
+ }
513
+ }
514
+ @keyframes sprout-agent-fanout-pulse {
515
+ 0% {
516
+ box-shadow: 0 0 0 0
517
+ color-mix(in oklab, var(--sprout-agent-fanout-color) 45%, transparent);
518
+ }
519
+ 100% {
520
+ box-shadow: 0 0 0 5px transparent;
521
+ }
522
+ }
523
+ @keyframes sprout-agent-fanout-enter {
524
+ from {
525
+ filter: blur(2px);
526
+ opacity: 0;
527
+ translate: 0 3px;
528
+ }
529
+ }
@@ -1,4 +1,5 @@
1
1
  @import "./accordion.css";
2
+ @import "./agent-fanout.css";
2
3
  @import "./alert.css";
3
4
  @import "./alert-dialog.css";
4
5
  @import "./aspect-ratio.css";
@@ -52,6 +53,7 @@
52
53
  @import "./pagination.css";
53
54
  @import "./plan-outline.css";
54
55
  @import "./popover.css";
56
+ @import "./presence-cursor.css";
55
57
  @import "./progress.css";
56
58
  @import "./prompt-list.css";
57
59
  @import "./questionnaire.css";
@@ -62,6 +64,7 @@
62
64
  @import "./review-card.css";
63
65
  @import "./rich-text-editor.css";
64
66
  @import "./schema-diagram.css";
67
+ @import "./sql-viewer.css";
65
68
  @import "./scroll-area.css";
66
69
  @import "./search-dialog.css";
67
70
  @import "./search-field.css";
@@ -206,7 +206,7 @@
206
206
  );
207
207
  --sprout-message-bubble-ink: var(--sprout-danger);
208
208
  }
209
- .sprout-message-bubble-destructive:focus-visible {
209
+ .sprout-message-bubble-destructive:is(button, a):focus-visible {
210
210
  outline-color: var(--sprout-danger);
211
211
  }
212
212