ai-design-system 0.1.75 → 0.1.78
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/components/ai-elements/ExpoAppPreview/ExpoAppPreview.tsx +112 -0
- package/components/ai-elements/ExpoAppPreview/index.ts +2 -0
- package/components/ai-elements/ExpoAppPreview/interfaces.ts +58 -0
- package/components/ai-elements/checkpoint.tsx +86 -0
- package/components/ai-elements/edge.tsx +41 -5
- package/components/blocks/AIConversation/AIConversation.stories.tsx +129 -0
- package/components/blocks/AIConversation/AIConversation.tsx +135 -77
- package/components/blocks/PreviewCanvas/index.ts +2 -0
- package/components/blocks/PreviewCanvas/interfaces.ts +21 -0
- package/components/blocks/PreviewCanvas/preview-layout.ts +112 -0
- package/components/blocks/WorkflowCanvas/WorkflowCanvas.stories.tsx +60 -0
- package/components/blocks/WorkflowCanvas/WorkflowCanvas.tsx +12 -1
- package/components/blocks/WorkflowCanvas/index.ts +2 -2
- package/components/blocks/index.ts +5 -0
- package/components/composites/AdjustableLayout/AdjustableLayout.tsx +8 -10
- package/components/composites/AgentIndicator/AgentIndicator.tsx +1 -0
- package/components/composites/DevicePreviewNode/DevicePreviewNode.stories.tsx +214 -0
- package/components/composites/DevicePreviewNode/DevicePreviewNode.tsx +250 -0
- package/components/composites/DevicePreviewNode/ExpoAppPreview.stories.tsx +228 -0
- package/components/composites/DevicePreviewNode/index.ts +12 -0
- package/components/composites/DevicePreviewNode/interfaces.ts +98 -0
- package/components/composites/DevicePreviewToolbar/DevicePreviewToolbar.stories.tsx +81 -0
- package/components/composites/DevicePreviewToolbar/DevicePreviewToolbar.tsx +127 -0
- package/components/composites/DevicePreviewToolbar/index.ts +6 -0
- package/components/composites/DevicePreviewToolbar/interfaces.ts +38 -0
- package/components/composites/MediaPreview/MediaPreview.stories.tsx +60 -0
- package/components/composites/MediaPreview/MediaPreview.tsx +186 -0
- package/components/composites/MediaPreview/index.ts +9 -0
- package/components/composites/PromptInput/PromptInput.tsx +17 -0
- package/components/composites/SpecialistMessage/SpecialistMessage.tsx +37 -1
- package/components/composites/StreamingMarkdown.tsx +11 -5
- package/components/composites/SystemMessage/SystemMessage.tsx +49 -1
- package/components/composites/index.ts +11 -1
- package/components/features/ChatPanel/ChatPanel.stories.tsx +2 -0
- package/components/features/ChatPanel/ChatPanel.tsx +21 -0
- package/components/features/ChatPanel/useChatPanel.d.ts +3 -0
- package/components/features/ChatPanel/useChatPanel.mock.ts +12 -0
- package/components/features/NodeEditor/NodeEditor.stories.tsx +409 -0
- package/components/features/NodeEditor/NodeEditor.tsx +5 -0
- package/components/features/NodeEditor/PREVIEW_MANIFEST_MENTAL_MODEL.md +197 -0
- package/components/features/NodeEditor/bookgeek.preview.json +311 -0
- package/components/features/PageLayout/PageLayout.stories.tsx +2 -2
- package/components/features/TextEditor/TextEditor.tsx +25 -23
- package/components/features/TextEditor/index.ts +3 -0
- package/components/features/TextEditor/useTextEditor.mock.ts +1 -14
- package/components/index.ts +15 -3
- package/dist/index.cjs +963 -132
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +135 -0
- package/dist/index.d.ts +295 -78
- package/dist/index.js +960 -136
- package/dist/index.js.map +1 -1
- package/{components/blocks/WorkflowCanvas → lib/workflow}/interfaces.ts +4 -1
- package/lib/workflow/layout-engine.ts +105 -0
- package/package.json +1 -1
- package/components/blocks/WorkflowCanvas/layout-engine.ts +0 -77
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { cn } from "@/lib/utils"
|
|
3
|
+
import type { ExpoAppPreviewProps } from "./interfaces"
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* ExpoAppPreview Block
|
|
7
|
+
*
|
|
8
|
+
* A generic, full-bleed sandboxed iframe container for rendering an Expo /
|
|
9
|
+
* React Native app compiled to web. It is intentionally unopinionated about
|
|
10
|
+
* where the app is served from: `src` may point at a Metro dev server (live
|
|
11
|
+
* reload during development) or a static export (published app).
|
|
12
|
+
*
|
|
13
|
+
* ## Security model
|
|
14
|
+
* The default sandbox retains same-origin (`allow-same-origin`) per the
|
|
15
|
+
* waypoint preview contract. As documented on the props, `allow-scripts`
|
|
16
|
+
* combined with `allow-same-origin` is not a security boundary against the
|
|
17
|
+
* embedded app; pass a sandbox without `allow-same-origin` for untrusted
|
|
18
|
+
* content.
|
|
19
|
+
*
|
|
20
|
+
* ## Host <-> app messaging
|
|
21
|
+
* `onMessage` forwards `message` events whose `event.source` is this iframe's
|
|
22
|
+
* window. It is an optional passthrough of a browser primitive — standalone
|
|
23
|
+
* previews render without it.
|
|
24
|
+
*/
|
|
25
|
+
export const ExpoAppPreview = React.memo<ExpoAppPreviewProps>(
|
|
26
|
+
({
|
|
27
|
+
src,
|
|
28
|
+
title = "App preview",
|
|
29
|
+
sandbox = "allow-scripts allow-same-origin allow-forms allow-popups",
|
|
30
|
+
allow,
|
|
31
|
+
loading = false,
|
|
32
|
+
loadingLabel = "Loading app…",
|
|
33
|
+
isEmpty = false,
|
|
34
|
+
emptyLabel = "No preview available",
|
|
35
|
+
error = null,
|
|
36
|
+
errorLabel = "Preview unavailable",
|
|
37
|
+
onLoad,
|
|
38
|
+
onError,
|
|
39
|
+
onMessage,
|
|
40
|
+
registerIframe,
|
|
41
|
+
className,
|
|
42
|
+
iframeClassName,
|
|
43
|
+
...props
|
|
44
|
+
}) => {
|
|
45
|
+
const iframeRef = React.useRef<HTMLIFrameElement>(null)
|
|
46
|
+
const onMessageRef = React.useRef(onMessage)
|
|
47
|
+
onMessageRef.current = onMessage
|
|
48
|
+
|
|
49
|
+
React.useEffect(() => {
|
|
50
|
+
if (!registerIframe) return undefined
|
|
51
|
+
registerIframe(iframeRef.current)
|
|
52
|
+
return () => registerIframe(null)
|
|
53
|
+
}, [registerIframe])
|
|
54
|
+
|
|
55
|
+
React.useEffect(() => {
|
|
56
|
+
if (!onMessageRef.current) return undefined
|
|
57
|
+
const handler = (event: MessageEvent): void => {
|
|
58
|
+
if (event.source !== iframeRef.current?.contentWindow) return
|
|
59
|
+
onMessageRef.current?.(event)
|
|
60
|
+
}
|
|
61
|
+
window.addEventListener("message", handler)
|
|
62
|
+
return () => window.removeEventListener("message", handler)
|
|
63
|
+
}, [])
|
|
64
|
+
|
|
65
|
+
return (
|
|
66
|
+
<div
|
|
67
|
+
className={cn(
|
|
68
|
+
"relative h-full w-full min-h-0 overflow-hidden bg-background",
|
|
69
|
+
className,
|
|
70
|
+
)}
|
|
71
|
+
{...props}
|
|
72
|
+
>
|
|
73
|
+
<iframe
|
|
74
|
+
ref={iframeRef}
|
|
75
|
+
src={src}
|
|
76
|
+
title={title}
|
|
77
|
+
sandbox={sandbox}
|
|
78
|
+
allow={allow}
|
|
79
|
+
onLoad={onLoad}
|
|
80
|
+
onError={onError}
|
|
81
|
+
className={cn("h-full w-full border-0", iframeClassName)}
|
|
82
|
+
/>
|
|
83
|
+
|
|
84
|
+
{loading && (
|
|
85
|
+
<div className="absolute inset-0 flex flex-col items-center justify-center gap-3 bg-background/80">
|
|
86
|
+
<div
|
|
87
|
+
className="h-8 w-8 animate-spin rounded-full border-2 border-primary/30 border-t-primary"
|
|
88
|
+
role="status"
|
|
89
|
+
aria-label={loadingLabel}
|
|
90
|
+
/>
|
|
91
|
+
<span className="text-sm text-muted-foreground">{loadingLabel}</span>
|
|
92
|
+
</div>
|
|
93
|
+
)}
|
|
94
|
+
|
|
95
|
+
{!loading && isEmpty && (
|
|
96
|
+
<div className="absolute inset-0 flex flex-col items-center justify-center gap-1 bg-background/80">
|
|
97
|
+
<span className="text-sm font-medium text-foreground">{emptyLabel}</span>
|
|
98
|
+
</div>
|
|
99
|
+
)}
|
|
100
|
+
|
|
101
|
+
{!loading && !isEmpty && error && (
|
|
102
|
+
<div className="absolute inset-0 flex flex-col items-center justify-center gap-1 bg-background/80">
|
|
103
|
+
<span className="text-sm font-medium text-destructive">{errorLabel}</span>
|
|
104
|
+
<span className="max-w-md text-center text-sm text-muted-foreground">{error}</span>
|
|
105
|
+
</div>
|
|
106
|
+
)}
|
|
107
|
+
</div>
|
|
108
|
+
)
|
|
109
|
+
},
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
ExpoAppPreview.displayName = "ExpoAppPreview"
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type * as React from "react"
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Props for the ExpoAppPreview Block.
|
|
5
|
+
*
|
|
6
|
+
* A generic, dependency-free container that renders a full-bleed sandboxed
|
|
7
|
+
* iframe pointing at an Expo/React Native app compiled to web (dev server or
|
|
8
|
+
* static export). It owns no knowledge of how the `src` is produced or served —
|
|
9
|
+
* consumers (e.g. waypoint) compute the URL and pass it in.
|
|
10
|
+
*/
|
|
11
|
+
export interface ExpoAppPreviewProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
12
|
+
/** Absolute or same-origin URL of the app to render in the iframe. */
|
|
13
|
+
src: string
|
|
14
|
+
/** Accessible name for the iframe (default: "App preview"). */
|
|
15
|
+
title?: string
|
|
16
|
+
/**
|
|
17
|
+
* iframe `sandbox` attribute. Defaults to ADR-033's contract:
|
|
18
|
+
* `allow-scripts allow-same-origin allow-forms allow-popups`.
|
|
19
|
+
*
|
|
20
|
+
* Security note: `allow-scripts` combined with `allow-same-origin` lets the
|
|
21
|
+
* embedded app remove its own sandbox restrictions, so this is an isolation
|
|
22
|
+
* boundary for reliable rendering, not a security boundary against the
|
|
23
|
+
* embedded app. Omit `allow-same-origin` when the embedded app is untrusted.
|
|
24
|
+
*/
|
|
25
|
+
sandbox?: string
|
|
26
|
+
/** iframe `allow` attribute (e.g. feature policy overrides). Optional. */
|
|
27
|
+
allow?: string
|
|
28
|
+
/** Show a loading overlay (spinner + label) while true. */
|
|
29
|
+
loading?: boolean
|
|
30
|
+
/** Label shown with the spinner while loading. */
|
|
31
|
+
loadingLabel?: string
|
|
32
|
+
/** Show an empty-state overlay when true (e.g. no build deployed yet). */
|
|
33
|
+
isEmpty?: boolean
|
|
34
|
+
/** Label shown in the empty state. */
|
|
35
|
+
emptyLabel?: string
|
|
36
|
+
/** When truthy, show an error overlay with this message. */
|
|
37
|
+
error?: string | null
|
|
38
|
+
/** Label shown above the error message. */
|
|
39
|
+
errorLabel?: string
|
|
40
|
+
/** Fired when the iframe finishes loading. */
|
|
41
|
+
onLoad?: () => void
|
|
42
|
+
/** Fired when the iframe errors. */
|
|
43
|
+
onError?: () => void
|
|
44
|
+
/**
|
|
45
|
+
* Receives the iframe element once mounted (and `null` on unmount).
|
|
46
|
+
* Lets consumers capture a screenshot (e.g. html-to-image) without the
|
|
47
|
+
* block owning capture tooling.
|
|
48
|
+
*/
|
|
49
|
+
registerIframe?: (el: HTMLIFrameElement | null) => void
|
|
50
|
+
/**
|
|
51
|
+
* Receives host-side `message` events originating from the iframe window
|
|
52
|
+
* (browser postMessage primitive; optional passthrough — not required for
|
|
53
|
+
* standalone rendering).
|
|
54
|
+
*/
|
|
55
|
+
onMessage?: (event: MessageEvent) => void
|
|
56
|
+
/** Extra classes for the iframe element. */
|
|
57
|
+
iframeClassName?: string
|
|
58
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { Button } from "@/components/ui/button";
|
|
4
|
+
import { Separator } from "@/components/ui/separator";
|
|
5
|
+
import { cn } from "@/lib/utils";
|
|
6
|
+
import { BookmarkIcon } from "lucide-react";
|
|
7
|
+
import type { ComponentProps, HTMLAttributes } from "react";
|
|
8
|
+
|
|
9
|
+
// ── Checkpoint ────────────────────────────────────────────────────────────────
|
|
10
|
+
// Root container: renders [icon] [trigger] [Separator] in a flex row.
|
|
11
|
+
// The Separator stretches to fill remaining width automatically.
|
|
12
|
+
|
|
13
|
+
export type CheckpointProps = HTMLAttributes<HTMLDivElement>;
|
|
14
|
+
|
|
15
|
+
export const Checkpoint = ({
|
|
16
|
+
className,
|
|
17
|
+
children,
|
|
18
|
+
...props
|
|
19
|
+
}: CheckpointProps) => (
|
|
20
|
+
<div
|
|
21
|
+
className={cn(
|
|
22
|
+
"flex items-center gap-0.5 overflow-hidden text-muted-foreground",
|
|
23
|
+
className
|
|
24
|
+
)}
|
|
25
|
+
{...props}
|
|
26
|
+
>
|
|
27
|
+
{children}
|
|
28
|
+
<Separator className="flex-1" />
|
|
29
|
+
</div>
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
// ── CheckpointIcon ────────────────────────────────────────────────────────────
|
|
33
|
+
// Defaults to BookmarkIcon. Pass children to use a custom icon.
|
|
34
|
+
|
|
35
|
+
export type CheckpointIconProps = ComponentProps<typeof BookmarkIcon>;
|
|
36
|
+
|
|
37
|
+
export const CheckpointIcon = ({
|
|
38
|
+
className,
|
|
39
|
+
children,
|
|
40
|
+
...props
|
|
41
|
+
}: CheckpointIconProps) =>
|
|
42
|
+
children ? (
|
|
43
|
+
<span className={cn("size-4 shrink-0", className)}>{children}</span>
|
|
44
|
+
) : (
|
|
45
|
+
<BookmarkIcon
|
|
46
|
+
aria-hidden="true"
|
|
47
|
+
className={cn("size-4 shrink-0", className)}
|
|
48
|
+
{...props}
|
|
49
|
+
/>
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
// ── CheckpointTrigger ─────────────────────────────────────────────────────────
|
|
53
|
+
// The clickable restore button. Extends shadcn/ui Button.
|
|
54
|
+
|
|
55
|
+
export type CheckpointTriggerProps = ComponentProps<typeof Button> & {
|
|
56
|
+
/** Optional tooltip shown on hover and used as aria-label when no children. */
|
|
57
|
+
tooltip?: string;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export const CheckpointTrigger = ({
|
|
61
|
+
children,
|
|
62
|
+
tooltip,
|
|
63
|
+
variant = "ghost",
|
|
64
|
+
size = "sm",
|
|
65
|
+
className,
|
|
66
|
+
...props
|
|
67
|
+
}: CheckpointTriggerProps) => (
|
|
68
|
+
<Button
|
|
69
|
+
aria-label={tooltip ?? "Restore checkpoint"}
|
|
70
|
+
className={cn(
|
|
71
|
+
"h-8 gap-1.5 px-3 text-muted-foreground hover:text-foreground",
|
|
72
|
+
className
|
|
73
|
+
)}
|
|
74
|
+
size={size}
|
|
75
|
+
title={tooltip}
|
|
76
|
+
type="button"
|
|
77
|
+
variant={variant}
|
|
78
|
+
{...props}
|
|
79
|
+
>
|
|
80
|
+
{children}
|
|
81
|
+
</Button>
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
Checkpoint.displayName = "Checkpoint";
|
|
85
|
+
CheckpointIcon.displayName = "CheckpointIcon";
|
|
86
|
+
CheckpointTrigger.displayName = "CheckpointTrigger";
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
BaseEdge,
|
|
3
|
+
EdgeLabelRenderer,
|
|
3
4
|
type EdgeProps,
|
|
4
5
|
getSimpleBezierPath,
|
|
5
6
|
getSmoothStepPath,
|
|
@@ -45,18 +46,37 @@ const Strict = ({
|
|
|
45
46
|
targetPosition,
|
|
46
47
|
markerEnd,
|
|
47
48
|
style,
|
|
49
|
+
label,
|
|
48
50
|
}: EdgeProps) => {
|
|
49
|
-
const [edgePath] = getSmoothStepPath({
|
|
51
|
+
const [edgePath, labelX, labelY] = getSmoothStepPath({
|
|
50
52
|
sourceX,
|
|
51
53
|
sourceY,
|
|
52
54
|
sourcePosition,
|
|
53
55
|
targetX,
|
|
54
56
|
targetY,
|
|
55
57
|
targetPosition,
|
|
56
|
-
borderRadius:
|
|
58
|
+
borderRadius: 8,
|
|
57
59
|
});
|
|
58
60
|
|
|
59
|
-
return
|
|
61
|
+
return (
|
|
62
|
+
<>
|
|
63
|
+
<BaseEdge id={id} markerEnd={markerEnd} path={edgePath} style={style} />
|
|
64
|
+
{label && (
|
|
65
|
+
<EdgeLabelRenderer>
|
|
66
|
+
<div
|
|
67
|
+
style={{
|
|
68
|
+
position: "absolute",
|
|
69
|
+
transform: `translate(-50%, -50%) translate(${labelX}px,${labelY}px)`,
|
|
70
|
+
pointerEvents: "all",
|
|
71
|
+
}}
|
|
72
|
+
className="nodrag nopan rounded-md bg-secondary/90 px-2 py-0.5 text-[11px] font-medium text-secondary-foreground border border-border/80 shadow-xs backdrop-blur-xs"
|
|
73
|
+
>
|
|
74
|
+
{label}
|
|
75
|
+
</div>
|
|
76
|
+
</EdgeLabelRenderer>
|
|
77
|
+
)}
|
|
78
|
+
</>
|
|
79
|
+
);
|
|
60
80
|
};
|
|
61
81
|
|
|
62
82
|
const Animated = ({
|
|
@@ -69,15 +89,16 @@ const Animated = ({
|
|
|
69
89
|
targetPosition,
|
|
70
90
|
markerEnd,
|
|
71
91
|
style,
|
|
92
|
+
label,
|
|
72
93
|
}: EdgeProps) => {
|
|
73
|
-
const [edgePath] = getSmoothStepPath({
|
|
94
|
+
const [edgePath, labelX, labelY] = getSmoothStepPath({
|
|
74
95
|
sourceX,
|
|
75
96
|
sourceY,
|
|
76
97
|
sourcePosition,
|
|
77
98
|
targetX,
|
|
78
99
|
targetY,
|
|
79
100
|
targetPosition,
|
|
80
|
-
borderRadius:
|
|
101
|
+
borderRadius: 8,
|
|
81
102
|
});
|
|
82
103
|
|
|
83
104
|
return (
|
|
@@ -86,6 +107,20 @@ const Animated = ({
|
|
|
86
107
|
<circle fill="var(--primary)" r="4">
|
|
87
108
|
<animateMotion dur="2s" path={edgePath} repeatCount="indefinite" />
|
|
88
109
|
</circle>
|
|
110
|
+
{label && (
|
|
111
|
+
<EdgeLabelRenderer>
|
|
112
|
+
<div
|
|
113
|
+
style={{
|
|
114
|
+
position: "absolute",
|
|
115
|
+
transform: `translate(-50%, -50%) translate(${labelX}px,${labelY}px)`,
|
|
116
|
+
pointerEvents: "all",
|
|
117
|
+
}}
|
|
118
|
+
className="nodrag nopan rounded-md bg-secondary/90 px-2 py-0.5 text-[11px] font-medium text-secondary-foreground border border-border/80 shadow-xs backdrop-blur-xs"
|
|
119
|
+
>
|
|
120
|
+
{label}
|
|
121
|
+
</div>
|
|
122
|
+
</EdgeLabelRenderer>
|
|
123
|
+
)}
|
|
89
124
|
</>
|
|
90
125
|
);
|
|
91
126
|
};
|
|
@@ -95,3 +130,4 @@ export const Edge = {
|
|
|
95
130
|
Temporary,
|
|
96
131
|
Animated,
|
|
97
132
|
};
|
|
133
|
+
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Meta, StoryObj } from '@storybook/react'
|
|
2
|
+
import { fn } from '@storybook/test'
|
|
2
3
|
import { AIConversation } from './AIConversation'
|
|
3
4
|
|
|
4
5
|
const meta: Meta<typeof AIConversation> = {
|
|
@@ -126,6 +127,92 @@ export const MultipleAgents: Story = {
|
|
|
126
127
|
},
|
|
127
128
|
}
|
|
128
129
|
|
|
130
|
+
export const NestedSubAgents: Story = {
|
|
131
|
+
args: {
|
|
132
|
+
messages: [
|
|
133
|
+
{
|
|
134
|
+
id: '1',
|
|
135
|
+
type: 'human',
|
|
136
|
+
role: 'user',
|
|
137
|
+
content: 'Generate a 30 second product demo video with motion graphics overlays',
|
|
138
|
+
avatarSrc: 'https://images.unsplash.com/photo-1494790108755-2616b612b786?w=32&h=32&fit=crop&crop=face',
|
|
139
|
+
avatarName: 'User',
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
id: '2',
|
|
143
|
+
type: 'ai',
|
|
144
|
+
role: 'orchestrator',
|
|
145
|
+
content: '',
|
|
146
|
+
avatarSrc: 'https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?w=32&h=32&fit=crop&crop=face',
|
|
147
|
+
avatarName: 'Coordinator',
|
|
148
|
+
subAgents: [
|
|
149
|
+
{
|
|
150
|
+
id: 'call_00_soED4oref3iyY11T',
|
|
151
|
+
name: 'task',
|
|
152
|
+
subAgentName: 'media-generator-agent',
|
|
153
|
+
input: 'Generate a 30 second product demo video with motion graphics overlays',
|
|
154
|
+
status: 'active',
|
|
155
|
+
// Nested children — video-sequencer and motion-graphics are both
|
|
156
|
+
// dispatched by media-generator-agent (task() calls), and stream
|
|
157
|
+
// their own tool-call-started/tool-output-delta/tool-finished
|
|
158
|
+
// events. This is what AIConversation must render one level deep.
|
|
159
|
+
subAgents: [
|
|
160
|
+
{
|
|
161
|
+
id: 'call_00_9LyCOvjRGrApV6II',
|
|
162
|
+
name: 'task',
|
|
163
|
+
subAgentName: 'video-sequencer',
|
|
164
|
+
input: 'Sequence the raw clips into a 30s cut with transitions',
|
|
165
|
+
output: 'Sequenced 12 clips into a 30s timeline. Exported sequence.json (30069 bytes).',
|
|
166
|
+
status: 'completed',
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
id: 'call_00_pQZBX2VUDWwmKQUz',
|
|
170
|
+
name: 'task',
|
|
171
|
+
subAgentName: 'motion-graphics',
|
|
172
|
+
input: 'Add lower-thirds, logo animation, and title card overlays',
|
|
173
|
+
output: 'Rendering title card overlay (2 of 4 assets)...',
|
|
174
|
+
status: 'active',
|
|
175
|
+
},
|
|
176
|
+
],
|
|
177
|
+
},
|
|
178
|
+
],
|
|
179
|
+
blocks: [
|
|
180
|
+
{ type: 'text', id: 'text-1', text: "I'll generate this using two specialized sub-agents." },
|
|
181
|
+
{
|
|
182
|
+
type: 'subAgent',
|
|
183
|
+
id: 'call_00_soED4oref3iyY11T',
|
|
184
|
+
subAgent: {
|
|
185
|
+
id: 'call_00_soED4oref3iyY11T',
|
|
186
|
+
name: 'task',
|
|
187
|
+
subAgentName: 'media-generator-agent',
|
|
188
|
+
input: 'Generate a 30 second product demo video with motion graphics overlays',
|
|
189
|
+
status: 'active',
|
|
190
|
+
subAgents: [
|
|
191
|
+
{
|
|
192
|
+
id: 'call_00_9LyCOvjRGrApV6II',
|
|
193
|
+
name: 'task',
|
|
194
|
+
subAgentName: 'video-sequencer',
|
|
195
|
+
input: 'Sequence the raw clips into a 30s cut with transitions',
|
|
196
|
+
output: 'Sequenced 12 clips into a 30s timeline. Exported sequence.json (30069 bytes).',
|
|
197
|
+
status: 'completed',
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
id: 'call_00_pQZBX2VUDWwmKQUz',
|
|
201
|
+
name: 'task',
|
|
202
|
+
subAgentName: 'motion-graphics',
|
|
203
|
+
input: 'Add lower-thirds, logo animation, and title card overlays',
|
|
204
|
+
output: 'Rendering title card overlay (2 of 4 assets)...',
|
|
205
|
+
status: 'active',
|
|
206
|
+
},
|
|
207
|
+
],
|
|
208
|
+
},
|
|
209
|
+
},
|
|
210
|
+
],
|
|
211
|
+
},
|
|
212
|
+
],
|
|
213
|
+
},
|
|
214
|
+
}
|
|
215
|
+
|
|
129
216
|
export const Empty: Story = {
|
|
130
217
|
args: {
|
|
131
218
|
messages: [],
|
|
@@ -161,4 +248,46 @@ export const UserOnly: Story = {
|
|
|
161
248
|
},
|
|
162
249
|
],
|
|
163
250
|
},
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
export const WithCheckpointRestore: Story = {
|
|
254
|
+
args: {
|
|
255
|
+
messages: [
|
|
256
|
+
{
|
|
257
|
+
id: '1',
|
|
258
|
+
type: 'human',
|
|
259
|
+
role: 'user',
|
|
260
|
+
content: 'What is React?',
|
|
261
|
+
avatarSrc: 'https://images.unsplash.com/photo-1494790108755-2616b612b786?w=32&h=32&fit=crop&crop=face',
|
|
262
|
+
avatarName: 'User',
|
|
263
|
+
},
|
|
264
|
+
{
|
|
265
|
+
id: '2',
|
|
266
|
+
type: 'ai',
|
|
267
|
+
role: 'orchestrator',
|
|
268
|
+
content: 'React is a JavaScript library for building user interfaces. It was developed by Facebook and is now maintained by Meta and a community of developers.',
|
|
269
|
+
avatarSrc: 'https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?w=32&h=32&fit=crop&crop=face',
|
|
270
|
+
avatarName: 'Coordinator',
|
|
271
|
+
checkpointId: 'cp-step-1',
|
|
272
|
+
},
|
|
273
|
+
{
|
|
274
|
+
id: '3',
|
|
275
|
+
type: 'human',
|
|
276
|
+
role: 'user',
|
|
277
|
+
content: 'How does component state work?',
|
|
278
|
+
avatarSrc: 'https://images.unsplash.com/photo-1494790108755-2616b612b786?w=32&h=32&fit=crop&crop=face',
|
|
279
|
+
avatarName: 'User',
|
|
280
|
+
},
|
|
281
|
+
{
|
|
282
|
+
id: '4',
|
|
283
|
+
type: 'ai',
|
|
284
|
+
role: 'orchestrator',
|
|
285
|
+
content: 'State in React represents values that can change over time and trigger component re-renders when updated.',
|
|
286
|
+
avatarSrc: 'https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?w=32&h=32&fit=crop&crop=face',
|
|
287
|
+
avatarName: 'Coordinator',
|
|
288
|
+
checkpointId: 'cp-step-2',
|
|
289
|
+
},
|
|
290
|
+
],
|
|
291
|
+
onRestoreCheckpoint: fn(),
|
|
292
|
+
},
|
|
164
293
|
}
|