diff --git a/app/components/chat/Artifact.tsx b/app/components/chat/Artifact.tsx index 154b69eb..75cf6e26 100644 --- a/app/components/chat/Artifact.tsx +++ b/app/components/chat/Artifact.tsx @@ -75,7 +75,7 @@ export const Artifact = memo(({ messageId }: ArtifactProps) => { >
- {artifact.type === 'bundled' ? 'Setup Project' : artifact?.title} + {artifact?.title}
Click to open Workbench @@ -109,7 +109,13 @@ export const Artifact = memo(({ messageId }: ArtifactProps) => {
)}
-
Create initial files
+
+ {allActionFinished + ? artifact.id === 'imported-files' + ? 'Restore files from snapshot' + : 'Initial files created' + : 'Creating initial files'} +
)} diff --git a/app/components/chat/BaseChat.tsx b/app/components/chat/BaseChat.tsx index 59b684fc..42662d05 100644 --- a/app/components/chat/BaseChat.tsx +++ b/app/components/chat/BaseChat.tsx @@ -367,33 +367,32 @@ export const BaseChat = React.forwardRef( }} - {deployAlert && ( - clearDeployAlert?.()} - postMessage={(message: string | undefined) => { - sendMessage?.({} as any, message); - clearSupabaseAlert?.(); - }} - /> - )} - {supabaseAlert && ( - clearSupabaseAlert?.()} - postMessage={(message) => { - sendMessage?.({} as any, message); - clearSupabaseAlert?.(); - }} - /> - )} -
-
+
+ {deployAlert && ( + clearDeployAlert?.()} + postMessage={(message: string | undefined) => { + sendMessage?.({} as any, message); + clearSupabaseAlert?.(); + }} + /> + )} + {supabaseAlert && ( + clearSupabaseAlert?.()} + postMessage={(message) => { + sendMessage?.({} as any, message); + clearSupabaseAlert?.(); + }} + /> + )} {actionAlert && ( ( /> )}
+ {progressAnnotations && }
scrollToBottom()} > Go to last message diff --git a/app/components/chat/Chat.client.tsx b/app/components/chat/Chat.client.tsx index c5ff7dbb..8786b222 100644 --- a/app/components/chat/Chat.client.tsx +++ b/app/components/chat/Chat.client.tsx @@ -316,14 +316,14 @@ export const ChatImpl = memo( setFakeLoading(true); if (autoSelectTemplate) { - const { template, title } = await selectStarterTemplate({ + const { template } = await selectStarterTemplate({ message: messageContent, model, provider, }); if (template !== 'blank') { - const temResp = await getTemplates(template, title).catch((e) => { + const temResp = await getTemplates(template).catch((e) => { if (e.message.includes('rate limit')) { toast.warning('Rate limit exceeded. Skipping starter template\n Continuing with blank template'); } else { diff --git a/app/components/chat/SupabaseAlert.tsx b/app/components/chat/SupabaseAlert.tsx index d86e5e53..414a6e51 100644 --- a/app/components/chat/SupabaseAlert.tsx +++ b/app/components/chat/SupabaseAlert.tsx @@ -99,7 +99,7 @@ export function SupabaseChatAlert({ alert, clearAlert, postMessage }: Props) { animate={{ opacity: 1, y: 0 }} exit={{ opacity: 0, y: -20 }} transition={{ duration: 0.3 }} - className="max-w-chat rounded-lg border-l-2 border-l-[#098F5F] border-bolt-elements-borderColor bg-bolt-elements-background-depth-2" + className="max-w-chat rounded-lg border-l-2 border-l-[#098F5F] border border-bolt-elements-borderColor bg-bolt-elements-background-depth-2" > {/* Header */}
diff --git a/app/lib/persistence/useChatHistory.ts b/app/lib/persistence/useChatHistory.ts index 3676dc32..dad3da7c 100644 --- a/app/lib/persistence/useChatHistory.ts +++ b/app/lib/persistence/useChatHistory.ts @@ -20,7 +20,7 @@ import { import type { FileMap } from '~/lib/stores/files'; import type { Snapshot } from './types'; import { webcontainer } from '~/lib/webcontainer'; -import { createCommandsMessage, detectProjectCommands } from '~/utils/projectCommands'; +import { detectProjectCommands, createCommandActionsString } from '~/utils/projectCommands'; import type { ContextAnnotation } from '~/types/context'; export interface ChatHistoryItem { @@ -112,23 +112,26 @@ export function useChatHistory() { path: key, }; }) - .filter((x) => !!x); + .filter((x): x is { content: string; path: string } => !!x); // Type assertion const projectCommands = await detectProjectCommands(files); - const commands = createCommandsMessage(projectCommands); + + // Call the modified function to get only the command actions string + const commandActionsString = createCommandActionsString(projectCommands); filteredMessages = [ { id: generateId(), role: 'user', - content: `Restore project from snapshot - `, + content: `Restore project from snapshot`, // Removed newline annotations: ['no-store', 'hidden'], }, { id: storedMessages.messages[snapshotIndex].id, role: 'assistant', - content: ` 📦 Chat Restored from snapshot, You can revert this message to load the full chat history - + + // Combine followup message and the artifact with files and command actions + content: `Bolt Restored your chat from a snapshot. You can revert this message to load the full chat history. + ${Object.entries(snapshot?.files || {}) .map(([key, value]) => { if (value?.type === 'file') { @@ -142,8 +145,9 @@ ${value.content} } }) .join('\n')} + ${commandActionsString} - `, + `, // Added commandActionsString, followupMessage, updated id and title annotations: [ 'no-store', ...(summary @@ -157,33 +161,13 @@ ${value.content} : []), ], }, - ...(commands !== null - ? [ - { - id: `${storedMessages.messages[snapshotIndex].id}-2`, - role: 'user' as const, - content: `setup project`, - annotations: ['no-store', 'hidden'], - }, - { - ...commands, - id: `${storedMessages.messages[snapshotIndex].id}-3`, - annotations: [ - 'no-store', - ...(commands.annotations || []), - ...(summary - ? [ - { - chatId: `${storedMessages.messages[snapshotIndex].id}-3`, - type: 'chatSummary', - summary, - } satisfies ContextAnnotation, - ] - : []), - ], - }, - ] - : []), + + // Remove the separate user and assistant messages for commands + /* + *...(commands !== null // This block is no longer needed + * ? [ ... ] + * : []), + */ ...filteredMessages, ]; restoreSnapshot(mixedId); diff --git a/app/utils/constants.ts b/app/utils/constants.ts index e85405d6..4d955d75 100644 --- a/app/utils/constants.ts +++ b/app/utils/constants.ts @@ -26,7 +26,7 @@ PROVIDER_LIST.forEach((provider) => { export const STARTER_TEMPLATES: Template[] = [ { - name: 'bolt-expo-app', + name: 'Expo App', label: 'Expo App', description: 'Expo starter template for building cross-platform mobile apps', githubRepo: 'xKevIsDev/bolt-expo-template', @@ -34,7 +34,7 @@ export const STARTER_TEMPLATES: Template[] = [ icon: 'i-bolt:expo', }, { - name: 'bolt-astro-basic', + name: 'Basic Astro', label: 'Astro Basic', description: 'Lightweight Astro starter template for building fast static websites', githubRepo: 'xKevIsDev/bolt-astro-basic-template', @@ -42,7 +42,7 @@ export const STARTER_TEMPLATES: Template[] = [ icon: 'i-bolt:astro', }, { - name: 'bolt-nextjs-shadcn', + name: 'NextJS Shadcn', label: 'Next.js with shadcn/ui', description: 'Next.js starter fullstack template integrated with shadcn/ui components and styling system', githubRepo: 'xKevIsDev/bolt-nextjs-shadcn-template', @@ -50,7 +50,7 @@ export const STARTER_TEMPLATES: Template[] = [ icon: 'i-bolt:nextjs', }, { - name: 'bolt-qwik-ts', + name: 'Qwik Typescript', label: 'Qwik TypeScript', description: 'Qwik framework starter with TypeScript for building resumable applications', githubRepo: 'xKevIsDev/bolt-qwik-ts-template', @@ -58,7 +58,7 @@ export const STARTER_TEMPLATES: Template[] = [ icon: 'i-bolt:qwik', }, { - name: 'bolt-remix-ts', + name: 'Remix Typescript', label: 'Remix TypeScript', description: 'Remix framework starter with TypeScript for full-stack web applications', githubRepo: 'xKevIsDev/bolt-remix-ts-template', @@ -66,7 +66,7 @@ export const STARTER_TEMPLATES: Template[] = [ icon: 'i-bolt:remix', }, { - name: 'bolt-slidev', + name: 'Slidev', label: 'Slidev Presentation', description: 'Slidev starter template for creating developer-friendly presentations using Markdown', githubRepo: 'xKevIsDev/bolt-slidev-template', @@ -74,7 +74,7 @@ export const STARTER_TEMPLATES: Template[] = [ icon: 'i-bolt:slidev', }, { - name: 'bolt-sveltekit', + name: 'Sveltekit', label: 'SvelteKit', description: 'SvelteKit starter template for building fast, efficient web applications', githubRepo: 'bolt-sveltekit-template', @@ -82,7 +82,7 @@ export const STARTER_TEMPLATES: Template[] = [ icon: 'i-bolt:svelte', }, { - name: 'vanilla-vite', + name: 'Vanilla Vite', label: 'Vanilla + Vite', description: 'Minimal Vite starter template for vanilla JavaScript projects', githubRepo: 'xKevIsDev/vanilla-vite-template', @@ -90,7 +90,7 @@ export const STARTER_TEMPLATES: Template[] = [ icon: 'i-bolt:vite', }, { - name: 'bolt-vite-react', + name: 'Vite React', label: 'React + Vite + typescript', description: 'React starter template powered by Vite for fast development experience', githubRepo: 'xKevIsDev/bolt-vite-react-ts-template', @@ -98,7 +98,7 @@ export const STARTER_TEMPLATES: Template[] = [ icon: 'i-bolt:react', }, { - name: 'bolt-vite-ts', + name: 'Vite Typescript', label: 'Vite + TypeScript', description: 'Vite starter template with TypeScript configuration for type-safe development', githubRepo: 'xKevIsDev/bolt-vite-ts-template', @@ -106,7 +106,7 @@ export const STARTER_TEMPLATES: Template[] = [ icon: 'i-bolt:typescript', }, { - name: 'bolt-vue', + name: 'Vue', label: 'Vue.js', description: 'Vue.js starter template with modern tooling and best practices', githubRepo: 'xKevIsDev/bolt-vue-template', @@ -114,7 +114,7 @@ export const STARTER_TEMPLATES: Template[] = [ icon: 'i-bolt:vue', }, { - name: 'bolt-angular', + name: 'Angular', label: 'Angular Starter', description: 'A modern Angular starter template with TypeScript support and best practices configuration', githubRepo: 'xKevIsDev/bolt-angular-template', diff --git a/app/utils/projectCommands.ts b/app/utils/projectCommands.ts index 34abc0a0..f3a9f8be 100644 --- a/app/utils/projectCommands.ts +++ b/app/utils/projectCommands.ts @@ -84,9 +84,10 @@ export function createCommandsMessage(commands: ProjectCommands): Message | null return { role: 'assistant', content: ` +${commands.followupMessage ? `\n\n${commands.followupMessage}` : ''} ${commandString} -${commands.followupMessage ? `\n\n${commands.followupMessage}` : ''}`, +`, id: generateId(), createdAt: new Date(), }; @@ -127,3 +128,26 @@ export function escapeBoltAActionTags(input: string) { export function escapeBoltTags(input: string) { return escapeBoltArtifactTags(escapeBoltAActionTags(input)); } + +// We have this seperate function to simplify the restore snapshot process in to one single artifact. +export function createCommandActionsString(commands: ProjectCommands): string { + if (!commands.setupCommand && !commands.startCommand) { + // Return empty string if no commands + return ''; + } + + let commandString = ''; + + if (commands.setupCommand) { + commandString += ` +${commands.setupCommand}`; + } + + if (commands.startCommand) { + commandString += ` +${commands.startCommand} +`; + } + + return commandString; +} diff --git a/app/utils/selectStarterTemplate.ts b/app/utils/selectStarterTemplate.ts index bccab72c..29fe6cbb 100644 --- a/app/utils/selectStarterTemplate.ts +++ b/app/utils/selectStarterTemplate.ts @@ -129,7 +129,7 @@ const getGitHubRepoContent = async (repoName: string): Promise<{ name: string; p } }; -export async function getTemplates(templateName: string, title?: string) { +export async function getTemplates(templateName: string) { const template = STARTER_TEMPLATES.find((t) => t.name == templateName); if (!template) { @@ -182,7 +182,8 @@ export async function getTemplates(templateName: string, title?: string) { } const assistantMessage = ` - +Bolt is initializing your project with the required files using the ${template.name} template. + ${filesToImport.files .map( (file) =>