Unverified 提交 f03bf922 authored 作者: Will Chen's avatar Will Chen 提交者: GitHub

Refactor Dyad card components to use new DyadCardPrimitives (#2482)

## Summary This PR introduces a new `DyadCardPrimitives` component library and refactors all Dyad markdown action cards to use these reusable primitives. This improves consistency, reduces code duplication, and makes styling changes easier to maintain across the codebase. ## Key Changes - **New `DyadCardPrimitives.tsx`**: Created a comprehensive library of reusable card components including: - `DyadCard`: Main container with accent color support and state indicators - `DyadCardHeader`: Header row with icon and flexible content area - `DyadBadge`: Small pill badges for card type labels - `DyadExpandIcon`: Animated chevron for expand/collapse - `DyadStateIndicator`: Spinner/X/checkmark for pending/aborted/finished states - `DyadCardContent`: Expandable content area with smooth animations - `DyadFilePath`, `DyadDescription`, `DyadFinishedIcon`: Additional utility components - Support for 11 accent colors (blue, purple, violet, red, amber, green, emerald, teal, sky, indigo, slate) - **Refactored components** to use new primitives: - `DyadAddDependency`: Simplified layout, improved package display styling - `DyadAddIntegration`: Consistent card styling for both pending and completed states - `DyadCodeSearch`: Cleaner header with state indicator - `DyadCodeSearchResult`: Standardized file list display - `DyadCodebaseContext`: Unified expand/collapse behavior - `DyadDatabaseSchema`: Simplified header structure - `DyadDelete`: Consistent red accent styling - `DyadEdit`: Improved state indicators and layout - `DyadExecuteSql`: Cleaner SQL badge and state handling - And 19+ additional components ## Implementation Details - All components now use semantic color tokens (`foreground`, `muted-foreground`, `background-lightest`, etc.) for better dark mode support - Consistent spacing and sizing across all cards (px-3, py-2 for headers, px-3 pb-3 for content) - Smooth transitions for expand/collapse animations using `max-h` and `opacity` - Left border accent (3px) appears only when card is in pending or aborted state - Removed inline icon imports from individual components; now centralized in primitives - Improved accessibility with proper semantic HTML and ARIA attributes https://claude.ai/code/session_01AAvVLShqeRjs42LhUdeK3e <!-- devin-review-badge-begin --> --- <a href="https://app.devin.ai/review/dyad-sh/dyad/pull/2482"> <picture> <source media="(prefers-color-scheme: dark)" srcset="https://static.devin.ai/assets/gh-open-in-devin-review-dark.svg?v=1"> <img src="https://static.devin.ai/assets/gh-open-in-devin-review-light.svg?v=1" alt="Open with Devin"> </picture> </a> <!-- devin-review-badge-end --> <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Large UI refactor across many chat card components and accessibility attributes; primary risk is visual/interaction regressions and snapshot/test churn rather than data/security impact. > > **Overview** > **Refactors Dyad chat “action cards” to a shared component library.** Introduces `DyadCardPrimitives` (e.g., `DyadCard`, `DyadCardHeader`, `DyadBadge`, `DyadStateIndicator`, `DyadCardContent`) and migrates many markdown-rendered cards (edit/write/delete/grep/logs/list-files/code-search, integrations, etc.) to use these primitives for consistent styling, expand/collapse behavior, and keyboard/ARIA interaction. > > Updates E2E ARIA snapshots to match the new card structure and accessibility tree, slightly tweaks chat message typography, bumps `@playwright/test` to `^1.58.2`, and expands `lint-staged` formatting (`oxfmt`) to include `*.json`. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit e020627fdb8955d948d982cb525c929b0610ee77. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY --> <!-- This is an auto-generated description by cubic. --> --- ## Summary by cubic Refactored all Dyad markdown action cards to use new DyadCardPrimitives for a consistent, accessible card UI with smoother interactions and easier styling. Adds lazy-mounted content and keyboard-accessible expand/collapse, reduces duplication, and improves dark mode across 28+ components. - **New Features** - Added DyadCardPrimitives: DyadCard, DyadCardHeader, DyadBadge, DyadExpandIcon, DyadStateIndicator (with finished state), DyadCardContent, plus helpers (DyadFilePath, DyadDescription, DyadFinishedIcon). - 11 accent colors with tinted icon circles and a left accent border shown for pending/aborted states; showAccent prop for explicit control. - Smooth expand/collapse using CSS grid with a rotating chevron; DyadCardContent lazy-mounts heavy children. - Semantic color tokens for reliable light/dark theming. - Improved accessibility: role="button", tabIndex, Enter/Space handlers, and aria-expanded on interactive cards. - **Refactors** - Migrated 28+ markdown action cards (e.g., AddDependency, CodeSearch, Edit, ExecuteSql, Status, WebSearch, Write) to the primitives; unified headers, badges, spacing, state indicators, and expand/collapse behavior. - Fixed regressions: correct state extraction (Delete/Rename/Output), restored children rendering (Delete/Read/Rename), passed isExpanded to all interactive cards, click propagation fix in Grep. - Removed per-component icon imports and ad‑hoc styles; net reduction of ~480 LOC. <sup>Written for commit e020627fdb8955d948d982cb525c929b0610ee77. Summary will update on new commits.</sup> <!-- End of auto-generated description by cubic. --> --------- Co-authored-by: 's avatarClaude <noreply@anthropic.com>
上级 1516181e
- paragraph: tc=write-index
- paragraph: OK, I'm going to do some writing now...
- 'button "Index.tsx src/pages/Index.tsx Edit Summary: write-description"':
- img
- button "Edit":
- img
- img
- paragraph: And it's done!
- button "Copy":
- img
- img
- text: Index.tsx
- text: test-model
- img
- text: "src/pages/Index.tsx Summary: write-description"
- paragraph: And it's done!
- text: less than a minute ago
- button "Undo":
- img
- button "Retry":
- img
\ No newline at end of file
- paragraph: tc=write-index
- paragraph: OK, I'm going to do some writing now...
- img
- text: Index.tsx
- button "Edit":
- 'button "Index.tsx src/pages/Index.tsx Edit Summary: write-description"':
- img
- img
- text: "src/pages/Index.tsx Summary: write-description"
- text: ""
- button "Edit":
- img
- text: ""
- img
- text: ""
- paragraph: And it's done!
- button "Copy":
- img
- img
- text: Approved
- img
- text: test-model
- img
- text: less than a minute ago
- img
- text: wrote 1 file(s)
- button "Undo":
- img
- text: ""
- button "Retry":
- img
- text: ""
\ No newline at end of file
- paragraph: basic
- img
- text: file1.txt
- img
- text: file1.txt
- button "file1.txt file1.txt Edit":
- img
- text: ""
- button "Edit":
- img
- text: ""
- img
- paragraph: More EOM
- button "Copy":
- img
- img
- text: Approved
- img
- text: test-model
- img
- text: less than a minute ago
- img
- text: wrote 1 file(s)
- paragraph: "[[UPLOAD_IMAGE_TO_CODEBASE]]"
- paragraph: "Attachments:"
- paragraph: "File to upload to codebase: logo.png (file id: DYAD_ATTACHMENT_0)"
- paragraph: Uploading image to codebase
- img
- text: file.png
- img
- text: "new/image/file.png Summary: Uploaded image to codebase"
- 'button "file.png new/image/file.png Edit Summary: Uploaded image to codebase"':
- img
- text: ""
- button "Edit":
- img
- text: ""
- img
- text: ""
- paragraph: "[[dyad-dump-path=*]]"
- button "Copy":
- img
- img
- text: Approved
- img
- text: test-model
- img
- text: less than a minute ago
- img
- text: wrote 1 file(s)
- button "Undo":
- img
- text: ""
- button "Retry":
- img
- text: ""
\ No newline at end of file
- paragraph: basic
- img
- text: file1.txt
- img
- text: file1.txt
- button "file1.txt file1.txt Edit"
- paragraph: More EOM
- img
- text: Approved
......
- paragraph: basic
- img
- text: file1.txt
- img
- text: file1.txt
- button "file1.txt file1.txt Edit"
- paragraph: More EOM
- img
- text: Approved
......
- paragraph: tc=write-index
- paragraph: OK, I'm going to do some writing now...
- img
- text: Index.tsx
- img
- text: "src/pages/Index.tsx Summary: write-description"
- 'button "Index.tsx src/pages/Index.tsx Edit Summary: write-description"':
- img
- text: ""
- button "Edit":
- img
- text: ""
- img
- text: ""
- paragraph: And it's done!
- button "Copy":
- img
- img
- text: Approved
- img
- text: test-model
- img
- text: less than a minute ago
- img
- text: wrote 1 file(s)
- button "Undo":
- img
- text: ""
- button "Retry":
- img
- text: ""
\ No newline at end of file
- 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\./
- img
- text: file1.txt
- img
- text: file1.txt
- button "file1.txt file1.txt Edit"
- paragraph: More EOM
- img
- text: Approved
......
- 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\./
- img
- text: file1.txt
- img
- text: file1.txt
- button "file1.txt file1.txt Edit"
- paragraph: More EOM
- img
- text: Approved
......
- paragraph: Create a utility function in src/utils/helper.ts
- img
- text: file1.txt
- button "Edit":
- img
- img
- text: file1.txt typescript
- button "file1.txt file1.txt Cancel"
- text: typescript
- button "Copy":
- img
- paragraph: More EOM
......
- paragraph: Create a simple React component in src/components/Hello.tsx
- img
- text: file1.txt
- button "Cancel":
- img
- img
- text: file1.txt file1.txt
- button "file1.txt file1.txt Cancel" [aria-expanded="true"]
- button [disabled]:
- img
- img
......
- 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\./
- img
- text: file1.txt
- button "Edit":
- img
- img
- text: file1.txt
- button "file1.txt file1.txt Edit"
- paragraph: More EOM
- button "Copy":
- img
......
- paragraph: Summarize from chat-id=1
- img
- text: file1.txt
- button "Edit":
- img
- img
- text: file1.txt
- button "file1.txt file1.txt Edit"
- paragraph: More EOM
- button "Copy":
- img
......
- paragraph: tc=create-multiple-errors
- paragraph: I will intentionally add multiple errors to test the Fix All Errors button
- img
- text: Index.tsx
- button "Edit":
- 'button "Index.tsx src/pages/Index.tsx Edit Summary: intentionally add first error"':
- img
- img
- text: "src/pages/Index.tsx Summary: intentionally add first error"
- img
- text: Error First error in Index...
- img
- button "Copy":
- text: ""
- button "Edit":
- img
- button "Fix with AI":
- text: ""
- img
- img
- text: ErrorComponent.tsx
- button "Edit":
- text: ""
- button "Error First error in Index Copy Fix with AI":
- img
- img
- text: "src/components/ErrorComponent.tsx Summary: intentionally add second error"
- img
- text: Error Second error in ErrorComponent...
- img
- button "Copy":
- text: ""
- img
- button "Fix with AI":
- button "Copy":
- img
- img
- text: helper.ts
- button "Edit":
- text: ""
- button "Fix with AI":
- img
- img
- text: "src/utils/helper.ts Summary: intentionally add third error"
- img
- text: Error Third error in helper...
- img
- button "Copy":
- text: ""
- 'button "ErrorComponent.tsx src/components/ErrorComponent.tsx Edit Summary: intentionally add second error"':
- img
- text: ""
- button "Edit":
- img
- text: ""
- img
- text: ""
- button "Error Second error in ErrorComponent Copy Fix with AI":
- img
- button "Fix with AI":
- text: ""
- img
- button "Copy":
- img
- text: ""
- button "Fix with AI":
- img
- text: ""
- 'button "helper.ts src/utils/helper.ts Edit Summary: intentionally add third error"':
- img
- text: ""
- button "Edit":
- img
- text: ""
- img
- text: ""
- button "Error Third error in helper Copy Fix with AI":
- img
- text: ""
- img
- button "Copy":
- img
- text: ""
- button "Fix with AI":
- img
- text: ""
- button "Fix All Errors (3)":
- img
- button:
- text: ""
- button "Copy":
- img
- img
- text: Approved
- img
- text: test-model
- img
- text: less than a minute ago
- img
- text: wrote 3 file(s)
......@@ -54,22 +72,27 @@
- listitem: First error in Index
- listitem: Second error in ErrorComponent
- listitem: Third error in helper
- img
- text: file1.txt
- button "Edit":
- button "file1.txt file1.txt Edit":
- img
- text: ""
- button "Edit":
- img
- text: ""
- img
- img
- text: file1.txt
- paragraph: More EOM
- button:
- button "Copy":
- img
- img
- text: Approved
- img
- text: test-model
- img
- text: less than a minute ago
- img
- text: wrote 1 file(s)
- button "Undo":
- img
- text: ""
- button "Retry":
- img
- text: ""
\ No newline at end of file
- paragraph: tc=create-error
- paragraph: I will intentionally add an error
- img
- text: Index.tsx
- button "Edit":
- 'button "Index.tsx src/pages/Index.tsx Edit Summary: intentionally add an error"':
- img
- img
- text: "src/pages/Index.tsx Summary: intentionally add an error"
- button:
- text: ""
- button "Edit":
- img
- text: ""
- img
- text: ""
- button "Copy":
- img
- img
- text: Approved
......@@ -19,17 +21,18 @@
- paragraph:
- text: "Fix error: Error Line 6 error Stack trace: Index ("
- link /http:\/\/localhost:\d+\/src\/pages\/Index\.tsx:6:6/:
- /url: /http:\/\/localhost:\d+\/src\/pages\/Index\.tsx:6:6/
- /url: http://localhost:53524/src/pages/Index.tsx:6:6
- text: )
- text: plaintext
- code: Fixing the error...
- img
- text: Index.tsx
- button "Edit":
- button "Index.tsx src/pages/Index.tsx Edit":
- img
- img
- text: src/pages/Index.tsx
- button:
- text: ""
- button "Edit":
- img
- text: ""
- img
- button "Copy":
- img
- img
- text: Approved
......@@ -41,5 +44,7 @@
- text: wrote 1 file(s)
- button "Undo":
- img
- text: ""
- button "Retry":
- img
- text: ""
\ No newline at end of file
- region "Notifications (F8)":
- list
- region "Notifications alt+T"
- heading "No more errors!" [level=1]
- link "Made with Dyad":
- /url: https://www.dyad.sh/
\ No newline at end of file
- 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\./
- img
- text: file1.txt
- button "Edit":
- img
- img
- text: file1.txt
- button "file1.txt file1.txt Edit"
- paragraph: More EOM
- button:
- img
......
- 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\./
- img
- text: file1.txt
- button "Edit":
- img
- img
- text: file1.txt
- button "file1.txt file1.txt Edit"
- paragraph: More EOM
- button:
- img
......
- 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: file1.txt
- text: test-model
- img
- text: file1.txt
- paragraph: More EOM
- text: less than a minute ago
- button "Undo":
- img
- text: ""
- button "Retry":
- img
- text: ""
\ No newline at end of file
- paragraph: hi
- button "file1.txt file1.txt Edit":
- img
- text: ""
- button "Edit":
- img
- text: ""
- img
- paragraph: More EOM
- button "Copy":
- img
- img
- text: file1.txt
- text: lmstudio-model-1
- img
- text: file1.txt
- paragraph: More EOM
- text: less than a minute ago
- button "Undo":
- img
- text: ""
- button "Retry":
- img
- text: ""
\ No newline at end of file
- 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\./
- img
- text: file1.txt
- button "Edit":
- button "file1.txt file1.txt Edit":
- img
- text: ""
- button "Edit":
- img
- text: ""
- img
- img
- text: file1.txt
- paragraph: More EOM
- button "Copy":
- img
......@@ -16,19 +17,23 @@
- text: less than a minute ago
- button "Copy Request ID":
- img
- text: ""
- paragraph: tc=local-agent/mcp-calculator
- paragraph: I'll calculate the sum of 5 and 3 using the calculator.
- img
- text: Tool Call
- img
- text: testing-mcp-server calculator_add
- img
- text: "Error MCP tool 'testing-mcp-server__calculator_add' failed: keyValidator._parse is not a function..."
- img
- button "Copy":
- button "Tool Call testing-mcp-server calculator_add":
- img
- text: ""
- img
- 'button "Error MCP tool ''testing-mcp-server__calculator_add'' failed: keyValidator._parse is not a function Copy Fix with AI"':
- img
- text: ""
- img
- button "Copy":
- img
- button "Fix with AI":
- text: ""
- button "Fix with AI":
- img
- text: ""
- paragraph: The sum of 5 and 3 is 8. The calculation was performed successfully using the MCP calculator tool.
- button "Copy":
- img
......@@ -38,7 +43,10 @@
- text: less than a minute ago
- button "Copy Request ID":
- img
- text: ""
- button "Undo":
- img
- text: ""
- button "Retry":
- img
- text: ""
\ No newline at end of file
- 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\./
- img
- text: file1.txt
- button "Edit":
- button "file1.txt file1.txt Edit":
- img
- text: ""
- button "Edit":
- img
- text: ""
- img
- img
- text: file1.txt
- paragraph: More EOM
- button "Copy":
- img
......@@ -16,10 +17,11 @@
- text: less than a minute ago
- button "Copy Request ID":
- img
- text: ""
- paragraph: tc=local-agent/ask-read-file
- paragraph: Let me read the file to explain its contents.
- img
- text: App.tsx Read src/App.tsx
- text: Read src/App.tsx
- paragraph: This is a simple React component that renders a div with the text 'Minimal imported app'. The component is exported as the default export.
- button "Copy":
- img
......@@ -29,7 +31,10 @@
- text: less than a minute ago
- button "Copy Request ID":
- img
- text: ""
- button "Undo":
- img
- text: ""
- button "Retry":
- img
- text: ""
\ No newline at end of file
- 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\./
- img
- text: file1.txt
- button "Edit":
- button "file1.txt file1.txt Edit":
- img
- text: ""
- button "Edit":
- img
- text: ""
- img
- img
- text: file1.txt
- paragraph: More EOM
- button "Copy":
- img
......@@ -16,20 +17,25 @@
- text: less than a minute ago
- button "Copy Request ID":
- img
- text: ""
- paragraph: tc=local-agent/parallel-tools
- paragraph: I'll create two files for you in parallel.
- img
- text: math.ts
- button "Edit":
- 'button "math.ts src/utils/math.ts Edit Summary: Create math utilities"':
- img
- img
- text: "src/utils/math.ts Summary: Create math utilities"
- img
- text: string.ts
- button "Edit":
- text: ""
- button "Edit":
- img
- img
- text: "src/utils/string.ts Summary: Create string utilities"
- text: ""
- img
- text: ""
- 'button "string.ts src/utils/string.ts Edit Summary: Create string utilities"':
- img
- text: ""
- button "Edit":
- img
- text: ""
- img
- text: ""
- paragraph: Task completed.
- button "Copy":
- img
......@@ -39,7 +45,10 @@
- text: less than a minute ago
- button "Copy Request ID":
- img
- text: ""
- button "Undo":
- img
- text: ""
- button "Retry":
- img
- text: ""
\ No newline at end of file
- 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\./
- img
- text: file1.txt
- button "Edit":
- button "file1.txt file1.txt Edit":
- img
- text: ""
- button "Edit":
- img
- text: ""
- img
- img
- text: file1.txt
- paragraph: More EOM
- button "Copy":
- img
......@@ -16,15 +17,16 @@
- text: less than a minute ago
- button "Copy Request ID":
- img
- text: ""
- paragraph: tc=local-agent/read-then-edit
- paragraph: Let me first read the current file contents to understand what we're working with.
- img
- text: App.tsx Read src/App.tsx
- text: Read src/App.tsx
- paragraph: Now I'll update the welcome message to say Hello World instead.
- img
- text: Turbo Edit App.tsx
- img
- text: src/App.tsx
- button "App.tsx src/App.tsx Turbo Edit":
- img
- text: ""
- img
- paragraph: Done! I've updated the title from 'Minimal imported app' to 'UPDATED imported app'. The change has been applied successfully.
- button "Copy":
- img
......@@ -34,7 +36,10 @@
- text: less than a minute ago
- button "Copy Request ID":
- img
- text: ""
- button "Undo":
- img
- text: ""
- button "Retry":
- img
- text: ""
\ No newline at end of file
- 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\./
- img
- text: file1.txt
- button "Edit":
- button "file1.txt file1.txt Edit":
- img
- text: ""
- button "Edit":
- img
- text: ""
- img
- img
- text: file1.txt
- paragraph: More EOM
- button "Copy":
- img
......@@ -16,10 +17,12 @@
- text: less than a minute ago
- button "Copy Request ID":
- img
- text: ""
- paragraph: tc=local-agent/code-search
- paragraph: I'll search for files related to React components in the codebase.
- 'button "Code Search React component rendering Query: React component rendering Results: - .gitignore - index.html - package.json"':
- button "Code Search React component rendering":
- img
- text: ""
- img
- paragraph: I found the relevant files! The main React component is in src/App.tsx which handles the app rendering.
- button "Copy":
......@@ -30,7 +33,10 @@
- text: less than a minute ago
- button "Copy Request ID":
- img
- text: ""
- button "Undo":
- img
- text: ""
- button "Retry":
- img
- text: ""
\ No newline at end of file
- 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\./
- img
- text: file1.txt
- button "Edit":
- button "file1.txt file1.txt Edit":
- img
- text: ""
- button "Edit":
- img
- text: ""
- img
- img
- text: file1.txt
- paragraph: More EOM
- button "Copy":
- img
......@@ -16,10 +17,11 @@
- text: less than a minute ago
- button "Copy Request ID":
- img
- text: ""
- paragraph: tc=local-agent/add-dependency
- paragraph: I'll add a dependency to your project.
- img
- text: Do you want to install these packages? @dyad-sh/supabase-management-js Make sure these packages are what you want.
- text: Add Packages Do you want to install these packages? @dyad-sh/supabase-management-js Make sure these packages are what you want.
- paragraph: Dependency added done.
- button "Copy":
- img
......@@ -29,7 +31,10 @@
- text: less than a minute ago
- button "Copy Request ID":
- img
- text: ""
- button "Undo":
- img
- text: ""
- button "Retry":
- img
- text: ""
\ No newline at end of file
- 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\./
- img
- text: file1.txt
- button "Edit":
- button "file1.txt file1.txt Edit":
- img
- text: ""
- button "Edit":
- img
- text: ""
- img
- img
- text: file1.txt
- paragraph: More EOM
- button "Copy":
- img
......@@ -16,10 +17,11 @@
- text: less than a minute ago
- button "Copy Request ID":
- img
- text: ""
- paragraph: tc=local-agent/add-dependency
- paragraph: I'll add a dependency to your project.
- img
- text: Do you want to install these packages? @dyad-sh/supabase-management-js Make sure these packages are what you want.
- text: Add Packages Do you want to install these packages? @dyad-sh/supabase-management-js Make sure these packages are what you want.
- paragraph: Dependency added done.
- button "Copy":
- img
......@@ -29,7 +31,10 @@
- text: less than a minute ago
- button "Copy Request ID":
- img
- text: ""
- button "Undo":
- img
- text: ""
- button "Retry":
- img
- text: ""
\ No newline at end of file
- 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\./
- img
- text: file1.txt
- button "Edit":
- button "file1.txt file1.txt Edit":
- img
- text: ""
- button "Edit":
- img
- text: ""
- img
- img
- text: file1.txt
- paragraph: More EOM
- button "Copy":
- img
......@@ -16,17 +17,21 @@
- text: less than a minute ago
- button "Copy Request ID":
- img
- text: ""
- paragraph: tc=local-agent/add-dependency
- paragraph: I'll add a dependency to your project.
- img
- text: Do you want to install these packages? @dyad-sh/supabase-management-js Make sure these packages are what you want.
- img
- text: "Error Tool 'add_dependency' failed: User denied permission for add_dependency..."
- img
- button "Copy":
- text: Add Packages Do you want to install these packages? @dyad-sh/supabase-management-js Make sure these packages are what you want.
- 'button "Error Tool ''add_dependency'' failed: User denied permission for add_dependency Copy Fix with AI"':
- img
- text: ""
- img
- button "Copy":
- img
- button "Fix with AI":
- text: ""
- button "Fix with AI":
- img
- text: ""
- paragraph: Dependency added done.
- button "Copy":
- img
......@@ -36,7 +41,10 @@
- text: less than a minute ago
- button "Copy Request ID":
- img
- text: ""
- button "Undo":
- img
- text: ""
- button "Retry":
- img
- text: ""
\ No newline at end of file
- 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\./
- img
- text: file1.txt
- button "Edit":
- button "file1.txt file1.txt Edit":
- img
- text: ""
- button "Edit":
- img
- text: ""
- img
- img
- text: file1.txt
- paragraph: More EOM
- button "Copy":
- img
......@@ -16,16 +17,19 @@
- text: less than a minute ago
- button "Copy Request ID":
- img
- text: ""
- paragraph: tc=local-agent/upload-to-codebase
- paragraph: "Attachments:"
- paragraph: "File to upload to codebase: logo.png (file id: DYAD_ATTACHMENT_0)"
- paragraph: I'll upload your file to the codebase.
- img
- text: uploaded-file.png
- button "Edit":
- 'button "uploaded-file.png assets/uploaded-file.png Edit Summary: Upload file to codebase"':
- img
- img
- text: "assets/uploaded-file.png Summary: Upload file to codebase"
- text: ""
- button "Edit":
- img
- text: ""
- img
- text: ""
- paragraph: I've successfully uploaded your file to assets/uploaded-file.png in the codebase.
- button "Copy":
- img
......@@ -35,7 +39,10 @@
- text: less than a minute ago
- button "Copy Request ID":
- img
- text: ""
- button "Undo":
- img
- text: ""
- button "Retry":
- img
- text: ""
\ No newline at end of file
- 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\./
- img
- text: file1.txt
- button "Edit":
- button "file1.txt file1.txt Edit":
- img
- text: ""
- button "Edit":
- img
- text: ""
- img
- img
- text: file1.txt
- paragraph: More EOM
- button "Copy":
- img
......@@ -16,25 +17,30 @@
- text: less than a minute ago
- button "Copy Request ID":
- img
- text: ""
- paragraph: tc=local-agent/grep-search
- paragraph: I'll search for 'createRoot' in the codebase to find where the React app is initialized.
- img
- text: GREP"createRoot"(2 matches)
- img
- text: log
- button "Copy":
- 'button "GREP \"createRoot\" (2 matches) log Copy log src/main.tsx:1: import { createRoot } from \"react-dom/client\"; src/main.tsx:4: createRoot(document.getElementById(\"root\")!).render(<App />);" [expanded]':
- img
- text: log
- code: "src/main.tsx:1: import { createRoot } from \"react-dom/client\"; src/main.tsx:4: createRoot(document.getElementById(\"root\")!).render(<App />);"
- text: ""
- img
- text: ""
- button "Copy":
- img
- text: ""
- text: ""
- code: "src/main.tsx:1: import { createRoot } from \"react-dom/client\"; src/main.tsx:4: createRoot(document.getElementById(\"root\")!).render(<App />);"
- paragraph: Now I'll search specifically in .tsx files for 'App' to find component references.
- img
- text: GREP"App" in *.tsx(4 matches)
- img
- text: log
- button "Copy":
- 'button "GREP \"App\" in *.tsx (4 matches) log Copy log src/App.tsx:1: const App = () => <div>Minimal imported app</div>; src/App.tsx:3: export default App; src/main.tsx:2: import App from \"./App.tsx\"; src/main.tsx:4: createRoot(document.getElementById(\"root\")!).render(<App />);" [expanded]':
- img
- text: ""
- img
- text: ""
- button "Copy":
- img
- text: log
- code: "src/App.tsx:1: const App = () => <div>Minimal imported app</div>; src/App.tsx:3: export default App; src/main.tsx:2: import App from \"./App.tsx\"; src/main.tsx:4: createRoot(document.getElementById(\"root\")!).render(<App />);"
- text: ""
- text: ""
- code: "src/App.tsx:1: const App = () => <div>Minimal imported app</div>; src/App.tsx:3: export default App; src/main.tsx:2: import App from \"./App.tsx\"; src/main.tsx:4: createRoot(document.getElementById(\"root\")!).render(<App />);"
- paragraph: I found the matches! The React app is initialized in src/main.tsx using createRoot, and the App component is defined in src/App.tsx and imported in src/main.tsx.
- button "Copy":
- img
......@@ -44,7 +50,10 @@
- text: less than a minute ago
- button "Copy Request ID":
- img
- text: ""
- button "Undo":
- img
- text: ""
- button "Retry":
- img
- text: ""
\ No newline at end of file
- img
- text: GREP"createRoot"(2 matches)
- img
- text: log
- button "Copy":
- 'button "GREP \"createRoot\" (2 matches) log Copy log src/main.tsx:1: import { createRoot } from \"react-dom/client\"; src/main.tsx:4: createRoot(document.getElementById(\"root\")!).render(<App />);" [expanded]':
- img
- text: log
- code: "src/main.tsx:1: import { createRoot } from \"react-dom/client\"; src/main.tsx:4: createRoot(document.getElementById(\"root\")!).render(<App />);"
\ No newline at end of file
- text: ""
- img
- text: ""
- button "Copy":
- img
- text: ""
- text: ""
- code: "src/main.tsx:1: import { createRoot } from \"react-dom/client\"; src/main.tsx:4: createRoot(document.getElementById(\"root\")!).render(<App />);"
\ No newline at end of file
- img
- text: GREP"App" in *.tsx(4 matches)
- img
- text: log
- button "Copy":
- 'button "GREP \"App\" in *.tsx (4 matches) log Copy log src/App.tsx:1: const App = () => <div>Minimal imported app</div>; src/App.tsx:3: export default App; src/main.tsx:2: import App from \"./App.tsx\"; src/main.tsx:4: createRoot(document.getElementById(\"root\")!).render(<App />);" [expanded]':
- img
- text: log
- code: "src/App.tsx:1: const App = () => <div>Minimal imported app</div>; src/App.tsx:3: export default App; src/main.tsx:2: import App from \"./App.tsx\"; src/main.tsx:4: createRoot(document.getElementById(\"root\")!).render(<App />);"
\ No newline at end of file
- text: ""
- img
- text: ""
- button "Copy":
- img
- text: ""
- text: ""
- code: "src/App.tsx:1: const App = () => <div>Minimal imported app</div>; src/App.tsx:3: export default App; src/main.tsx:2: import App from \"./App.tsx\"; src/main.tsx:4: createRoot(document.getElementById(\"root\")!).render(<App />);"
\ No newline at end of file
- 'button "List Files: src"':
- button "src - src/App.tsx - src/main.tsx - src/vite-env.d.ts (3 files total)" [expanded]:
- img
- text: ""
- img
- text: "- src/App.tsx - src/main.tsx - src/vite-env.d.ts (3 files total)"
\ No newline at end of file
- text: ""
\ No newline at end of file
- 'button "List Files: src (recursive)"':
- button "src recursive - src/App.tsx - src/main.tsx - src/vite-env.d.ts (3 files total)" [expanded]:
- img
- text: ""
- img
- text: "- src/App.tsx - src/main.tsx - src/vite-env.d.ts (3 files total)"
\ No newline at end of file
- text: ""
\ No newline at end of file
- button "List Files (recursive) (include hidden)":
- button /List Files recursive include hidden - \.dyad\/plans\/test-plan\.md - index\.html - package\.json - src\/App\.tsx - src\/main\.tsx - src\/vite-env\.d\.ts - tsconfig\.app\.json - tsconfig\.json - tsconfig\.node\.json - vite\.config\.ts \(\d+ files total\)/ [expanded]:
- img
- text: ""
- img
- text: /- \.dyad\/plans\/test-plan\.md - index\.html - package\.json - src\/App\.tsx - src\/main\.tsx - src\/vite-env\.d\.ts - tsconfig\.app\.json - tsconfig\.json - tsconfig\.node\.json - vite\.config\.ts \(\d+ files total\)/
- text: ""
\ No newline at end of file
- 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\./
- img
- text: file1.txt
- button "Edit":
- button "file1.txt file1.txt Edit":
- img
- text: ""
- button "Edit":
- img
- text: ""
- img
- img
- text: file1.txt
- paragraph: More EOM
- button "Copy":
- img
......@@ -16,19 +17,23 @@
- text: less than a minute ago
- button "Copy Request ID":
- img
- text: ""
- paragraph: tc=local-agent/read-logs
- paragraph: Let me check the recent console logs to see what's happening in the application.
- img
- text: /LOGSReading \d+ logs/
- img
- button "LOGS Reading 1 logs":
- img
- text: ""
- img
- paragraph: Now let me filter for only error logs to identify any issues.
- img
- text: "LOGSReading 0 logs (level: error)"
- img
- 'button "LOGS Reading 0 logs (level: error)"':
- img
- text: ""
- img
- paragraph: Let me also check client-side logs specifically.
- img
- text: "LOGSReading 0 logs (type: client)"
- img
- 'button "LOGS Reading 0 logs (type: client)"':
- img
- text: ""
- img
- paragraph: I've reviewed the console logs. The application appears to be running normally with no critical errors detected.
- button "Copy":
- img
......@@ -38,7 +43,10 @@
- text: less than a minute ago
- button "Copy Request ID":
- img
- text: ""
- button "Undo":
- img
- text: ""
- button "Retry":
- img
- text: ""
\ No newline at end of file
- 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\./
- img
- text: file1.txt
- button "Edit":
- button "file1.txt file1.txt Edit":
- img
- text: ""
- button "Edit":
- img
- text: ""
- img
- img
- text: file1.txt
- paragraph: More EOM
- button "Copy":
- img
......@@ -16,15 +17,17 @@
- text: less than a minute ago
- button "Copy Request ID":
- img
- text: ""
- paragraph: tc=local-agent/search-replace
- paragraph: Let me first read the file to see its contents.
- img
- text: App.tsx Read src/App.tsx
- text: Read src/App.tsx
- paragraph: Now I'll use search_replace to update the text with proper context.
- img
- text: Search & Replace App.tsx
- img
- text: src/App.tsx
- button "Search & Replace App.tsx src/App.tsx":
- img
- text: ""
- img
- text: ""
- paragraph: Done! I've updated the message using search_replace. The edit was applied successfully.
- button "Copy":
- img
......@@ -34,7 +37,10 @@
- text: less than a minute ago
- button "Copy Request ID":
- img
- text: ""
- button "Undo":
- img
- text: ""
- button "Retry":
- img
- text: ""
\ No newline at end of file
- paragraph: Summarize from chat-id=1
- img
- text: file1.txt
- button "Edit":
- img
- img
- text: file1.txt
- button "file1.txt file1.txt Edit"
- paragraph: More EOM
- button "Copy":
- img
......
- paragraph: hi
- img
- text: file1.txt
- img
- text: file1.txt
- button "file1.txt file1.txt Edit"
- paragraph: More EOM
- button "Retry":
- img
\ No newline at end of file
- paragraph: "[call_tool=calculator_add]"
- img
- text: Tool Call
- img
- text: testing-mcp-server calculator_add
- button "Tool Call testing-mcp-server calculator_add"
- img
- text: less than a minute ago
- paragraph: "[call_tool=calculator_add]"
- img
- text: Tool Call
- img
- text: testing-mcp-server calculator_add
- button "Tool Call testing-mcp-server calculator_add":
- img
- text: ""
- img
- img
- text: less than a minute ago
\ No newline at end of file
- paragraph: hi
- button "file1.txt file1.txt Edit":
- img
- text: ""
- button "Edit":
- img
- text: ""
- img
- paragraph: More EOM
- button "Copy":
- img
- img
- text: file1.txt
- text: testollama
- img
- text: file1.txt
- paragraph: More EOM
- text: less than a minute ago
- button "Undo":
- img
- text: ""
- button "Retry":
- img
- text: ""
\ No newline at end of file
- 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\./
- img
- text: file1.txt
- img
- text: file1.txt
- button "file1.txt file1.txt Edit":
- img
- text: ""
- button "Edit":
- img
- text: ""
- img
- paragraph: More EOM
- button "Copy":
- img
- img
- text: Approved
- paragraph: tc=partial-write
- paragraph: START OF MESSAGE
- img
- text: new-file.ts
- text: test-model
- img
- text: less than a minute ago
- img
- text: "src/new-file.ts Summary: this file will be partially written"
- text: wrote 1 file(s)
- paragraph: tc=partial-write
- paragraph: START OF MESSAGE
- 'button "new-file.ts src/new-file.ts Edit Summary: this file will be partially written"':
- img
- text: ""
- button "Edit":
- img
- text: ""
- img
- text: ""
- paragraph: Finished writing file.
- paragraph: "[[dyad-dump-path=*]]"
- button "Copy":
- img
- img
- text: Approved
- img
- text: test-model
- img
- text: less than a minute ago
- img
- text: wrote 1 file(s)
- button "Undo":
- img
- text: ""
- button "Retry":
- img
- text: ""
\ No newline at end of file
......@@ -15,13 +15,7 @@
- paragraph:
- strong: Which framework do you prefer?
- text: Vue
- img
- text: file1.txt
- button "Edit":
- img
- text: Edit
- img
- text: file1.txt
- button "file1.txt file1.txt Edit"
- paragraph: More EOM
- button "Copy":
- img
......
- paragraph: tc=create-ts-errors
- paragraph: This will get a TypeScript error.
- img
- text: bad-file.ts
- img
- text: "src/bad-file.ts Summary: This will get a TypeScript error."
- 'button "bad-file.ts src/bad-file.ts Edit Summary: This will get a TypeScript error."':
- img
- text: ""
- button "Edit":
- img
- text: ""
- img
- text: ""
- paragraph: EOM
- img
- text: Auto-fix5 problems
- img
- img
- text: file1.txt
- img
- text: file1.txt
- button "Auto-fix 5 problems":
- img
- text: ""
- img
- button "file1.txt file1.txt Edit":
- img
- text: ""
- button "Edit":
- img
- text: ""
- img
- paragraph: More EOM
- paragraph: "[[dyad-dump-path=*]]"
- img
- text: Auto-fix5 problems
- img
- img
- text: file1.txt
- img
- text: file1.txt
- button "Auto-fix 5 problems":
- img
- text: ""
- img
- button "file1.txt file1.txt Edit":
- img
- text: ""
- button "Edit":
- img
- text: ""
- img
- paragraph: More EOM
- paragraph: "[[dyad-dump-path=*]]"
- button "Copy":
- img
- img
- text: test-model
- img
- text: less than a minute ago
- paragraph: "Fix these 3 TypeScript compile-time errors:"
- list:
- listitem: src/bad-file.tsx:2:1 - Cannot find name 'nonExistentFunction1'. (TS2304)
- text: plaintext
- code: const App = () => <div>Minimal imported app</div>; nonExistentFunction1(); // <-- TypeScript compiler error here nonExistentFunction2();
- list:
- listitem: src/bad-file.tsx:3:1 - Cannot find name 'nonExistentFunction2'. (TS2304)
- text: plaintext
- code: nonExistentFunction1(); nonExistentFunction2(); // <-- TypeScript compiler error here nonExistentFunction3();
- list:
- listitem: src/bad-file.tsx:4:1 - Cannot find name 'nonExistentFunction3'. (TS2304)
- text: plaintext
- code: nonExistentFunction2(); nonExistentFunction3(); // <-- TypeScript compiler error here
- paragraph: Please fix all errors in a concise way.
- img
- text: file1.txt
- img
- text: file1.txt
- button "file1.txt file1.txt Edit":
- img
- text: ""
- button "Edit":
- img
- text: ""
- img
- paragraph: More EOM
- paragraph: "[[dyad-dump-path=*]]"
- img
- text: Auto-fix3 problems
- img
- img
- text: file1.txt
- img
- text: file1.txt
- button "Auto-fix 3 problems":
- img
- text: ""
- img
- button "file1.txt file1.txt Edit":
- img
- text: ""
- button "Edit":
- img
- text: ""
- img
- paragraph: More EOM
- paragraph: "[[dyad-dump-path=*]]"
- img
- text: Auto-fix3 problems
- img
- img
- text: file1.txt
- img
- text: file1.txt
- button "Auto-fix 3 problems":
- img
- text: ""
- img
- button "file1.txt file1.txt Edit":
- img
- text: ""
- button "Edit":
- img
- text: ""
- img
- paragraph: More EOM
- paragraph: "[[dyad-dump-path=*]]"
- button "Copy":
- img
- img
- text: test-model
- img
- text: less than a minute ago
- button "Undo":
- img
- text: ""
- button "Retry":
- img
- text: ""
\ No newline at end of file
- paragraph: "Fix these 2 TypeScript compile-time errors:"
- list:
- listitem: src/bad-file.tsx:2:1 - Cannot find name 'nonExistentFunction1'. (TS2304)
- text: plaintext
- code: const App = () => <div>Minimal imported app</div>; nonExistentFunction1(); // <-- TypeScript compiler error here nonExistentFunction2();
- list:
- listitem: src/bad-file.tsx:4:1 - Cannot find name 'nonExistentFunction3'. (TS2304)
- text: plaintext
- code: nonExistentFunction2(); nonExistentFunction3(); // <-- TypeScript compiler error here
- paragraph: Please fix all errors in a concise way.
- img
- text: bad-file.ts
- button "Edit":
- 'button "bad-file.ts src/bad-file.ts Edit Summary: Fix 2 errors and introduce a new error."':
- img
- img
- text: "src/bad-file.ts Summary: Fix 2 errors and introduce a new error."
- text: ""
- button "Edit":
- img
- text: ""
- img
- text: ""
- paragraph: "[[dyad-dump-path=*]]"
- button:
- button "Copy":
- img
- img
- text: test-model
- img
- text: less than a minute ago
- button "Undo":
- img
- text: ""
- button "Retry":
- img
- text: ""
\ No newline at end of file
......@@ -4,26 +4,50 @@
- text: main.tsx Delete src/main.tsx
- img
- text: "App.tsx main.tsx Rename From: src/App.tsx To: src/main.tsx"
- img
- text: main.tsx
- img
- text: "src/main.tsx Summary: final main.tsx file."
- 'button "main.tsx src/main.tsx Edit Summary: final main.tsx file."':
- img
- text: ""
- button "Edit":
- img
- text: ""
- img
- text: ""
- paragraph: EOM
- img
- text: Auto-fix1 problems
- img
- img
- text: bad-file.ts
- img
- text: "src/bad-file.ts Summary: Fix remaining error."
- button "Auto-fix 1 problems":
- img
- text: ""
- img
- 'button "bad-file.ts src/bad-file.ts Edit Summary: Fix remaining error."':
- img
- text: ""
- button "Edit":
- img
- text: ""
- img
- text: ""
- paragraph: "[[dyad-dump-path=*]]"
- button "Auto-fix 1 problems":
- img
- text: ""
- img
- 'button "bad-file.ts src/bad-file.ts Edit Summary: Fix remaining error."':
- img
- text: ""
- button "Edit":
- img
- text: ""
- img
- text: ""
- paragraph: "[[dyad-dump-path=*]]"
- button "Copy":
- img
- img
- text: Auto-fix1 problems
- img
- img
- text: bad-file.ts
- text: test-model
- img
- text: "src/bad-file.ts Summary: Fix remaining error."
- paragraph: "[[dyad-dump-path=*]]"
- text: less than a minute ago
- button "Undo":
- img
- text: ""
- button "Retry":
- img
- text: ""
\ No newline at end of file
- paragraph: tc=create-ts-errors
- paragraph: This will get a TypeScript error.
- 'button "bad-file.ts src/bad-file.ts Edit Summary: This will get a TypeScript error."':
- img
- text: ""
- button "Edit":
- img
- text: ""
- img
- text: ""
- paragraph: EOM
- button "Copy":
- img
- img
- text: bad-file.ts
- text: test-model
- img
- text: "src/bad-file.ts Summary: This will get a TypeScript error."
- paragraph: EOM
- text: less than a minute ago
- button "Undo":
- img
- text: ""
- button "Retry":
- img
- text: ""
\ No newline at end of file
- paragraph: tc=create-ts-errors
- paragraph: This will get a TypeScript error.
- img
- text: bad-file.ts
- img
- text: "src/bad-file.ts Summary: This will get a TypeScript error."
- 'button "bad-file.ts src/bad-file.ts Edit Summary: This will get a TypeScript error."':
- img
- text: ""
- button "Edit":
- img
- text: ""
- img
- text: ""
- paragraph: EOM
- img
- text: Auto-fix2 problems
- img
- img
- text: bad-file.ts
- img
- text: "src/bad-file.ts Summary: Fix 2 errors and introduce a new error."
- button "Auto-fix 2 problems":
- img
- text: ""
- img
- 'button "bad-file.ts src/bad-file.ts Edit Summary: Fix 2 errors and introduce a new error."':
- img
- text: ""
- button "Edit":
- img
- text: ""
- img
- text: ""
- paragraph: "[[dyad-dump-path=*]]"
- button "Auto-fix 1 problems":
- img
- text: ""
- img
- 'button "bad-file.ts src/bad-file.ts Edit Summary: Fix remaining error."':
- img
- text: ""
- button "Edit":
- img
- text: ""
- img
- text: ""
- paragraph: "[[dyad-dump-path=*]]"
- button "Copy":
- img
- img
- text: Auto-fix1 problems
- img
- img
- text: bad-file.ts
- text: test-model
- img
- text: "src/bad-file.ts Summary: Fix remaining error."
- paragraph: "[[dyad-dump-path=*]]"
- text: less than a minute ago
- button "Undo":
- img
- text: ""
- button "Retry":
- img
- text: ""
\ No newline at end of file
- img
- text: Auto-fix5 problems
- img
- text: "1"
- img
- text: /src\/bad-file\.ts 1:\d+ TS2307/
- paragraph: Cannot find module 'non-existent-class' or its corresponding type declarations.
- text: "2"
- img
- text: /src\/bad-file\.ts 2:\d+ TS2307/
- paragraph: Cannot find module 'non-existent-class' or its corresponding type declarations.
- text: "3"
- img
- text: /src\/bad-file\.ts 3:\d+ TS2307/
- paragraph: Cannot find module 'non-existent-class' or its corresponding type declarations.
- text: "4"
- img
- text: /src\/bad-file\.ts 4:\d+ TS2307/
- paragraph: Cannot find module 'non-existent-class' or its corresponding type declarations.
- text: "5"
- img
- text: /src\/bad-file\.ts 5:\d+ TS2307/
- paragraph: Cannot find module 'non-existent-class' or its corresponding type declarations.
\ No newline at end of file
- button /Auto-fix 5 problems 1 src\/bad-file\.ts 1:\d+ TS2307 Cannot find module 'non-existent-class' or its corresponding type declarations\. 2 src\/bad-file\.ts 2:\d+ TS2307 Cannot find module 'non-existent-class' or its corresponding type declarations\. 3 src\/bad-file\.ts 3:\d+ TS2307 Cannot find module 'non-existent-class' or its corresponding type declarations\. 4 src\/bad-file\.ts 4:\d+ TS2307 Cannot find module 'non-existent-class' or its corresponding type declarations\. 5 src\/bad-file\.ts 5:\d+ TS2307 Cannot find module 'non-existent-class' or its corresponding type declarations\./ [expanded]:
- img
- text: ""
- img
- text: ""
- img
- text: ""
- paragraph: Cannot find module 'non-existent-class' or its corresponding type declarations.
- text: ""
- img
- text: ""
- paragraph: Cannot find module 'non-existent-class' or its corresponding type declarations.
- text: ""
- img
- text: ""
- paragraph: Cannot find module 'non-existent-class' or its corresponding type declarations.
- text: ""
- img
- text: ""
- paragraph: Cannot find module 'non-existent-class' or its corresponding type declarations.
- text: ""
- img
- text: ""
- paragraph: Cannot find module 'non-existent-class' or its corresponding type declarations.
\ No newline at end of file
- paragraph: tc=create-unfixable-ts-errors
- paragraph: This should not get fixed
- img
- text: bad-file.ts
- img
- text: "src/bad-file.ts Summary: This will produce 5 TypeScript errors."
- 'button "bad-file.ts src/bad-file.ts Edit Summary: This will produce 5 TypeScript errors."':
- img
- text: ""
- button "Edit":
- img
- text: ""
- img
- text: ""
- paragraph: EOM
- img
- text: Auto-fix5 problems
- img
- img
- text: file1.txt
- img
- text: file1.txt
- button "Auto-fix 5 problems":
- img
- text: ""
- img
- button "file1.txt file1.txt Edit":
- img
- text: ""
- button "Edit":
- img
- text: ""
- img
- paragraph: More EOM
- paragraph: "[[dyad-dump-path=*]]"
- img
- text: Auto-fix5 problems
- img
- text: "1"
- img
- text: /src\/bad-file\.ts 1:\d+ TS2307/
- paragraph: Cannot find module 'non-existent-class' or its corresponding type declarations.
- text: "2"
- img
- text: /src\/bad-file\.ts 2:\d+ TS2307/
- paragraph: Cannot find module 'non-existent-class' or its corresponding type declarations.
- text: "3"
- img
- text: /src\/bad-file\.ts 3:\d+ TS2307/
- paragraph: Cannot find module 'non-existent-class' or its corresponding type declarations.
- text: "4"
- img
- text: /src\/bad-file\.ts 4:\d+ TS2307/
- paragraph: Cannot find module 'non-existent-class' or its corresponding type declarations.
- text: "5"
- img
- text: /src\/bad-file\.ts 5:\d+ TS2307/
- paragraph: Cannot find module 'non-existent-class' or its corresponding type declarations.
- img
- text: file1.txt
- img
- text: file1.txt
- button /Auto-fix 5 problems 1 src\/bad-file\.ts 1:\d+ TS2307 Cannot find module 'non-existent-class' or its corresponding type declarations\. 2 src\/bad-file\.ts 2:\d+ TS2307 Cannot find module 'non-existent-class' or its corresponding type declarations\. 3 src\/bad-file\.ts 3:\d+ TS2307 Cannot find module 'non-existent-class' or its corresponding type declarations\. 4 src\/bad-file\.ts 4:\d+ TS2307 Cannot find module 'non-existent-class' or its corresponding type declarations\. 5 src\/bad-file\.ts 5:\d+ TS2307 Cannot find module 'non-existent-class' or its corresponding type declarations\./ [expanded]:
- img
- text: ""
- img
- text: ""
- img
- text: ""
- paragraph: Cannot find module 'non-existent-class' or its corresponding type declarations.
- text: ""
- img
- text: ""
- paragraph: Cannot find module 'non-existent-class' or its corresponding type declarations.
- text: ""
- img
- text: ""
- paragraph: Cannot find module 'non-existent-class' or its corresponding type declarations.
- text: ""
- img
- text: ""
- paragraph: Cannot find module 'non-existent-class' or its corresponding type declarations.
- text: ""
- img
- text: ""
- paragraph: Cannot find module 'non-existent-class' or its corresponding type declarations.
- button "file1.txt file1.txt Edit":
- img
- text: ""
- button "Edit":
- img
- text: ""
- img
- paragraph: More EOM
- paragraph: "[[dyad-dump-path=*]]"
- button "Copy":
- img
- img
- text: test-model
- img
- text: less than a minute ago
- button "Undo":
- img
- text: ""
- button "Retry":
- img
- text: ""
\ No newline at end of file
- paragraph: tc=write-index
- paragraph: OK, I'm going to do some writing now...
- 'button "Index.tsx src/pages/Index.tsx Edit Summary: write-description"':
- img
- text: ""
- button "Edit":
- img
- text: ""
- img
- text: ""
- paragraph: And it's done!
- button "Copy":
- img
- img
- text: Index.tsx
- text: test-model
- img
- text: "src/pages/Index.tsx Summary: write-description"
- paragraph: And it's done!
- text: less than a minute ago
- button "Undo":
- img
- text: ""
- button "Retry":
- img
- text: ""
\ No newline at end of file
- paragraph: tc=write-index
- paragraph: OK, I'm going to do some writing now...
- img
- text: Index.tsx
- img
- text: "src/pages/Index.tsx Summary: write-description"
- 'button "Index.tsx src/pages/Index.tsx Edit Summary: write-description"':
- img
- text: ""
- button "Edit":
- img
- text: ""
- img
- text: ""
- paragraph: And it's done!
- button "Copy":
- img
- img
- text: Rejected
- img
- text: test-model
- img
- text: less than a minute ago
- button "Undo":
- img
- text: ""
- button "Retry":
- img
- text: ""
\ No newline at end of file
......@@ -53,12 +53,7 @@
- code: "`src/config/aws.ts`"
- text: ","
- code: "`src/services/s3-uploader.ts`"
- img
- text: file1.txt
- button "Edit":
- img
- img
- text: file1.txt
- button "file1.txt file1.txt Edit"
- paragraph: More EOM
- button:
- img
......
......@@ -24,12 +24,7 @@
- strong: Relevant Files
- text: ":"
- code: "`src/api/users.ts`"
- img
- text: file1.txt
- button "Edit":
- img
- img
- text: file1.txt
- button "file1.txt file1.txt Edit"
- paragraph: More EOM
- button:
- img
......
- paragraph: tc=read-index
- paragraph: "Read the index page:"
- img
- text: Index.tsx Read src/pages/Index.tsx
- text: Read src/pages/Index.tsx
- paragraph: Done.
- button "Copy":
- img
......@@ -13,14 +13,17 @@
- text: less than a minute ago
- button "Copy Request ID":
- img
- text: ""
- paragraph: tc=update-index-1
- paragraph: First read
- img
- text: Index.tsx
- button "Edit":
- 'button "Index.tsx src/pages/Index.tsx Edit Summary: replace file"':
- img
- img
- text: "src/pages/Index.tsx Summary: replace file"
- text: ""
- button "Edit":
- img
- text: ""
- img
- text: ""
- button "Copy":
- img
- img
......@@ -33,10 +36,11 @@
- text: wrote 1 file(s)
- button "Copy Request ID":
- img
- text: ""
- paragraph: tc=read-index
- paragraph: "Read the index page:"
- img
- text: Index.tsx Read src/pages/Index.tsx
- text: Read src/pages/Index.tsx
- paragraph: Done.
- button "Copy":
- img
......@@ -48,6 +52,7 @@
- text: less than a minute ago
- button "Copy Request ID":
- img
- text: ""
- paragraph: "[dump]"
- paragraph: "[[dyad-dump-path=*]]"
- button "Copy":
......@@ -60,7 +65,10 @@
- text: less than a minute ago
- button "Copy Request ID":
- img
- text: ""
- button "Undo":
- img
- text: ""
- button "Retry":
- img
- text: ""
\ No newline at end of file
- paragraph: tc=add-supabase
- paragraph: Adding supabase...
- text: Integrate with supabase?
- img
- text: Integration Integrate with supabase?
- button "Set up supabase"
- button "Copy":
- img
- img
- text: test-model
- img
- text: less than a minute ago
- button "Undo":
- img
- text: ""
- button "Retry":
- img
- text: ""
\ No newline at end of file
- paragraph: tc=add-supabase
- paragraph: Adding supabase...
- img
- text: Supabase integration complete
- text: Integration Complete Supabase integration complete
- paragraph: "This app is connected to Supabase project: Fake Supabase Project"
- button "Continue"
- button:
- button "Copy":
- img
- img
- text: test-model
......@@ -12,5 +12,7 @@
- text: less than a minute ago
- button "Undo":
- img
- text: ""
- button "Retry":
- img
- text: ""
\ No newline at end of file
- paragraph: tc=add-supabase
- paragraph: Adding supabase...
- text: Integrate with supabase?
- img
- text: Integration Integrate with supabase?
- button "Set up supabase"
- button "Copy":
- img
- img
- text: test-model
- img
- text: less than a minute ago
- button "Undo":
- img
- text: ""
- button "Retry":
- img
- text: ""
\ No newline at end of file
- paragraph: tc=turbo-edits-v2
- paragraph: Example with turbo edit v2
- img
- text: Search & Replace Index.tsx
- img
- text: src/pages/Index.tsx
- button "Search & Replace Index.tsx src/pages/Index.tsx"
- paragraph: End of turbo edit
- button "Copy":
- img
......
- paragraph: tc=turbo-edits-v2-trigger-fallback
- paragraph: Example with turbo edit v2
- img
- text: Search & Replace Index.tsx
- img
- text: src/pages/Index.tsx
- button "Search & Replace Index.tsx src/pages/Index.tsx":
- img
- text: ""
- img
- text: ""
- paragraph: End of turbo edit
- button "Warning Could not apply Turbo Edits properly for some of the files; re-generating code...":
- img
- text: ""
- img
- img
- text: Warning Could not apply Turbo Edits properly for some of the files; re-generating code......
- img
- img
- text: Index.tsx Read src/pages/Index.tsx
- img
- text: Search & Replace Index.tsx
- img
- text: src/pages/Index.tsx
- text: Read src/pages/Index.tsx
- button "Search & Replace Index.tsx src/pages/Index.tsx":
- img
- text: ""
- img
- text: ""
- paragraph: "[[dyad-dump-path=*]]"
- img
- text: Warning Could not apply Turbo Edits properly for some of the files; re-generating code......
- img
- img
- text: Index.tsx
- button "Edit":
- button "Warning Could not apply Turbo Edits properly for some of the files; re-generating code...":
- img
- img
- text: "src/pages/Index.tsx Summary: Rewrite file."
- text: ""
- img
- 'button "Index.tsx src/pages/Index.tsx Edit Summary: Rewrite file."':
- img
- text: ""
- button "Edit":
- img
- text: ""
- img
- text: ""
- paragraph: "[[dyad-dump-path=*]]"
- button "Copy":
- img
......@@ -33,7 +39,10 @@
- text: less than a minute ago
- button "Copy Request ID":
- img
- text: ""
- button "Undo":
- img
- text: ""
- button "Retry":
- img
- text: ""
\ No newline at end of file
module.exports = {
"**/*.{ts,tsx}": () => "npm run ts",
"**/*.{js,mjs,cjs,jsx,ts,mts,cts,tsx,vue,astro,svelte}": "oxlint",
"**/*.{js,css,md,ts,tsx,jsx}": "oxfmt",
"**/*.{js,css,md,ts,tsx,jsx,json}": "oxfmt",
};
......@@ -100,7 +100,7 @@
"@electron-forge/plugin-vite": "^7.11.1",
"@electron-forge/publisher-github": "^7.11.1",
"@electron/fuses": "^1.8.0",
"@playwright/test": "^1.52.0",
"@playwright/test": "^1.58.2",
"@storybook/addon-essentials": "^8.6.14",
"@storybook/blocks": "^8.6.14",
"@storybook/react": "^8.6.15",
......@@ -5428,13 +5428,13 @@
}
},
"node_modules/@playwright/test": {
"version": "1.55.0",
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.55.0.tgz",
"integrity": "sha512-04IXzPwHrW69XusN/SIdDdKZBzMfOT9UNT/YiJit/xpy2VuAoB8NHc8Aplb96zsWDddLnbkPL3TsmrS04ZU2xQ==",
"version": "1.58.2",
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.58.2.tgz",
"integrity": "sha512-akea+6bHYBBfA9uQqSYmlJXn61cTa+jbO87xVLCWbTqbWadRVmhxlXATaOjOgcBaWU4ePo0wB41KMFv3o35IXA==",
"devOptional": true,
"license": "Apache-2.0",
"dependencies": {
"playwright": "1.55.0"
"playwright": "1.58.2"
},
"bin": {
"playwright": "cli.js"
......@@ -19109,13 +19109,13 @@
}
},
"node_modules/playwright": {
"version": "1.55.0",
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.55.0.tgz",
"integrity": "sha512-sdCWStblvV1YU909Xqx0DhOjPZE4/5lJsIS84IfN9dAZfcl/CIZ5O8l3o0j7hPMjDvqoTF8ZUcc+i/GL5erstA==",
"version": "1.58.2",
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.58.2.tgz",
"integrity": "sha512-vA30H8Nvkq/cPBnNw4Q8TWz1EJyqgpuinBcHET0YVJVFldr8JDNiU9LaWAE1KqSkRYazuaBhTpB5ZzShOezQ6A==",
"devOptional": true,
"license": "Apache-2.0",
"dependencies": {
"playwright-core": "1.55.0"
"playwright-core": "1.58.2"
},
"bin": {
"playwright": "cli.js"
......@@ -19128,9 +19128,9 @@
}
},
"node_modules/playwright-core": {
"version": "1.55.0",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.55.0.tgz",
"integrity": "sha512-GvZs4vU3U5ro2nZpeiwyb0zuFaqb9sUiAJuyrWpcGouD8y9/HLgGbNRjIph7zU9D3hnPaisMl9zG9CgFi/biIg==",
"version": "1.58.2",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.58.2.tgz",
"integrity": "sha512-yZkEtftgwS8CsfYo7nm0KE8jsvm6i/PTgVtB8DL726wNf6H2IMsDuxCpJj59KDaxCtSnrWan2AeDqM7JBaultg==",
"devOptional": true,
"license": "Apache-2.0",
"bin": {
......
......@@ -137,7 +137,7 @@
"@electron-forge/plugin-vite": "^7.11.1",
"@electron-forge/publisher-github": "^7.11.1",
"@electron/fuses": "^1.8.0",
"@playwright/test": "^1.52.0",
"@playwright/test": "^1.58.2",
"@storybook/addon-essentials": "^8.6.14",
"@storybook/blocks": "^8.6.14",
"@storybook/react": "^8.6.15",
......
......@@ -100,7 +100,7 @@ const ChatMessage = ({ message, isLastMessage }: ChatMessageProps) => {
<StreamingLoadingAnimation variant="initial" />
) : (
<div
className="prose dark:prose-invert prose-headings:mb-2 prose-p:my-1 prose-pre:my-0 max-w-none break-words"
className="prose dark:prose-invert prose-headings:mb-2 prose-p:my-1 prose-pre:my-0 max-w-none break-words text-[15px]"
suppressHydrationWarning
>
{message.role === "assistant" ? (
......
......@@ -4,8 +4,15 @@ import { useState } from "react";
import { ipc } from "@/ipc/types";
import { Package, ChevronsUpDown, ChevronsDownUp } from "lucide-react";
import { Package } from "lucide-react";
import { CodeHighlight } from "./CodeHighlight";
import {
DyadCard,
DyadCardHeader,
DyadBadge,
DyadExpandIcon,
DyadCardContent,
} from "./DyadCardPrimitives";
interface DyadAddDependencyProps {
children?: ReactNode;
......@@ -17,34 +24,40 @@ export const DyadAddDependency: React.FC<DyadAddDependencyProps> = ({
children,
node,
}) => {
// Extract package attribute from the node if available
const packages = node?.properties?.packages?.split(" ") || "";
const packages = node?.properties?.packages
? node.properties.packages.split(" ").filter(Boolean)
: [];
const [isContentVisible, setIsContentVisible] = useState(false);
const hasChildren = !!children;
return (
<div
className={`bg-(--background-lightest) dark:bg-gray-900 hover:bg-(--background-lighter) rounded-lg px-4 py-3 border my-2 border-border ${
hasChildren ? "cursor-pointer" : ""
}`}
<DyadCard
accentColor="blue"
isExpanded={isContentVisible}
onClick={
hasChildren ? () => setIsContentVisible(!isContentVisible) : undefined
}
>
<div className="flex items-center justify-between mb-2">
<div className="flex items-center gap-2">
<Package size={18} className="text-gray-600 dark:text-gray-400" />
<DyadCardHeader icon={<Package size={15} />} accentColor="blue">
<DyadBadge color="blue">Add Packages</DyadBadge>
{hasChildren && (
<div className="ml-auto">
<DyadExpandIcon isExpanded={isContentVisible} />
</div>
)}
</DyadCardHeader>
{packages.length > 0 && (
<div className="text-gray-800 dark:text-gray-200 font-semibold text-base">
<div className="font-normal">
<div className="px-3 pb-2">
<div className="text-sm text-foreground mb-1">
Do you want to install these packages?
</div>{" "}
<div className="flex flex-wrap gap-2 mt-2">
</div>
<div className="flex flex-wrap gap-1.5 mt-1.5">
{packages.map((p: string) => (
<span
className="cursor-pointer text-blue-500 hover:text-blue-700 dark:text-blue-400 dark:hover:text-blue-300"
className="cursor-pointer text-sm px-2 py-0.5 rounded-md bg-blue-50 dark:bg-blue-950/40 text-blue-600 dark:text-blue-400 hover:bg-blue-100 dark:hover:bg-blue-900/60 ring-1 ring-inset ring-blue-200 dark:ring-blue-800 transition-colors"
key={p}
onClick={() => {
onClick={(e) => {
e.stopPropagation();
ipc.system.openExternalUrl(
`https://www.npmjs.com/package/${p}`,
);
......@@ -54,40 +67,18 @@ export const DyadAddDependency: React.FC<DyadAddDependencyProps> = ({
</span>
))}
</div>
<div className="text-xs text-muted-foreground mt-2">
Make sure these packages are what you want.
</div>
)}
</div>
{hasChildren && (
<div className="flex items-center">
{isContentVisible ? (
<ChevronsDownUp
size={20}
className="text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200"
/>
) : (
<ChevronsUpDown
size={20}
className="text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200"
/>
)}
</div>
)}
</div>
{packages.length > 0 && (
<div className="text-sm text-gray-600 dark:text-gray-400 mb-1">
Make sure these packages are what you want.{" "}
</div>
)}
{/* Show content if it's visible and has children */}
{isContentVisible && hasChildren && (
<div className="mt-2">
<DyadCardContent isExpanded={isContentVisible}>
{hasChildren && (
<div className="text-xs">
<CodeHighlight className="language-shell">{children}</CodeHighlight>
</div>
</div>
)}
</div>
</DyadCardContent>
</DyadCard>
);
};
......@@ -7,6 +7,8 @@ import { useAtomValue } from "jotai";
import { showError } from "@/lib/toast";
import { useLoadApp } from "@/hooks/useLoadApp";
import { useStreamChat } from "@/hooks/useStreamChat";
import { CheckCircle2, Plug } from "lucide-react";
import { DyadCard, DyadCardHeader, DyadBadge } from "./DyadCardPrimitives";
interface DyadAddIntegrationProps {
node: {
......@@ -50,65 +52,47 @@ export const DyadAddIntegration: React.FC<DyadAddIntegrationProps> = ({
if (app?.supabaseProjectName) {
return (
<div className="flex flex-col my-2 p-3 border border-green-300 dark:border-green-800/50 rounded-lg bg-green-50 dark:bg-green-900/20 shadow-sm">
<div className="flex items-center space-x-2">
<svg
className="w-5 h-5 text-green-600 dark:text-green-400"
fill="none"
stroke="currentColor"
strokeWidth={2}
viewBox="0 0 24 24"
>
<circle
cx="12"
cy="12"
r="10"
stroke="currentColor"
strokeWidth="2"
fill="currentColor"
className="opacity-20"
/>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M9 12l2 2 4-4"
/>
</svg>
<span className="font-semibold text-green-800 dark:text-green-300">
<DyadCard accentColor="green" state="finished">
<DyadCardHeader icon={<CheckCircle2 size={15} />} accentColor="green">
<DyadBadge color="green">Integration Complete</DyadBadge>
<span className="text-sm font-medium text-foreground">
Supabase integration complete
</span>
</div>
<div className="text-sm text-green-900 dark:text-green-100">
<p>
</DyadCardHeader>
<div className="px-3 pb-3">
<p className="text-sm text-muted-foreground mb-2">
This app is connected to Supabase project:{" "}
<span className="font-mono font-medium bg-green-100 dark:bg-green-900/40 px-1 py-0.5 rounded">
<span className="font-mono font-medium px-1.5 py-0.5 rounded bg-green-100 dark:bg-green-900/40 text-green-800 dark:text-green-200">
{app.supabaseProjectName}
</span>
</p>
</div>
<Button
onClick={handleKeepGoingClick}
className="self-start mt-2"
variant="default"
disabled={isStreaming}
size="sm"
>
Continue
</Button>
</div>
</DyadCard>
);
}
return (
<div className="flex flex-col gap-2 my-2 p-3 border rounded-md bg-secondary/10 dark:bg-secondary/20">
<div className="text-sm">
<div className="font-medium text-foreground">
<DyadCard accentColor="blue">
<DyadCardHeader icon={<Plug size={15} />} accentColor="blue">
<DyadBadge color="blue">Integration</DyadBadge>
<span className="text-sm font-medium text-foreground">
Integrate with {provider}?
</div>
<div className="text-muted-foreground text-xs">{children}</div>
</div>
<Button onClick={handleSetupClick} className="self-start w-full">
</span>
</DyadCardHeader>
<div className="px-3 pb-3">
<div className="text-xs text-muted-foreground mb-3">{children}</div>
<Button onClick={handleSetupClick} className="w-full" size="sm">
Set up {provider}
</Button>
</div>
</DyadCard>
);
};
差异被折叠。
import type React from "react";
import { useState, type ReactNode } from "react";
import { ChevronDown, ChevronUp, FileCode, Loader } from "lucide-react";
import { FileCode } from "lucide-react";
import { CustomTagState } from "./stateTypes";
import {
DyadCard,
DyadCardHeader,
DyadBadge,
DyadExpandIcon,
DyadStateIndicator,
DyadCardContent,
} from "./DyadCardPrimitives";
interface DyadCodeSearchProps {
children?: ReactNode;
......@@ -19,78 +27,48 @@ export const DyadCodeSearch: React.FC<DyadCodeSearchProps> = ({
const inProgress = state === "pending";
return (
<div
className={`bg-(--background-lightest) dark:bg-zinc-900 hover:bg-(--background-lighter) rounded-lg px-4 py-2 border my-2 cursor-pointer ${
inProgress ? "border-purple-500" : "border-border"
}`}
<DyadCard
state={state}
accentColor="indigo"
onClick={() => setIsExpanded(!isExpanded)}
role="button"
aria-expanded={isExpanded}
tabIndex={0}
onKeyDown={(e) => {
if (e.key === "Enter" || e.key === " ") {
e.preventDefault();
setIsExpanded(!isExpanded);
}
}}
isExpanded={isExpanded}
>
<div className="flex items-center justify-between">
<div className="flex items-center gap-2">
<FileCode size={16} className="text-purple-600" />
<div className="text-xs text-purple-600 font-medium">Code Search</div>
<DyadCardHeader icon={<FileCode size={15} />} accentColor="indigo">
<DyadBadge color="indigo">Code Search</DyadBadge>
{!isExpanded && query && (
<span className="text-sm text-muted-foreground italic truncate">
{query}
</span>
)}
{inProgress && (
<div className="flex items-center text-purple-600 text-xs">
<Loader size={14} className="mr-1 animate-spin" />
<span>Searching...</span>
</div>
<DyadStateIndicator state="pending" pendingLabel="Searching..." />
)}
<div className="ml-auto">
<DyadExpandIcon isExpanded={isExpanded} />
</div>
<div className="p-1 text-gray-500">
{isExpanded ? <ChevronUp size={16} /> : <ChevronDown size={16} />}
</div>
</div>
{/* Collapsed preview - show query */}
<div
className="text-sm italic text-gray-600 dark:text-gray-300 mt-2 overflow-hidden transition-all duration-300 ease-in-out"
style={{
maxHeight: isExpanded ? "0px" : "3em",
opacity: isExpanded ? 0 : 1,
}}
>
{query}
</div>
{/* Expanded content */}
<div
className="overflow-hidden transition-all duration-300 ease-in-out"
style={{
maxHeight: isExpanded ? "none" : "0px",
opacity: isExpanded ? 1 : 0,
marginTop: isExpanded ? "0.5rem" : "0",
}}
>
<div className="text-sm text-gray-600 dark:text-gray-300 space-y-2">
</DyadCardHeader>
<DyadCardContent isExpanded={isExpanded}>
<div className="text-sm text-muted-foreground space-y-2">
{query && (
<div>
<span className="text-xs font-medium text-gray-500 dark:text-gray-400">
<span className="text-xs font-medium text-muted-foreground">
Query:
</span>
<div className="italic mt-0.5">{query}</div>
<div className="italic mt-0.5 text-foreground">{query}</div>
</div>
)}
{children && (
<div>
<span className="text-xs font-medium text-gray-500 dark:text-gray-400">
<span className="text-xs font-medium text-muted-foreground">
Results:
</span>
<div className="mt-0.5 whitespace-pre-wrap font-mono text-xs">
<div className="mt-0.5 whitespace-pre-wrap font-mono text-xs text-foreground">
{children}
</div>
</div>
)}
</div>
</div>
</div>
</DyadCardContent>
</DyadCard>
);
};
import React, { useState, useMemo } from "react";
import { ChevronDown, ChevronUp, FileCode, FileText } from "lucide-react";
import { FileCode, FileText } from "lucide-react";
import {
DyadCard,
DyadCardHeader,
DyadBadge,
DyadExpandIcon,
DyadCardContent,
} from "./DyadCardPrimitives";
interface DyadCodeSearchResultProps {
node?: any;
......@@ -11,7 +18,6 @@ export const DyadCodeSearchResult: React.FC<DyadCodeSearchResultProps> = ({
}) => {
const [isExpanded, setIsExpanded] = useState(false);
// Parse file paths from children content
const files = useMemo(() => {
if (typeof children !== "string") {
return [];
......@@ -22,7 +28,6 @@ export const DyadCodeSearchResult: React.FC<DyadCodeSearchResultProps> = ({
for (const line of lines) {
const trimmedLine = line.trim();
// Skip empty lines and lines that look like tags
if (
trimmedLine &&
!trimmedLine.startsWith("<") &&
......@@ -36,78 +41,44 @@ export const DyadCodeSearchResult: React.FC<DyadCodeSearchResultProps> = ({
}, [children]);
return (
<div
className="relative bg-(--background-lightest) dark:bg-zinc-900 hover:bg-(--background-lighter) rounded-lg px-4 py-2 border border-border my-2 cursor-pointer"
<DyadCard
accentColor="indigo"
isExpanded={isExpanded}
onClick={() => setIsExpanded(!isExpanded)}
role="button"
aria-expanded={isExpanded}
tabIndex={0}
onKeyDown={(e) => {
if (e.key === "Enter" || e.key === " ") {
e.preventDefault();
setIsExpanded(!isExpanded);
}
}}
>
{/* Top-left label badge */}
<div
className="absolute top-2 left-2 flex items-center gap-1 px-2 py-0.5 rounded text-xs font-semibold text-purple-600 bg-white dark:bg-zinc-900"
style={{ zIndex: 1 }}
>
<FileCode size={16} className="text-purple-600" />
<span>Code Search Result</span>
</div>
{/* File count when collapsed */}
<DyadCardHeader icon={<FileCode size={15} />} accentColor="indigo">
<DyadBadge color="indigo">Code Search Result</DyadBadge>
{files.length > 0 && (
<div className="absolute top-2 left-44 flex items-center">
<span className="px-1.5 py-0.5 bg-gray-100 dark:bg-zinc-800 text-xs rounded text-gray-600 dark:text-gray-300">
<span className="text-xs text-muted-foreground">
Found {files.length} file{files.length !== 1 ? "s" : ""}
</span>
</div>
)}
{/* Indicator icon */}
<div className="absolute top-2 right-2 p-1 text-gray-500">
{isExpanded ? <ChevronUp size={16} /> : <ChevronDown size={16} />}
<div className="ml-auto">
<DyadExpandIcon isExpanded={isExpanded} />
</div>
{/* Main content with smooth transition */}
<div
className="pt-6 overflow-hidden transition-all duration-300 ease-in-out"
style={{
maxHeight: isExpanded ? "1000px" : "0px",
opacity: isExpanded ? 1 : 0,
marginBottom: isExpanded ? "0" : "-6px",
}}
>
{/* File list when expanded */}
</DyadCardHeader>
<DyadCardContent isExpanded={isExpanded}>
{files.length > 0 && (
<div className="mb-3">
<div className="flex flex-wrap gap-2 mt-2">
<div className="flex flex-wrap gap-1.5">
{files.map((file, index) => {
const filePath = file.trim();
const fileName = filePath.split("/").pop() || filePath;
const pathPart =
filePath.substring(0, filePath.length - fileName.length) ||
"";
filePath.substring(0, filePath.length - fileName.length) || "";
return (
<div
key={index}
className="px-2 py-1 bg-gray-100 dark:bg-zinc-800 rounded-lg"
>
<div key={index} className="px-2 py-1 bg-muted/40 rounded-lg">
<div className="flex items-center gap-1.5">
<FileText
size={14}
className="text-gray-500 dark:text-gray-400 flex-shrink-0"
size={13}
className="text-muted-foreground shrink-0"
/>
<div className="text-sm font-medium text-gray-700 dark:text-gray-300">
<span className="text-sm font-medium text-foreground">
{fileName}
</div>
</span>
</div>
{pathPart && (
<div className="text-xs text-gray-500 dark:text-gray-400 ml-5">
<div className="text-[11px] text-muted-foreground ml-5 font-mono">
{pathPart}
</div>
)}
......@@ -115,9 +86,8 @@ export const DyadCodeSearchResult: React.FC<DyadCodeSearchResultProps> = ({
);
})}
</div>
</div>
)}
</div>
</div>
</DyadCardContent>
</DyadCard>
);
};
import React, { useState, useEffect } from "react";
import { ChevronUp, ChevronDown, Code2, FileText } from "lucide-react";
import { Code2, FileText } from "lucide-react";
import { CustomTagState } from "./stateTypes";
import {
DyadCard,
DyadCardHeader,
DyadBadge,
DyadExpandIcon,
DyadStateIndicator,
DyadCardContent,
} from "./DyadCardPrimitives";
interface DyadCodebaseContextProps {
children: React.ReactNode;
......@@ -20,7 +28,6 @@ export const DyadCodebaseContext: React.FC<DyadCodebaseContextProps> = ({
const [isExpanded, setIsExpanded] = useState(inProgress);
const files = node?.properties?.files?.split(",") || [];
// Collapse when transitioning from in-progress to not-in-progress
useEffect(() => {
if (!inProgress && isExpanded) {
setIsExpanded(false);
......@@ -28,80 +35,46 @@ export const DyadCodebaseContext: React.FC<DyadCodebaseContextProps> = ({
}, [inProgress]);
return (
<div
className={`relative bg-(--background-lightest) dark:bg-zinc-900 hover:bg-(--background-lighter) rounded-lg px-4 py-2 border my-2 cursor-pointer ${
inProgress ? "border-blue-500" : "border-border"
}`}
<DyadCard
state={state}
accentColor="blue"
onClick={() => setIsExpanded(!isExpanded)}
role="button"
aria-expanded={isExpanded}
tabIndex={0}
onKeyDown={(e) => {
if (e.key === "Enter" || e.key === " ") {
e.preventDefault();
setIsExpanded(!isExpanded);
}
}}
>
{/* Top-left label badge */}
<div
className="absolute top-2 left-2 flex items-center gap-1 px-2 py-0.5 rounded text-xs font-semibold text-blue-500 bg-white dark:bg-zinc-900"
style={{ zIndex: 1 }}
isExpanded={isExpanded}
>
<Code2 size={16} className="text-blue-500" />
<span>Codebase Context</span>
</div>
{/* File count when collapsed */}
<DyadCardHeader icon={<Code2 size={15} />} accentColor="blue">
<DyadBadge color="blue">Codebase Context</DyadBadge>
{files.length > 0 && (
<div className="absolute top-2 left-40 flex items-center">
<span className="px-1.5 py-0.5 bg-gray-100 dark:bg-zinc-800 text-xs rounded text-gray-600 dark:text-gray-300">
<span className="text-xs text-muted-foreground">
Using {files.length} file{files.length !== 1 ? "s" : ""}
</span>
</div>
)}
{/* Indicator icon */}
<div className="absolute top-2 right-2 p-1 text-gray-500">
{isExpanded ? <ChevronUp size={16} /> : <ChevronDown size={16} />}
{inProgress && <DyadStateIndicator state="pending" />}
<div className="ml-auto">
<DyadExpandIcon isExpanded={isExpanded} />
</div>
{/* Main content with smooth transition */}
<div
className="pt-6 overflow-hidden transition-all duration-300 ease-in-out"
style={{
maxHeight: isExpanded ? "1000px" : "0px",
opacity: isExpanded ? 1 : 0,
marginBottom: isExpanded ? "0" : "-6px", // Compensate for padding
}}
>
{/* File list when expanded */}
</DyadCardHeader>
<DyadCardContent isExpanded={isExpanded}>
{files.length > 0 && (
<div className="mb-3">
<div className="flex flex-wrap gap-2 mt-2">
<div className="flex flex-wrap gap-1.5">
{files.map((file, index) => {
const filePath = file.trim();
const fileName = filePath.split("/").pop() || filePath;
const pathPart =
filePath.substring(0, filePath.length - fileName.length) ||
"";
filePath.substring(0, filePath.length - fileName.length) || "";
return (
<div
key={index}
className="px-2 py-1 bg-gray-100 dark:bg-zinc-800 rounded-lg"
>
<div key={index} className="px-2 py-1 bg-muted/40 rounded-lg">
<div className="flex items-center gap-1.5">
<FileText
size={14}
className="text-gray-500 dark:text-gray-400 flex-shrink-0"
size={13}
className="text-muted-foreground shrink-0"
/>
<div className="text-sm font-medium text-gray-700 dark:text-gray-300">
<span className="text-sm font-medium text-foreground">
{fileName}
</div>
</span>
</div>
{pathPart && (
<div className="text-xs text-gray-500 dark:text-gray-400 ml-5">
<div className="text-[11px] text-muted-foreground ml-5 font-mono">
{pathPart}
</div>
)}
......@@ -109,9 +82,8 @@ export const DyadCodebaseContext: React.FC<DyadCodebaseContextProps> = ({
);
})}
</div>
</div>
)}
</div>
</div>
</DyadCardContent>
</DyadCard>
);
};
import React from "react";
import { CustomTagState } from "./stateTypes";
import { Database, Loader2 } from "lucide-react";
import { Database } from "lucide-react";
import {
DyadCard,
DyadCardHeader,
DyadBadge,
DyadStateIndicator,
} from "./DyadCardPrimitives";
interface DyadDatabaseSchemaProps {
node: {
......@@ -20,20 +26,18 @@ export function DyadDatabaseSchema({
const content = typeof children === "string" ? children : "";
return (
<div className="my-2 border rounded-md overflow-hidden">
<div className="flex items-center gap-2 px-3 py-2 bg-muted/50 border-b">
{isLoading ? (
<Loader2 className="size-4 animate-spin text-muted-foreground" />
) : (
<Database className="size-4 text-muted-foreground" />
)}
<span className="font-medium text-sm">Database Schema</span>
</div>
<DyadCard state={state} accentColor="teal">
<DyadCardHeader icon={<Database size={15} />} accentColor="teal">
<DyadBadge color="teal">Database Schema</DyadBadge>
{isLoading && <DyadStateIndicator state="pending" />}
</DyadCardHeader>
{content && (
<div className="p-3 text-xs font-mono whitespace-pre-wrap max-h-60 overflow-y-auto bg-muted/20">
<div className="px-3 pb-3">
<div className="p-3 text-xs font-mono whitespace-pre-wrap max-h-60 overflow-y-auto bg-muted/20 rounded-lg">
{content}
</div>
)}
</div>
)}
</DyadCard>
);
}
import type React from "react";
import type { ReactNode } from "react";
import { Trash2 } from "lucide-react";
import {
DyadCard,
DyadCardHeader,
DyadBadge,
DyadFilePath,
DyadDescription,
} from "./DyadCardPrimitives";
import { CustomTagState } from "./stateTypes";
interface DyadDeleteProps {
children?: ReactNode;
......@@ -13,33 +21,22 @@ export const DyadDelete: React.FC<DyadDeleteProps> = ({
node,
path: pathProp,
}) => {
// Use props directly if provided, otherwise extract from node
const path = pathProp || node?.properties?.path || "";
// Extract filename from path
const state = node?.properties?.state as CustomTagState;
const fileName = path ? path.split("/").pop() : "";
return (
<div className="bg-(--background-lightest) rounded-lg px-4 py-2 border border-red-500 my-2">
<div className="flex items-center justify-between">
<div className="flex items-center gap-2">
<Trash2 size={16} className="text-red-500" />
<DyadCard accentColor="red" state={state}>
<DyadCardHeader icon={<Trash2 size={15} />} accentColor="red">
{fileName && (
<span className="text-gray-700 dark:text-gray-300 font-medium text-sm">
<span className="font-medium text-sm text-foreground truncate">
{fileName}
</span>
)}
<div className="text-xs text-red-500 font-medium">Delete</div>
</div>
</div>
{path && (
<div className="text-xs text-gray-500 dark:text-gray-400 font-medium mb-1">
{path}
</div>
)}
<div className="text-sm text-gray-600 dark:text-gray-300 mt-2">
{children}
</div>
</div>
<DyadBadge color="red">Delete</DyadBadge>
</DyadCardHeader>
<DyadFilePath path={path} />
{children && <DyadDescription>{children}</DyadDescription>}
</DyadCard>
);
};
import type React from "react";
import type { ReactNode } from "react";
import { useState } from "react";
import {
ChevronsDownUp,
ChevronsUpDown,
Loader,
CircleX,
Rabbit,
} from "lucide-react";
import { Zap } from "lucide-react";
import { CodeHighlight } from "./CodeHighlight";
import { CustomTagState } from "./stateTypes";
import {
DyadCard,
DyadCardHeader,
DyadBadge,
DyadExpandIcon,
DyadStateIndicator,
DyadDescription,
DyadCardContent,
} from "./DyadCardPrimitives";
interface DyadEditProps {
children?: ReactNode;
......@@ -26,79 +29,54 @@ export const DyadEdit: React.FC<DyadEditProps> = ({
}) => {
const [isContentVisible, setIsContentVisible] = useState(false);
// Use props directly if provided, otherwise extract from node
const path = pathProp || node?.properties?.path || "";
const description = descriptionProp || node?.properties?.description || "";
const state = node?.properties?.state as CustomTagState;
const inProgress = state === "pending";
const aborted = state === "aborted";
// Extract filename from path
const fileName = path ? path.split("/").pop() : "";
return (
<div
className={`bg-(--background-lightest) hover:bg-(--background-lighter) rounded-lg px-4 py-2 border my-2 cursor-pointer ${
inProgress
? "border-amber-500"
: aborted
? "border-red-500"
: "border-border"
}`}
<DyadCard
state={state}
accentColor="sky"
onClick={() => setIsContentVisible(!isContentVisible)}
isExpanded={isContentVisible}
>
<div className="flex items-center justify-between">
<div className="flex items-center gap-2">
<div className="flex items-center">
<Rabbit size={16} />
<span className="bg-blue-500 text-white text-xs px-1.5 py-0.5 rounded ml-1 font-medium">
Turbo Edit
</span>
</div>
<DyadCardHeader icon={<Zap size={15} />} accentColor="sky">
<div className="min-w-0 truncate">
{fileName && (
<span className="text-gray-700 dark:text-gray-300 font-medium text-sm">
<span className="font-medium text-sm text-foreground truncate block">
{fileName}
</span>
)}
{inProgress && (
<div className="flex items-center text-amber-600 text-xs">
<Loader size={14} className="mr-1 animate-spin" />
<span>Editing...</span>
</div>
{path && (
<span className="text-[11px] text-muted-foreground truncate block">
{path}
</span>
)}
{aborted && (
<div className="flex items-center text-red-600 text-xs">
<CircleX size={14} className="mr-1" />
<span>Did not finish</span>
</div>
{inProgress && (
<DyadStateIndicator state="pending" pendingLabel="Editing..." />
)}
</div>
<div className="flex items-center">
{isContentVisible ? (
<ChevronsDownUp
size={20}
className="text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200"
/>
) : (
<ChevronsUpDown
size={20}
className="text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200"
/>
{aborted && (
<DyadStateIndicator state="aborted" abortedLabel="Did not finish" />
)}
<div className="ml-auto flex items-center gap-1">
<DyadBadge color="sky">Turbo Edit</DyadBadge>
<DyadExpandIcon isExpanded={isContentVisible} />
</div>
</div>
{path && (
<div className="text-xs text-gray-500 dark:text-gray-400 font-medium mb-1">
{path}
</div>
)}
</DyadCardHeader>
{description && (
<div className="text-sm text-gray-600 dark:text-gray-300">
<DyadDescription>
<span className={!isContentVisible ? "line-clamp-2" : undefined}>
<span className="font-medium">Summary: </span>
{description}
</div>
</span>
</DyadDescription>
)}
{isContentVisible && (
<DyadCardContent isExpanded={isContentVisible}>
<div
className="text-xs cursor-text"
onClick={(e) => e.stopPropagation()}
......@@ -107,7 +85,7 @@ export const DyadEdit: React.FC<DyadEditProps> = ({
{children}
</CodeHighlight>
</div>
)}
</div>
</DyadCardContent>
</DyadCard>
);
};
import type React from "react";
import type { ReactNode } from "react";
import { useState } from "react";
import {
ChevronsDownUp,
ChevronsUpDown,
Database,
Loader,
CircleX,
} from "lucide-react";
import { Database } from "lucide-react";
import { CodeHighlight } from "./CodeHighlight";
import { CustomTagState } from "./stateTypes";
import {
DyadCard,
DyadCardHeader,
DyadBadge,
DyadExpandIcon,
DyadStateIndicator,
DyadCardContent,
} from "./DyadCardPrimitives";
interface DyadExecuteSqlProps {
children?: ReactNode;
......@@ -29,57 +31,34 @@ export const DyadExecuteSql: React.FC<DyadExecuteSqlProps> = ({
const queryDescription = description || node?.properties?.description;
return (
<div
className={`bg-(--background-lightest) hover:bg-(--background-lighter) rounded-lg px-4 py-2 border my-2 cursor-pointer ${
inProgress
? "border-amber-500"
: aborted
? "border-red-500"
: "border-border"
}`}
<DyadCard
state={state}
accentColor="teal"
isExpanded={isContentVisible}
onClick={() => setIsContentVisible(!isContentVisible)}
>
<div className="flex items-center justify-between">
<div className="flex items-center gap-2">
<Database size={16} />
<span className="text-gray-700 dark:text-gray-300 font-medium text-sm">
<span className="font-bold mr-2 outline-2 outline-gray-200 dark:outline-gray-700 bg-gray-100 dark:bg-gray-800 rounded-md px-1">
SQL
</span>
<DyadCardHeader icon={<Database size={15} />} accentColor="teal">
<DyadBadge color="teal">SQL</DyadBadge>
{queryDescription && (
<span className="font-medium text-sm text-foreground truncate">
{queryDescription}
</span>
)}
{inProgress && (
<div className="flex items-center text-amber-600 text-xs">
<Loader size={14} className="mr-1 animate-spin" />
<span>Executing...</span>
</div>
<DyadStateIndicator state="pending" pendingLabel="Executing..." />
)}
{aborted && (
<div className="flex items-center text-red-600 text-xs">
<CircleX size={14} className="mr-1" />
<span>Did not finish</span>
</div>
<DyadStateIndicator state="aborted" abortedLabel="Did not finish" />
)}
<div className="ml-auto">
<DyadExpandIcon isExpanded={isContentVisible} />
</div>
<div className="flex items-center">
{isContentVisible ? (
<ChevronsDownUp
size={20}
className="text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200"
/>
) : (
<ChevronsUpDown
size={20}
className="text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200"
/>
)}
</div>
</div>
{isContentVisible && (
</DyadCardHeader>
<DyadCardContent isExpanded={isContentVisible}>
<div className="text-xs">
<CodeHighlight className="language-sql">{children}</CodeHighlight>
</div>
)}
</div>
</DyadCardContent>
</DyadCard>
);
};
import type React from "react";
import type { ReactNode } from "react";
import { useState } from "react";
import {
ChevronsDownUp,
ChevronsUpDown,
Search,
Loader,
CircleX,
} from "lucide-react";
import { Search } from "lucide-react";
import { CodeHighlight } from "./CodeHighlight";
import { CustomTagState } from "./stateTypes";
import {
DyadCard,
DyadCardHeader,
DyadBadge,
DyadExpandIcon,
DyadStateIndicator,
DyadCardContent,
} from "./DyadCardPrimitives";
interface DyadGrepProps {
children?: ReactNode;
......@@ -30,12 +32,10 @@ interface DyadGrepProps {
export const DyadGrep: React.FC<DyadGrepProps> = ({ children, node }) => {
const [isContentVisible, setIsContentVisible] = useState(false);
// State handling
const state = node?.properties?.state as CustomTagState;
const inProgress = state === "pending";
const aborted = state === "aborted";
// Get properties from node
const query = node?.properties?.query || "";
const includePattern = node?.properties?.include || "";
const excludePattern = node?.properties?.exclude || "";
......@@ -43,9 +43,7 @@ export const DyadGrep: React.FC<DyadGrepProps> = ({ children, node }) => {
const count = node?.properties?.count || "";
const total = node?.properties?.total || "";
const truncated = node?.properties?.truncated === "true";
const hasResults = count !== "" && count !== "0";
// Build description
let description = `"${query}"`;
if (includePattern) {
description += ` in ${includePattern}`;
......@@ -57,70 +55,45 @@ export const DyadGrep: React.FC<DyadGrepProps> = ({ children, node }) => {
description += " (case-sensitive)";
}
// Build result summary
const resultSummary = count
? truncated && total
? `${count} of ${total} matches`
: `${count} match${count === "1" ? "" : "es"}`
: "";
// Dynamic border styling
const borderClass = inProgress
? "border-(--primary)"
: aborted
? "border-red-500"
: "border-(--primary)/30";
return (
<div
data-testid="dyad-grep"
className={`bg-(--background-lightest) hover:bg-(--background-lighter) rounded-lg px-4 py-2 border my-2 cursor-pointer ${borderClass}`}
<DyadCard
state={state}
accentColor="violet"
onClick={() => setIsContentVisible(!isContentVisible)}
isExpanded={isContentVisible}
data-testid="dyad-grep"
>
<div className="flex items-center justify-between">
<div className="flex items-center gap-2">
<Search size={16} className="text-(--primary)" />
<span className="text-gray-700 dark:text-gray-300 font-medium text-sm">
<span className="font-bold mr-2 outline-2 outline-(--primary)/20 bg-(--primary)/10 text-(--primary) rounded-md px-1">
GREP
</span>
<DyadCardHeader icon={<Search size={15} />} accentColor="violet">
<DyadBadge color="violet">GREP</DyadBadge>
<span className="font-medium text-sm text-foreground truncate">
{description}
</span>
{resultSummary && (
<span className="ml-2 text-gray-500">({resultSummary})</span>
)}
<span className="text-xs text-muted-foreground shrink-0">
({resultSummary})
</span>
)}
{inProgress && (
<div className="flex items-center text-(--primary) text-xs">
<Loader size={14} className="mr-1 animate-spin" />
<span>Searching...</span>
</div>
<DyadStateIndicator state="pending" pendingLabel="Searching..." />
)}
{aborted && (
<div className="flex items-center text-red-600 text-xs">
<CircleX size={14} className="mr-1" />
<span>Did not finish</span>
</div>
)}
</div>
<div className="flex items-center">
{isContentVisible ? (
<ChevronsDownUp
size={20}
className="text-(--primary)/70 hover:text-(--primary)"
/>
) : (
<ChevronsUpDown
size={20}
className="text-(--primary)/70 hover:text-(--primary)"
/>
<DyadStateIndicator state="aborted" abortedLabel="Did not finish" />
)}
<div className="ml-auto">
<DyadExpandIcon isExpanded={isContentVisible} />
</div>
</div>
{isContentVisible && (
<div className={`text-xs${hasResults ? " mt-2" : ""}`}>
</DyadCardHeader>
<DyadCardContent isExpanded={isContentVisible}>
<div className="text-xs" onClick={(e) => e.stopPropagation()}>
<CodeHighlight className="language-log">{children}</CodeHighlight>
</div>
)}
</div>
</DyadCardContent>
</DyadCard>
);
};
import React, { useState } from "react";
import { CustomTagState } from "./stateTypes";
import { ChevronRight, FolderOpen, Loader2 } from "lucide-react";
import { FolderOpen } from "lucide-react";
import {
DyadCard,
DyadCardHeader,
DyadBadge,
DyadExpandIcon,
DyadStateIndicator,
DyadCardContent,
} from "./DyadCardPrimitives";
interface DyadListFilesProps {
node: {
......@@ -22,45 +30,36 @@ export function DyadListFiles({ node, children }: DyadListFilesProps) {
const content = typeof children === "string" ? children : "";
const [isExpanded, setIsExpanded] = useState(false);
const getTitle = () => {
const parts: string[] = ["List Files"];
if (directory) {
parts[0] = `List Files: ${directory}`;
}
if (isRecursive) {
parts.push("(recursive)");
}
if (isIncludeHidden) {
parts.push("(include hidden)");
}
return parts.join(" ");
};
const title = directory ? directory : "List Files";
return (
<div
data-testid="dyad-list-files"
className="my-2 border rounded-md overflow-hidden"
>
<button
type="button"
<DyadCard
state={state}
accentColor="slate"
isExpanded={isExpanded}
onClick={() => setIsExpanded(!isExpanded)}
className="flex items-center gap-2 px-3 py-2 bg-muted/50 w-full text-left hover:bg-muted/70 transition-colors"
data-testid="dyad-list-files"
>
<ChevronRight
className={`size-4 text-muted-foreground transition-transform ${isExpanded ? "rotate-90" : ""}`}
/>
{isLoading ? (
<Loader2 className="size-4 animate-spin text-muted-foreground" />
) : (
<FolderOpen className="size-4 text-muted-foreground" />
<DyadCardHeader icon={<FolderOpen size={15} />} accentColor="slate">
<span className="font-medium text-sm text-foreground truncate">
{title}
</span>
{isRecursive && <DyadBadge color="slate">recursive</DyadBadge>}
{isIncludeHidden && <DyadBadge color="slate">include hidden</DyadBadge>}
{isLoading && (
<DyadStateIndicator state="pending" pendingLabel="Listing..." />
)}
<span className="font-medium text-sm">{getTitle()}</span>
</button>
{isExpanded && content && (
<div className="p-3 text-xs font-mono whitespace-pre-wrap max-h-60 overflow-y-auto bg-muted/20 border-t">
<div className="ml-auto">
<DyadExpandIcon isExpanded={isExpanded} />
</div>
</DyadCardHeader>
<DyadCardContent isExpanded={isExpanded}>
{content && (
<div className="p-3 text-xs font-mono whitespace-pre-wrap max-h-60 overflow-y-auto bg-muted/20 rounded-lg">
{content}
</div>
)}
</div>
</DyadCardContent>
</DyadCard>
);
}
import type React from "react";
import type { ReactNode } from "react";
import { useState } from "react";
import {
ChevronsDownUp,
ChevronsUpDown,
FileText,
Loader,
CircleX,
} from "lucide-react";
import { FileText } from "lucide-react";
import { CodeHighlight } from "./CodeHighlight";
import { CustomTagState } from "./stateTypes";
import {
DyadCard,
DyadCardHeader,
DyadBadge,
DyadExpandIcon,
DyadStateIndicator,
DyadCardContent,
} from "./DyadCardPrimitives";
interface DyadLogsProps {
children?: ReactNode;
......@@ -19,16 +21,13 @@ interface DyadLogsProps {
export const DyadLogs: React.FC<DyadLogsProps> = ({ children, node }) => {
const [isContentVisible, setIsContentVisible] = useState(false);
// State handling
const state = node?.properties?.state as CustomTagState;
const inProgress = state === "pending";
const aborted = state === "aborted";
// Get count from node properties
const logCount = node?.properties?.count || "";
const hasResults = !!logCount;
// Build description based on filters
const logType = node?.properties?.type || "all";
const logLevel = node?.properties?.level || "all";
const filters: string[] = [];
......@@ -36,62 +35,35 @@ export const DyadLogs: React.FC<DyadLogsProps> = ({ children, node }) => {
if (logLevel !== "all") filters.push(`level: ${logLevel}`);
const filterDesc = filters.length > 0 ? ` (${filters.join(", ")})` : "";
// Build display text
const displayText = `Reading ${hasResults ? `${logCount} ` : ""}logs${filterDesc}`;
// Dynamic border styling
const borderClass = inProgress
? "border-(--primary)"
: aborted
? "border-red-500"
: "border-(--primary)/30";
return (
<div
className={`bg-(--background-lightest) hover:bg-(--background-lighter) rounded-lg px-4 py-2 border my-2 cursor-pointer ${borderClass}`}
<DyadCard
state={state}
accentColor="slate"
isExpanded={isContentVisible}
onClick={() => setIsContentVisible(!isContentVisible)}
>
<div className="flex items-center justify-between">
<div className="flex items-center gap-2">
<FileText size={16} className="text-(--primary)" />
<span className="text-gray-700 dark:text-gray-300 font-medium text-sm">
<span className="font-bold mr-2 outline-2 outline-(--primary)/20 bg-(--primary)/10 text-(--primary) rounded-md px-1">
LOGS
</span>
<DyadCardHeader icon={<FileText size={15} />} accentColor="slate">
<DyadBadge color="slate">LOGS</DyadBadge>
<span className="font-medium text-sm text-foreground truncate">
{displayText}
</span>
{inProgress && (
<div className="flex items-center text-(--primary) text-xs">
<Loader size={14} className="mr-1 animate-spin" />
<span>Reading...</span>
</div>
<DyadStateIndicator state="pending" pendingLabel="Reading..." />
)}
{aborted && (
<div className="flex items-center text-red-600 text-xs">
<CircleX size={14} className="mr-1" />
<span>Did not finish</span>
</div>
<DyadStateIndicator state="aborted" abortedLabel="Did not finish" />
)}
<div className="ml-auto">
<DyadExpandIcon isExpanded={isContentVisible} />
</div>
<div className="flex items-center">
{isContentVisible ? (
<ChevronsDownUp
size={20}
className="text-(--primary)/70 hover:text-(--primary)"
/>
) : (
<ChevronsUpDown
size={20}
className="text-(--primary)/70 hover:text-(--primary)"
/>
)}
</div>
</div>
{isContentVisible && (
<div className={`text-xs${hasResults ? " mt-2" : ""}`}>
</DyadCardHeader>
<DyadCardContent isExpanded={isContentVisible}>
<div className="text-xs">
<CodeHighlight className="language-log">{children}</CodeHighlight>
</div>
)}
</div>
</DyadCardContent>
</DyadCard>
);
};
import React, { useMemo, useState } from "react";
import { Wrench, ChevronsUpDown, ChevronsDownUp } from "lucide-react";
import { Wrench } from "lucide-react";
import { CodeHighlight } from "./CodeHighlight";
import {
DyadCard,
DyadCardHeader,
DyadBadge,
DyadExpandIcon,
DyadCardContent,
} from "./DyadCardPrimitives";
interface DyadMcpToolCallProps {
node?: any;
......@@ -29,45 +36,30 @@ export const DyadMcpToolCall: React.FC<DyadMcpToolCallProps> = ({
}, [expanded, raw]);
return (
<div
className="relative bg-(--background-lightest) hover:bg-(--background-lighter) rounded-lg px-4 py-2 border my-2 cursor-pointer"
<DyadCard
accentColor="blue"
isExpanded={expanded}
onClick={() => setExpanded((v) => !v)}
>
{/* Top-left label badge */}
<div
className="absolute top-3 left-2 flex items-center gap-1 px-2 py-0.5 rounded text-xs font-semibold text-blue-600 bg-white dark:bg-zinc-900"
style={{ zIndex: 1 }}
>
<Wrench size={16} className="text-blue-600" />
<span>Tool Call</span>
</div>
{/* Right chevron */}
<div className="absolute top-2 right-2 p-1 text-gray-500">
{expanded ? <ChevronsDownUp size={18} /> : <ChevronsUpDown size={18} />}
</div>
{/* Header content */}
<div className="flex items-start gap-2 pl-24 pr-8 py-1">
{serverName ? (
<span className="text-xs px-2 py-0.5 rounded-full bg-blue-50 dark:bg-zinc-800 text-blue-700 dark:text-blue-300 border border-blue-200 dark:border-zinc-700">
<DyadCardHeader icon={<Wrench size={15} />} accentColor="blue">
<DyadBadge color="blue">Tool Call</DyadBadge>
{serverName && (
<span className="text-xs px-2 py-0.5 rounded-full bg-blue-50 dark:bg-blue-950/40 text-blue-700 dark:text-blue-300 ring-1 ring-inset ring-blue-200 dark:ring-blue-800">
{serverName}
</span>
) : null}
{toolName ? (
<span className="text-xs px-2 py-0.5 rounded-full bg-gray-100 dark:bg-zinc-800 text-gray-700 dark:text-gray-200 border border-border">
)}
{toolName && (
<span className="text-xs px-2 py-0.5 rounded-full bg-muted/50 text-muted-foreground ring-1 ring-inset ring-border">
{toolName}
</span>
) : null}
{/* Intentionally no preview or content when collapsed */}
)}
<div className="ml-auto">
<DyadExpandIcon isExpanded={expanded} />
</div>
{/* JSON content */}
{expanded ? (
<div className="mt-2 pr-4 pb-2">
</DyadCardHeader>
<DyadCardContent isExpanded={expanded}>
<CodeHighlight className="language-json">{prettyJson}</CodeHighlight>
</div>
) : null}
</div>
</DyadCardContent>
</DyadCard>
);
};
import React, { useMemo, useState } from "react";
import { CheckCircle, ChevronsUpDown, ChevronsDownUp } from "lucide-react";
import { CheckCircle } from "lucide-react";
import { CodeHighlight } from "./CodeHighlight";
import {
DyadCard,
DyadCardHeader,
DyadBadge,
DyadExpandIcon,
DyadCardContent,
} from "./DyadCardPrimitives";
interface DyadMcpToolResultProps {
node?: any;
......@@ -29,45 +36,30 @@ export const DyadMcpToolResult: React.FC<DyadMcpToolResultProps> = ({
}, [expanded, raw]);
return (
<div
className="relative bg-(--background-lightest) hover:bg-(--background-lighter) rounded-lg px-4 py-2 border my-2 cursor-pointer"
<DyadCard
accentColor="emerald"
isExpanded={expanded}
onClick={() => setExpanded((v) => !v)}
>
{/* Top-left label badge */}
<div
className="absolute top-3 left-2 flex items-center gap-1 px-2 py-0.5 rounded text-xs font-semibold text-emerald-600 bg-white dark:bg-zinc-900"
style={{ zIndex: 1 }}
>
<CheckCircle size={16} className="text-emerald-600" />
<span>Tool Result</span>
</div>
{/* Right chevron */}
<div className="absolute top-2 right-2 p-1 text-gray-500">
{expanded ? <ChevronsDownUp size={18} /> : <ChevronsUpDown size={18} />}
</div>
{/* Header content */}
<div className="flex items-start gap-2 pl-24 pr-8 py-1">
{serverName ? (
<span className="text-xs px-2 py-0.5 rounded-full bg-emerald-50 dark:bg-zinc-800 text-emerald-700 dark:text-emerald-300 border border-emerald-200 dark:border-zinc-700">
<DyadCardHeader icon={<CheckCircle size={15} />} accentColor="emerald">
<DyadBadge color="emerald">Tool Result</DyadBadge>
{serverName && (
<span className="text-xs px-2 py-0.5 rounded-full bg-emerald-50 dark:bg-emerald-950/40 text-emerald-700 dark:text-emerald-300 ring-1 ring-inset ring-emerald-200 dark:ring-emerald-800">
{serverName}
</span>
) : null}
{toolName ? (
<span className="text-xs px-2 py-0.5 rounded-full bg-gray-100 dark:bg-zinc-800 text-gray-700 dark:text-gray-200 border border-border">
)}
{toolName && (
<span className="text-xs px-2 py-0.5 rounded-full bg-muted/50 text-muted-foreground ring-1 ring-inset ring-border">
{toolName}
</span>
) : null}
{/* Intentionally no preview or content when collapsed */}
)}
<div className="ml-auto">
<DyadExpandIcon isExpanded={expanded} />
</div>
{/* JSON content */}
{expanded ? (
<div className="mt-2 pr-4 pb-2">
</DyadCardHeader>
<DyadCardContent isExpanded={expanded}>
<CodeHighlight className="language-json">{prettyJson}</CodeHighlight>
</div>
) : null}
</div>
</DyadCardContent>
</DyadCard>
);
};
import React, { useState } from "react";
import {
ChevronsDownUp,
ChevronsUpDown,
AlertTriangle,
XCircle,
Sparkles,
} from "lucide-react";
import { AlertTriangle, XCircle, Sparkles } from "lucide-react";
import { useAtomValue } from "jotai";
import { selectedChatIdAtom } from "@/atoms/chatAtoms";
import { useStreamChat } from "@/hooks/useStreamChat";
import { CopyErrorMessage } from "@/components/CopyErrorMessage";
import {
DyadCard,
DyadCardHeader,
DyadBadge,
DyadExpandIcon,
DyadCardContent,
} from "./DyadCardPrimitives";
interface DyadOutputProps {
type: "error" | "warning";
message?: string;
......@@ -27,13 +29,8 @@ export const DyadOutput: React.FC<DyadOutputProps> = ({
// If the type is not warning, it is an error (in case LLM gives a weird "type")
const isError = type !== "warning";
const borderColor = isError ? "border-red-500" : "border-amber-500";
const iconColor = isError ? "text-red-500" : "text-amber-500";
const icon = isError ? (
<XCircle size={16} className={iconColor} />
) : (
<AlertTriangle size={16} className={iconColor} />
);
const accentColor = isError ? "red" : "amber";
const icon = isError ? <XCircle size={15} /> : <AlertTriangle size={15} />;
const label = isError ? "Error" : "Warning";
const handleAIFix = (e: React.MouseEvent) => {
......@@ -47,66 +44,47 @@ export const DyadOutput: React.FC<DyadOutputProps> = ({
};
return (
<div
className={`relative bg-(--background-lightest) hover:bg-(--background-lighter) rounded-lg px-4 py-2 border my-2 cursor-pointer min-h-18 ${borderColor}`}
<DyadCard
showAccent
accentColor={accentColor}
onClick={() => setIsContentVisible(!isContentVisible)}
isExpanded={isContentVisible}
>
{/* Top-left label badge */}
<div
className={`absolute top-2 left-2 flex items-center gap-1 px-2 py-0.5 rounded text-xs font-semibold ${iconColor} bg-white dark:bg-gray-900`}
style={{ zIndex: 1 }}
>
{icon}
<span>{label}</span>
</div>
{/* Main content, padded to avoid label */}
<div className="flex items-center justify-between pl-24 pr-6">
<div className="flex items-center gap-2">
<DyadCardHeader icon={icon} accentColor={accentColor}>
<DyadBadge color={accentColor}>{label}</DyadBadge>
{message && (
<span className="text-gray-700 dark:text-gray-300 font-medium text-sm">
<span className="text-sm text-foreground truncate">
{message.slice(0, isContentVisible ? undefined : 100) +
(!isContentVisible ? "..." : "")}
(!isContentVisible && message.length > 100 ? "..." : "")}
</span>
)}
<div className="ml-auto">
<DyadExpandIcon isExpanded={isContentVisible} />
</div>
<div className="flex items-center">
{isContentVisible ? (
<ChevronsDownUp
size={20}
className="text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200"
/>
) : (
<ChevronsUpDown
size={20}
className="text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200"
/>
)}
</div>
</div>
</DyadCardHeader>
{/* Content area */}
{isContentVisible && children && (
<div className="mt-4 pl-20 text-sm text-gray-800 dark:text-gray-200">
{children}
</div>
<DyadCardContent isExpanded={isContentVisible}>
{children && (
<div className="text-sm text-muted-foreground mb-3">{children}</div>
)}
</DyadCardContent>
{/* Action buttons at the bottom - always visible for errors */}
{isError && message && (
<div className="mt-3 px-6 flex justify-end gap-2">
<div className="px-3 pb-2 flex justify-end gap-2">
<CopyErrorMessage
errorMessage={children ? `${message}\n${children}` : message}
/>
<button
onClick={handleAIFix}
className="cursor-pointer flex items-center justify-center bg-red-600 hover:bg-red-700 dark:bg-red-700 dark:hover:bg-red-800 text-white rounded text-xs px-2 py-1 h-6"
className="cursor-pointer flex items-center justify-center bg-red-600 hover:bg-red-700 dark:bg-red-700 dark:hover:bg-red-800 text-white rounded-md text-xs px-2.5 py-1 h-6 transition-colors"
>
<Sparkles size={14} className="mr-1" />
<Sparkles size={13} className="mr-1" />
<span>Fix with AI</span>
</button>
</div>
)}
</div>
</DyadCard>
);
};
import React, { useState } from "react";
import {
ChevronsDownUp,
ChevronsUpDown,
AlertTriangle,
FileText,
} from "lucide-react";
import { AlertTriangle, FileText } from "lucide-react";
import type { Problem } from "@/ipc/types";
import {
DyadCard,
DyadCardHeader,
DyadBadge,
DyadExpandIcon,
DyadCardContent,
} from "./DyadCardPrimitives";
type ProblemWithoutSnippet = Omit<Problem, "snippet">;
......@@ -21,27 +23,26 @@ interface ProblemItemProps {
const ProblemItem: React.FC<ProblemItemProps> = ({ problem, index }) => {
return (
<div className="flex items-start gap-3 py-2 px-3 border-b border-gray-200 dark:border-gray-700 last:border-b-0">
<div className="flex-shrink-0 w-6 h-6 rounded-full bg-gray-100 dark:bg-gray-800 flex items-center justify-center mt-0.5">
<span className="text-xs font-medium text-gray-600 dark:text-gray-400">
<div className="flex items-start gap-3 py-2 px-3 border-b border-border/40 last:border-b-0">
<div className="flex-shrink-0 size-6 rounded-full bg-muted/60 flex items-center justify-center mt-0.5">
<span className="text-[11px] font-semibold text-muted-foreground">
{index + 1}
</span>
</div>
<div className="flex-1 min-w-0">
<div className="flex items-center gap-2 mb-2">
<FileText size={14} className="text-gray-500 flex-shrink-0" />
<span className="text-sm font-medium text-gray-900 dark:text-gray-100 truncate">
<div className="flex items-center gap-2 mb-1.5">
<FileText size={13} className="text-muted-foreground shrink-0" />
<span className="text-sm font-medium text-foreground truncate">
{problem.file}
</span>
<span className="text-xs text-gray-500 dark:text-gray-400">
<span className="text-[11px] text-muted-foreground font-mono">
{problem.line}:{problem.column}
</span>
<span className="text-xs bg-gray-100 dark:bg-gray-800 px-2 py-0.5 rounded text-gray-600 dark:text-gray-300">
<span className="text-[11px] bg-muted/50 px-1.5 py-0.5 rounded text-muted-foreground font-mono">
TS{problem.code}
</span>
</div>
<p className="text-sm text-gray-700 dark:text-gray-300 leading-relaxed">
<p className="text-sm text-muted-foreground leading-relaxed">
{problem.message}
</p>
</div>
......@@ -59,7 +60,6 @@ export const DyadProblemSummary: React.FC<DyadProblemSummaryProps> = ({
const problems: ProblemWithoutSnippet[] = React.useMemo(() => {
if (!children || typeof children !== "string") return [];
// Parse structured format with <problem> tags
const problemTagRegex =
/<problem\s+file="([^"]+)"\s+line="(\d+)"\s+column="(\d+)"\s+code="(\d+)">([^<]+)<\/problem>/g;
const problems: ProblemWithoutSnippet[] = [];
......@@ -95,43 +95,26 @@ export const DyadProblemSummary: React.FC<DyadProblemSummaryProps> = ({
summary || `${totalProblems} problems found (TypeScript errors)`;
return (
<div
className="bg-(--background-lightest) hover:bg-(--background-lighter) rounded-lg px-4 py-2 border border-border my-2 cursor-pointer"
<DyadCard
accentColor="amber"
isExpanded={isContentVisible}
onClick={() => setIsContentVisible(!isContentVisible)}
data-testid="problem-summary"
>
<div className="flex items-center justify-between">
<div className="flex items-center gap-2">
<AlertTriangle
size={16}
className="text-amber-600 dark:text-amber-500"
/>
<span className="text-gray-700 dark:text-gray-300 font-medium text-sm">
<span className="font-bold mr-2 outline-2 outline-amber-200 dark:outline-amber-700 bg-amber-100 dark:bg-amber-900/30 text-amber-800 dark:text-amber-200 rounded-md px-1">
Auto-fix
</span>
<DyadCardHeader icon={<AlertTriangle size={15} />} accentColor="amber">
<DyadBadge color="amber">Auto-fix</DyadBadge>
<span className="font-medium text-sm text-foreground truncate">
{displaySummary}
</span>
<div className="ml-auto">
<DyadExpandIcon isExpanded={isContentVisible} />
</div>
<div className="flex items-center">
{isContentVisible ? (
<ChevronsDownUp
size={20}
className="text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200"
/>
) : (
<ChevronsUpDown
size={20}
className="text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200"
/>
)}
</div>
</div>
</DyadCardHeader>
{/* Content area - show individual problems */}
{isContentVisible && totalProblems > 0 && (
<div className="mt-4">
<div className="bg-white dark:bg-gray-800 rounded-lg border border-gray-200 dark:border-gray-700 overflow-hidden">
<DyadCardContent isExpanded={isContentVisible}>
{totalProblems > 0 ? (
<div className="bg-muted/20 rounded-lg border border-border/40 overflow-hidden">
{problems.map((problem, index) => (
<ProblemItem
key={`${problem.file}-${problem.line}-${problem.column}-${index}`}
......@@ -140,17 +123,14 @@ export const DyadProblemSummary: React.FC<DyadProblemSummaryProps> = ({
/>
))}
</div>
</div>
)}
{/* Fallback content area for raw children */}
{isContentVisible && totalProblems === 0 && children && (
<div className="mt-4 text-sm text-gray-800 dark:text-gray-200">
<pre className="whitespace-pre-wrap font-mono text-xs bg-gray-100 dark:bg-gray-800 p-3 rounded">
) : (
children && (
<pre className="whitespace-pre-wrap font-mono text-xs bg-muted/20 p-3 rounded-lg text-muted-foreground">
{children}
</pre>
</div>
)
)}
</div>
</DyadCardContent>
</DyadCard>
);
};
......@@ -15,29 +15,26 @@ export const DyadRead: React.FC<DyadReadProps> = ({
}) => {
const path = pathProp || node?.properties?.path || "";
const fileName = path ? path.split("/").pop() : "";
const dirPath = path
? path.slice(0, path.length - (fileName?.length || 0))
: "";
return (
<div className="bg-(--background-lightest) rounded-lg px-4 py-2 border border-border my-2">
<div className="flex items-center justify-between">
<div className="flex items-center gap-2">
<FileText size={16} className="text-gray-600" />
{fileName && (
<span className="text-gray-700 dark:text-gray-300 font-medium text-sm">
{fileName}
<div className="my-1">
<div className="flex items-center gap-1 py-1">
<FileText size={14} className="shrink-0 text-muted-foreground/50" />
<span className="text-[13px] font-medium text-foreground/70">Read</span>
{path && (
<span className="text-[13px] truncate min-w-0" title={path}>
{dirPath && (
<span className="text-muted-foreground/85">{dirPath}</span>
)}
<span className="font-medium text-foreground/70">{fileName}</span>
</span>
)}
<div className="text-xs text-gray-600 font-medium">Read</div>
</div>
</div>
{path && (
<div className="text-xs text-gray-500 dark:text-gray-400 font-medium mb-1">
{path}
</div>
)}
{children && (
<div className="text-sm text-gray-600 dark:text-gray-300 mt-2">
{children}
</div>
<div className="text-xs text-muted-foreground ml-5">{children}</div>
)}
</div>
);
......
import type React from "react";
import type { ReactNode } from "react";
import { FileEdit } from "lucide-react";
import {
DyadCard,
DyadCardHeader,
DyadBadge,
DyadFilePath,
DyadDescription,
} from "./DyadCardPrimitives";
import { CustomTagState } from "./stateTypes";
interface DyadRenameProps {
children?: ReactNode;
......@@ -15,47 +23,31 @@ export const DyadRename: React.FC<DyadRenameProps> = ({
from: fromProp,
to: toProp,
}) => {
// Use props directly if provided, otherwise extract from node
const from = fromProp || node?.properties?.from || "";
const to = toProp || node?.properties?.to || "";
const state = node?.properties?.state as CustomTagState;
// Extract filenames from paths
const fromFileName = from ? from.split("/").pop() : "";
const toFileName = to ? to.split("/").pop() : "";
return (
<div className="bg-(--background-lightest) rounded-lg px-4 py-2 border border-amber-500 my-2">
<div className="flex items-center justify-between">
<div className="flex items-center gap-2">
<FileEdit size={16} className="text-amber-500" />
{(fromFileName || toFileName) && (
<span className="text-gray-700 dark:text-gray-300 font-medium text-sm">
{fromFileName && toFileName
const displayTitle =
fromFileName && toFileName
? `${fromFileName}${toFileName}`
: fromFileName || toFileName}
: fromFileName || toFileName || "";
return (
<DyadCard accentColor="amber" state={state}>
<DyadCardHeader icon={<FileEdit size={15} />} accentColor="amber">
{displayTitle && (
<span className="font-medium text-sm text-foreground truncate">
{displayTitle}
</span>
)}
<div className="text-xs text-amber-500 font-medium">Rename</div>
</div>
</div>
{(from || to) && (
<div className="flex flex-col text-xs text-gray-500 dark:text-gray-400 font-medium mb-1">
{from && (
<div>
<span className="text-gray-500 dark:text-gray-400">From:</span>{" "}
{from}
</div>
)}
{to && (
<div>
<span className="text-gray-500 dark:text-gray-400">To:</span> {to}
</div>
)}
</div>
)}
<div className="text-sm text-gray-600 dark:text-gray-300 mt-2">
{children}
</div>
</div>
<DyadBadge color="amber">Rename</DyadBadge>
</DyadCardHeader>
{from && <DyadFilePath path={`From: ${from}`} />}
{to && <DyadFilePath path={`To: ${to}`} />}
{children && <DyadDescription>{children}</DyadDescription>}
</DyadCard>
);
};
import type React from "react";
import type { ReactNode } from "react";
import { useMemo, useState } from "react";
import {
ChevronsDownUp,
ChevronsUpDown,
Loader,
CircleX,
Search,
ArrowLeftRight,
} from "lucide-react";
import { Search, ArrowLeftRight } from "lucide-react";
import { CodeHighlight } from "./CodeHighlight";
import { CustomTagState } from "./stateTypes";
import { parseSearchReplaceBlocks } from "@/pro/shared/search_replace_parser";
import {
DyadCard,
DyadCardHeader,
DyadBadge,
DyadExpandIcon,
DyadStateIndicator,
DyadFilePath,
DyadDescription,
DyadCardContent,
} from "./DyadCardPrimitives";
interface DyadSearchReplaceProps {
children?: ReactNode;
......@@ -42,69 +45,41 @@ export const DyadSearchReplace: React.FC<DyadSearchReplaceProps> = ({
const fileName = path ? path.split("/").pop() : "";
return (
<div
data-testid="dyad-search-replace"
className={`bg-(--background-lightest) hover:bg-(--background-lighter) rounded-lg px-4 py-2 border my-2 cursor-pointer ${
inProgress
? "border-amber-500"
: aborted
? "border-red-500"
: "border-border"
}`}
<DyadCard
state={state}
accentColor="violet"
isExpanded={isContentVisible}
onClick={() => setIsContentVisible(!isContentVisible)}
data-testid="dyad-search-replace"
>
<div className="flex items-center justify-between">
<div className="flex items-center gap-2">
<div className="flex items-center">
<Search size={16} />
<span className="bg-purple-600 text-white text-xs px-1.5 py-0.5 rounded ml-1 font-medium">
Search & Replace
</span>
</div>
<DyadCardHeader icon={<Search size={15} />} accentColor="violet">
<DyadBadge color="violet">Search & Replace</DyadBadge>
{fileName && (
<span className="text-gray-700 dark:text-gray-300 font-medium text-sm">
<span className="font-medium text-sm text-foreground truncate">
{fileName}
</span>
)}
{inProgress && (
<div className="flex items-center text-amber-600 text-xs">
<Loader size={14} className="mr-1 animate-spin" />
<span>Applying changes...</span>
</div>
<DyadStateIndicator
state="pending"
pendingLabel="Applying changes..."
/>
)}
{aborted && (
<div className="flex items-center text-red-600 text-xs">
<CircleX size={14} className="mr-1" />
<span>Did not finish</span>
</div>
)}
</div>
<div className="flex items-center">
{isContentVisible ? (
<ChevronsDownUp
size={20}
className="text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200"
/>
) : (
<ChevronsUpDown
size={20}
className="text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200"
/>
<DyadStateIndicator state="aborted" abortedLabel="Did not finish" />
)}
<div className="ml-auto">
<DyadExpandIcon isExpanded={isContentVisible} />
</div>
</div>
{path && (
<div className="text-xs text-gray-500 dark:text-gray-400 font-medium mb-1">
{path}
</div>
)}
</DyadCardHeader>
<DyadFilePath path={path} />
{description && (
<div className="text-sm text-gray-600 dark:text-gray-300">
<DyadDescription>
<span className="font-medium">Summary: </span>
{description}
</div>
</DyadDescription>
)}
{isContentVisible && (
<DyadCardContent isExpanded={isContentVisible}>
<div
className="text-xs cursor-text"
onClick={(e) => e.stopPropagation()}
......@@ -114,17 +89,23 @@ export const DyadSearchReplace: React.FC<DyadSearchReplaceProps> = ({
{children}
</CodeHighlight>
) : (
<div className="space-y-3">
<div className="space-y-2">
{blocks.map((b, i) => (
<div key={i} className="border rounded-lg">
<div className="flex items-center justify-between px-3 py-2 bg-(--background-lighter) rounded-t-lg text-[11px]">
<div className="flex items-center gap-2">
<ArrowLeftRight size={14} />
<span className="font-medium">Change {i + 1}</span>
</div>
<div
key={i}
className="border border-border/60 rounded-lg overflow-hidden"
>
<div className="flex items-center gap-2 px-3 py-1.5 bg-muted/30 text-[11px]">
<ArrowLeftRight
size={13}
className="text-muted-foreground"
/>
<span className="font-medium text-muted-foreground">
Change {i + 1}
</span>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-0">
<div className="p-3 border-t md:border-r">
<div className="p-3 border-t border-border/40 md:border-r">
<div className="text-[11px] mb-1 text-muted-foreground font-medium">
Search
</div>
......@@ -132,7 +113,7 @@ export const DyadSearchReplace: React.FC<DyadSearchReplaceProps> = ({
{b.searchContent}
</CodeHighlight>
</div>
<div className="p-3 border-t">
<div className="p-3 border-t border-border/40">
<div className="text-[11px] mb-1 text-muted-foreground font-medium">
Replace
</div>
......@@ -146,7 +127,7 @@ export const DyadSearchReplace: React.FC<DyadSearchReplaceProps> = ({
</div>
)}
</div>
)}
</div>
</DyadCardContent>
</DyadCard>
);
};
import React, { useState } from "react";
import { CustomTagState } from "./stateTypes";
import {
Loader2,
CheckCircle2,
XCircle,
ChevronsDownUp,
ChevronsUpDown,
} from "lucide-react";
DyadCard,
DyadCardHeader,
DyadExpandIcon,
DyadFinishedIcon,
DyadCardContent,
} from "./DyadCardPrimitives";
import { CircleX, Loader2 } from "lucide-react";
interface DyadStatusProps {
node: {
......@@ -22,61 +23,55 @@ export function DyadStatus({ node, children }: DyadStatusProps) {
const { title = "Processing...", state } = node.properties;
const isInProgress = state === "pending";
const isAborted = state === "aborted";
const isFinished = state === "finished";
const content = typeof children === "string" ? children : "";
const [isContentVisible, setIsContentVisible] = useState(false);
// Pick accent color based on state
const accentColor = isAborted ? "red" : isInProgress ? "amber" : "green";
// Pick the left icon based on state
const icon = isInProgress ? (
<Loader2 size={15} className="animate-spin" />
) : isAborted ? (
<CircleX size={15} />
) : (
<DyadFinishedIcon />
);
return (
<div
className={`bg-(--background-lightest) hover:bg-(--background-lighter) rounded-lg px-4 py-2 border my-2 cursor-pointer ${
isInProgress
? "border-amber-500"
: isAborted
? "border-red-500"
: "border-border"
}`}
<DyadCard
state={state}
accentColor={accentColor}
isExpanded={isContentVisible}
onClick={() => setIsContentVisible(!isContentVisible)}
>
<div className="flex items-center justify-between">
<div className="flex items-center gap-2">
{isInProgress ? (
<Loader2 className="size-4 animate-spin text-amber-600" />
) : isAborted ? (
<XCircle className="size-4 text-red-600" />
) : (
<CheckCircle2 className="size-4 text-green-600 dark:text-green-500" />
)}
<DyadCardHeader icon={icon} accentColor={accentColor}>
<span
className={`font-medium text-sm ${
isInProgress
? "bg-gradient-to-r from-foreground via-muted-foreground to-foreground bg-[length:200%_100%] animate-[shimmer_2s_ease-in-out_infinite] bg-clip-text text-transparent"
: "text-gray-700 dark:text-gray-300"
: isFinished
? "text-foreground"
: "text-muted-foreground"
}`}
>
{title}
</span>
<div className="ml-auto">
<DyadExpandIcon isExpanded={isContentVisible} />
</div>
<div className="flex items-center">
{isContentVisible ? (
<ChevronsDownUp
size={20}
className="text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200"
/>
) : (
<ChevronsUpDown
size={20}
className="text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200"
/>
)}
</div>
</div>
{isContentVisible && content && (
</DyadCardHeader>
<DyadCardContent isExpanded={isContentVisible}>
{content && (
<div
className="mt-2 p-3 text-xs font-mono whitespace-pre-wrap max-h-60 overflow-y-auto bg-muted/20 rounded cursor-text"
className="p-3 text-xs font-mono whitespace-pre-wrap max-h-60 overflow-y-auto bg-muted/20 rounded-lg cursor-text"
onClick={(e) => e.stopPropagation()}
>
{content}
</div>
)}
</div>
</DyadCardContent>
</DyadCard>
);
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论