-
Notifications
You must be signed in to change notification settings - Fork 2.2k
Image generation tool #2789
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Image generation tool #2789
Changes from all commits
2ef9ff8
2e5573e
afa525a
f648f44
d131075
ebabaf2
a29bb89
b7ab669
fdbb29d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| import type { LocalAgentFixture } from "../../../../testing/fake-llm-server/localAgentTypes"; | ||
|
|
||
| export const fixture: LocalAgentFixture = { | ||
| description: "Generate an image using the generate_image tool", | ||
| turns: [ | ||
| { | ||
| text: "I'll generate a hero image for your landing page.", | ||
| toolCalls: [ | ||
| { | ||
| name: "generate_image", | ||
| args: { | ||
| prompt: | ||
| "A modern, minimal hero illustration of a rocket launching from a laptop screen, flat design style, blue and purple gradient background, clean lines", | ||
| }, | ||
| }, | ||
| ], | ||
| }, | ||
| { | ||
| text: "I've generated the hero image and saved it to your project. You can find it in the .dyad/media directory.", | ||
| }, | ||
| ], | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| import { testSkipIfWindows } from "./helpers/test_helper"; | ||
|
|
||
| /** | ||
| * E2E tests for the generate_image agent tool | ||
| * Tests image generation in local-agent mode | ||
| */ | ||
|
|
||
| testSkipIfWindows("local-agent - generate image", async ({ po }) => { | ||
| await po.setUpDyadPro({ localAgent: true }); | ||
| await po.importApp("minimal"); | ||
| await po.chatActions.selectLocalAgentMode(); | ||
|
|
||
| await po.sendPrompt("tc=local-agent/generate-image"); | ||
|
|
||
| await po.snapshotMessages(); | ||
| }); |
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| - paragraph: /Generate an AI_RULES\.md file for this app\. Describe the tech stack in 5-\d+ bullet points and describe clear rules about what libraries to use for what\./ | ||
| - button "file1.txt file1.txt Edit": | ||
| - img | ||
| - text: "" | ||
| - button "Edit": | ||
| - img | ||
| - text: "" | ||
| - img | ||
| - paragraph: More EOM | ||
| - button "Copy": | ||
| - img | ||
| - img | ||
| - text: Approved | ||
| - img | ||
| - text: claude-opus-4-5 | ||
| - img | ||
| - text: less than a minute ago | ||
| - button "Copy Request ID": | ||
| - img | ||
| - text: "" | ||
| - paragraph: tc=local-agent/generate-image | ||
| - paragraph: I'll generate a hero image for your landing page. | ||
| - button "Image Generation A modern, minimal hero illustration of a rocket launching from a laptop screen, flat design style, blue and purple gradient background, clean lines": | ||
| - img | ||
| - text: "" | ||
| - img | ||
| - paragraph: I've generated the hero image and saved it to your project. You can find it in the .dyad/media directory. | ||
| - button "Copy": | ||
| - img | ||
| - img | ||
| - text: claude-opus-4-5 | ||
| - img | ||
| - text: less than a minute ago | ||
| - button "Copy Request ID": | ||
| - img | ||
| - text: "" | ||
| - button "Undo": | ||
| - img | ||
| - text: "" | ||
| - button "Retry": | ||
| - img | ||
| - text: "" |
| Original file line number | Diff line number | Diff line change | ||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,87 @@ | ||||||||||||||||
| import type React from "react"; | ||||||||||||||||
| import { useState, type ReactNode } from "react"; | ||||||||||||||||
| import { ImageIcon } from "lucide-react"; | ||||||||||||||||
| import { CustomTagState } from "./stateTypes"; | ||||||||||||||||
| import { | ||||||||||||||||
| DyadCard, | ||||||||||||||||
| DyadCardHeader, | ||||||||||||||||
| DyadBadge, | ||||||||||||||||
| DyadExpandIcon, | ||||||||||||||||
| DyadStateIndicator, | ||||||||||||||||
| DyadCardContent, | ||||||||||||||||
| } from "./DyadCardPrimitives"; | ||||||||||||||||
|
|
||||||||||||||||
| interface DyadImageGenerationNode { | ||||||||||||||||
| properties: { | ||||||||||||||||
| prompt: string; | ||||||||||||||||
| path: string; | ||||||||||||||||
| state: CustomTagState; | ||||||||||||||||
| }; | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| interface DyadImageGenerationProps { | ||||||||||||||||
| children?: ReactNode; | ||||||||||||||||
| node?: DyadImageGenerationNode; | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| export const DyadImageGeneration: React.FC<DyadImageGenerationProps> = ({ | ||||||||||||||||
| children, | ||||||||||||||||
| node, | ||||||||||||||||
| }) => { | ||||||||||||||||
| const [isExpanded, setIsExpanded] = useState(false); | ||||||||||||||||
| const prompt = node?.properties?.prompt ?? ""; | ||||||||||||||||
| const imagePath = node?.properties?.path ?? ""; | ||||||||||||||||
| const state = node?.properties?.state; | ||||||||||||||||
| const inProgress = state === "pending"; | ||||||||||||||||
| const aborted = state === "aborted"; | ||||||||||||||||
|
|
||||||||||||||||
| return ( | ||||||||||||||||
| <DyadCard | ||||||||||||||||
| state={state} | ||||||||||||||||
| accentColor="violet" | ||||||||||||||||
| isExpanded={isExpanded} | ||||||||||||||||
| onClick={() => setIsExpanded(!isExpanded)} | ||||||||||||||||
| > | ||||||||||||||||
| <DyadCardHeader icon={<ImageIcon size={15} />} accentColor="violet"> | ||||||||||||||||
| <DyadBadge color="violet">Image Generation</DyadBadge> | ||||||||||||||||
| {!isExpanded && prompt && ( | ||||||||||||||||
| <span className="text-sm text-muted-foreground italic truncate"> | ||||||||||||||||
| {prompt} | ||||||||||||||||
| </span> | ||||||||||||||||
| )} | ||||||||||||||||
| {inProgress && ( | ||||||||||||||||
| <DyadStateIndicator state="pending" pendingLabel="Generating..." /> | ||||||||||||||||
| )} | ||||||||||||||||
| {aborted && ( | ||||||||||||||||
| <DyadStateIndicator state="aborted" abortedLabel="Did not finish" /> | ||||||||||||||||
| )} | ||||||||||||||||
| <div className="ml-auto"> | ||||||||||||||||
| <DyadExpandIcon isExpanded={isExpanded} /> | ||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 MEDIUM | missing-state-feedback No success indicator when image generation completes When image generation finishes successfully, the card shows no visual confirmation. The spinner disappears with no replacement feedback, making it unclear whether the operation succeeded. This is especially noticeable because image generation is a slow operation where the user is actively watching for completion. Other similar tools in the codebase (e.g., 💡 Suggestion: Add a finished state indicator:
Suggested change
|
||||||||||||||||
| </div> | ||||||||||||||||
| </DyadCardHeader> | ||||||||||||||||
| <DyadCardContent isExpanded={isExpanded}> | ||||||||||||||||
| <div className="text-sm text-muted-foreground space-y-2"> | ||||||||||||||||
| {prompt && ( | ||||||||||||||||
| <div> | ||||||||||||||||
| <span className="text-xs font-medium text-muted-foreground"> | ||||||||||||||||
| Prompt: | ||||||||||||||||
| </span> | ||||||||||||||||
| <div className="italic mt-0.5 text-foreground">{prompt}</div> | ||||||||||||||||
| </div> | ||||||||||||||||
| )} | ||||||||||||||||
| {imagePath && ( | ||||||||||||||||
| <div> | ||||||||||||||||
| <span className="text-xs font-medium text-muted-foreground"> | ||||||||||||||||
| Saved to: | ||||||||||||||||
| </span> | ||||||||||||||||
| <div className="mt-0.5 font-mono text-xs text-foreground"> | ||||||||||||||||
| {imagePath} | ||||||||||||||||
| </div> | ||||||||||||||||
| </div> | ||||||||||||||||
| )} | ||||||||||||||||
| {children && <div className="mt-0.5 text-foreground">{children}</div>} | ||||||||||||||||
| </div> | ||||||||||||||||
| </DyadCardContent> | ||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 MEDIUM | missing-image-preview Expanded card shows only a file path, not the actual generated image When a user expands the card after image generation completes, they see the prompt text and a raw file path like 💡 Suggestion: Render an inline image preview in the expanded content when the image path is available and generation is complete. For example: {imagePath && !inProgress && (
<img src={resolvedImagePath} alt={prompt} className="rounded-md max-h-48 mt-2" />
)} |
||||||||||||||||
| </DyadCard> | ||||||||||||||||
| ); | ||||||||||||||||
| }; | ||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🟡 MEDIUM | consistency
No success state indicator after image generation completes
The component shows
Generating...(pending) andDid not finish(aborted), but has no visual confirmation when generation succeeds. Other card components likeDyadCopyshow a green checkmark viaDyadStateIndicatorwithstate='finished'and afinishedLabel. After waiting through what could be a long generation, the user gets no visual feedback that it worked.💡 Suggestion: Add a finished state indicator: