diff --git a/src/components/chat/ChatInput.tsx b/src/components/chat/ChatInput.tsx index 21afc6a..0e02afc 100644 --- a/src/components/chat/ChatInput.tsx +++ b/src/components/chat/ChatInput.tsx @@ -421,6 +421,87 @@ function WriteCodeProperlyButton() { ); } +function RebuildButton() { + const { restartApp } = useRunApp(); + const posthog = usePostHog(); + const selectedAppId = useAtomValue(selectedAppIdAtom); + + const handleRebuild = useCallback(async () => { + if (!selectedAppId) return; + + posthog.capture("action:rebuild"); + await restartApp({ removeNodeModules: true }); + }, [selectedAppId, posthog, restartApp]); + + return ( + + + + + + +

Rebuild the application

+
+
+
+ ); +} + +function RestartButton() { + const { restartApp } = useRunApp(); + const posthog = usePostHog(); + const selectedAppId = useAtomValue(selectedAppIdAtom); + + const handleRestart = useCallback(async () => { + if (!selectedAppId) return; + + posthog.capture("action:restart"); + await restartApp(); + }, [selectedAppId, posthog, restartApp]); + + return ( + + + + + + +

Restart the development server

+
+
+
+ ); +} + +function RefreshButton() { + const { refreshAppIframe } = useRunApp(); + const posthog = usePostHog(); + + const handleRefresh = useCallback(() => { + posthog.capture("action:refresh"); + refreshAppIframe(); + }, [posthog, refreshAppIframe]); + + return ( + + + + + + +

Refresh the application preview

+
+
+
+ ); +} + function mapActionToButton(action: SuggestedAction) { switch (action.id) { case "summarize-in-new-chat": @@ -429,6 +510,12 @@ function mapActionToButton(action: SuggestedAction) { return ; case "write-code-properly": return ; + case "rebuild": + return ; + case "restart": + return ; + case "refresh": + return ; default: console.error(`Unsupported action: ${action.id}`); return ( diff --git a/src/ipc/handlers/proposal_handlers.ts b/src/ipc/handlers/proposal_handlers.ts index e527e1b..0658a88 100644 --- a/src/ipc/handlers/proposal_handlers.ts +++ b/src/ipc/handlers/proposal_handlers.ts @@ -18,6 +18,7 @@ import { getDyadExecuteSqlTags, getDyadRenameTags, getDyadWriteTags, + getDyadCommandTags, processFullResponseActions, } from "../processors/response_processor"; import log from "electron-log"; @@ -247,6 +248,24 @@ const getProposalHandler = async ( id: "write-code-properly", }); } + + // Check for command tags and add corresponding actions + const commandTags = getDyadCommandTags(latestAssistantMessage.content); + if (commandTags.includes("rebuild")) { + actions.push({ + id: "rebuild", + }); + } + if (commandTags.includes("restart")) { + actions.push({ + id: "restart", + }); + } + if (commandTags.includes("refresh")) { + actions.push({ + id: "refresh", + }); + } } // Get all chat messages to calculate token usage diff --git a/src/ipc/processors/response_processor.ts b/src/ipc/processors/response_processor.ts index 8566358..a151eb8 100644 --- a/src/ipc/processors/response_processor.ts +++ b/src/ipc/processors/response_processor.ts @@ -138,6 +138,19 @@ export function getDyadExecuteSqlTags(fullResponse: string): SqlQuery[] { return queries; } +export function getDyadCommandTags(fullResponse: string): string[] { + const dyadCommandRegex = + /]*><\/dyad-command>/g; + let match; + const commands: string[] = []; + + while ((match = dyadCommandRegex.exec(fullResponse)) !== null) { + commands.push(match[1]); + } + + return commands; +} + interface Output { message: string; error: unknown; diff --git a/src/lib/schemas.ts b/src/lib/schemas.ts index 5271f04..d01a389 100644 --- a/src/lib/schemas.ts +++ b/src/lib/schemas.ts @@ -160,7 +160,10 @@ export type SuggestedAction = | RestartAppAction | SummarizeInNewChatAction | RefactorFileAction - | WriteCodeProperlyAction; + | WriteCodeProperlyAction + | RebuildAction + | RestartAction + | RefreshAction; export interface RestartAppAction { id: "restart-app"; @@ -179,6 +182,18 @@ export interface RefactorFileAction { path: string; } +export interface RebuildAction { + id: "rebuild"; +} + +export interface RestartAction { + id: "restart"; +} + +export interface RefreshAction { + id: "refresh"; +} + export interface ActionProposal { type: "action-proposal"; actions: SuggestedAction[]; diff --git a/src/prompts/system_prompt.ts b/src/prompts/system_prompt.ts index 6c4cf86..cce389b 100644 --- a/src/prompts/system_prompt.ts +++ b/src/prompts/system_prompt.ts @@ -2,6 +2,20 @@ export const SYSTEM_PROMPT = ` You are Dyad, an AI editor that creates and modifies web applications. You assist users by chatting with them and making changes to their code in real-time. You understand that users can see a live preview of their application in an iframe on the right side of the screen while you make code changes. Not every interaction requires code changes - you're happy to discuss, explain concepts, or provide guidance without modifying the codebase. When code changes are needed, you make efficient and effective updates to React codebases while following best practices for maintainability and readability. You take pride in keeping things simple and elegant. You are friendly and helpful, always aiming to provide clear explanations. +# App Preview / Commands + +Do *not* tell the user to run shell commands. Instead, they can do one of the following commands in the UI: + +- **Rebuild**: This will rebuild the app from scratch. First it deletes the node_modules folder and then it re-installs the npm packages and then starts the app server. +- **Restart**: This will restart the app server. +- **Refresh**: This will refresh the app preview page. + +You can suggest one of these commands by using the tag like this: + + + + +If you output one of these commands, tell the user to look for the action button above the chat input. # Guidelines