mirror of
https://github.com/stackblitz/bolt.new
synced 2025-02-06 04:48:04 +00:00
Merge pull request #412 from wonderwhy-er/Cleanup-extract-import-button
Small-cleanup-of-base-chat-component
This commit is contained in:
commit
f8987bec55
@ -14,20 +14,13 @@ import { Messages } from './Messages.client';
|
|||||||
import { SendButton } from './SendButton.client';
|
import { SendButton } from './SendButton.client';
|
||||||
import { APIKeyManager } from './APIKeyManager';
|
import { APIKeyManager } from './APIKeyManager';
|
||||||
import Cookies from 'js-cookie';
|
import Cookies from 'js-cookie';
|
||||||
import { toast } from 'react-toastify';
|
|
||||||
import * as Tooltip from '@radix-ui/react-tooltip';
|
import * as Tooltip from '@radix-ui/react-tooltip';
|
||||||
|
|
||||||
import styles from './BaseChat.module.scss';
|
import styles from './BaseChat.module.scss';
|
||||||
import type { ProviderInfo } from '~/utils/types';
|
import type { ProviderInfo } from '~/utils/types';
|
||||||
import { ExportChatButton } from '~/components/chat/ExportChatButton';
|
import { ExportChatButton } from '~/components/chat/chatExportAndImport/ExportChatButton';
|
||||||
|
import { ImportButton } from '~/components/chat/chatExportAndImport/ImportButton';
|
||||||
const EXAMPLE_PROMPTS = [
|
import { ExamplePrompts } from '~/components/chat/ExamplePrompts';
|
||||||
{ text: 'Build a todo app in React using Tailwind' },
|
|
||||||
{ text: 'Build a simple blog using Astro' },
|
|
||||||
{ text: 'Create a cookie consent form using Material UI' },
|
|
||||||
{ text: 'Make a space invaders game' },
|
|
||||||
{ text: 'How do I center a div?' },
|
|
||||||
];
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
const providerList = PROVIDER_LIST;
|
const providerList = PROVIDER_LIST;
|
||||||
@ -168,67 +161,6 @@ export const BaseChat = React.forwardRef<HTMLDivElement, BaseChatProps>(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const chatImportButton = !chatStarted && (
|
|
||||||
<div className="flex flex-col items-center justify-center flex-1 p-4">
|
|
||||||
<input
|
|
||||||
type="file"
|
|
||||||
id="chat-import"
|
|
||||||
className="hidden"
|
|
||||||
accept=".json"
|
|
||||||
onChange={async (e) => {
|
|
||||||
const file = e.target.files?.[0];
|
|
||||||
|
|
||||||
if (file && importChat) {
|
|
||||||
try {
|
|
||||||
const reader = new FileReader();
|
|
||||||
|
|
||||||
reader.onload = async (e) => {
|
|
||||||
try {
|
|
||||||
const content = e.target?.result as string;
|
|
||||||
const data = JSON.parse(content);
|
|
||||||
|
|
||||||
if (!Array.isArray(data.messages)) {
|
|
||||||
toast.error('Invalid chat file format');
|
|
||||||
}
|
|
||||||
|
|
||||||
await importChat(data.description, data.messages);
|
|
||||||
toast.success('Chat imported successfully');
|
|
||||||
} catch (error: unknown) {
|
|
||||||
if (error instanceof Error) {
|
|
||||||
toast.error('Failed to parse chat file: ' + error.message);
|
|
||||||
} else {
|
|
||||||
toast.error('Failed to parse chat file');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
reader.onerror = () => toast.error('Failed to read chat file');
|
|
||||||
reader.readAsText(file);
|
|
||||||
} catch (error) {
|
|
||||||
toast.error(error instanceof Error ? error.message : 'Failed to import chat');
|
|
||||||
}
|
|
||||||
e.target.value = ''; // Reset file input
|
|
||||||
} else {
|
|
||||||
toast.error('Something went wrong');
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<div className="flex flex-col items-center gap-4 max-w-2xl text-center">
|
|
||||||
<div className="flex gap-2">
|
|
||||||
<button
|
|
||||||
onClick={() => {
|
|
||||||
const input = document.getElementById('chat-import');
|
|
||||||
input?.click();
|
|
||||||
}}
|
|
||||||
className="px-4 py-2 rounded-lg border border-bolt-elements-borderColor bg-bolt-elements-prompt-background text-bolt-elements-textPrimary hover:bg-bolt-elements-background-depth-3 transition-all flex items-center gap-2"
|
|
||||||
>
|
|
||||||
<div className="i-ph:upload-simple" />
|
|
||||||
Import Chat
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
const baseChat = (
|
const baseChat = (
|
||||||
<div
|
<div
|
||||||
ref={ref}
|
ref={ref}
|
||||||
@ -378,27 +310,8 @@ export const BaseChat = React.forwardRef<HTMLDivElement, BaseChatProps>(
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{chatImportButton}
|
{!chatStarted && ImportButton(importChat)}
|
||||||
{!chatStarted && (
|
{!chatStarted && ExamplePrompts(sendMessage)}
|
||||||
<div id="examples" className="relative w-full max-w-xl mx-auto mt-8 flex justify-center">
|
|
||||||
<div className="flex flex-col space-y-2 [mask-image:linear-gradient(to_bottom,black_0%,transparent_180%)] hover:[mask-image:none]">
|
|
||||||
{EXAMPLE_PROMPTS.map((examplePrompt, index) => {
|
|
||||||
return (
|
|
||||||
<button
|
|
||||||
key={index}
|
|
||||||
onClick={(event) => {
|
|
||||||
sendMessage?.(event, examplePrompt.text);
|
|
||||||
}}
|
|
||||||
className="group flex items-center w-full gap-2 justify-center bg-transparent text-bolt-elements-textTertiary hover:text-bolt-elements-textPrimary transition-theme"
|
|
||||||
>
|
|
||||||
{examplePrompt.text}
|
|
||||||
<div className="i-ph:arrow-bend-down-left" />
|
|
||||||
</button>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
<ClientOnly>{() => <Workbench chatStarted={chatStarted} isStreaming={isStreaming} />}</ClientOnly>
|
<ClientOnly>{() => <Workbench chatStarted={chatStarted} isStreaming={isStreaming} />}</ClientOnly>
|
||||||
</div>
|
</div>
|
||||||
|
32
app/components/chat/ExamplePrompts.tsx
Normal file
32
app/components/chat/ExamplePrompts.tsx
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
const EXAMPLE_PROMPTS = [
|
||||||
|
{ text: 'Build a todo app in React using Tailwind' },
|
||||||
|
{ text: 'Build a simple blog using Astro' },
|
||||||
|
{ text: 'Create a cookie consent form using Material UI' },
|
||||||
|
{ text: 'Make a space invaders game' },
|
||||||
|
{ text: 'How do I center a div?' },
|
||||||
|
];
|
||||||
|
|
||||||
|
export function ExamplePrompts(sendMessage?: { (event: React.UIEvent, messageInput?: string): void | undefined }) {
|
||||||
|
return (
|
||||||
|
<div id="examples" className="relative w-full max-w-xl mx-auto mt-8 flex justify-center">
|
||||||
|
<div className="flex flex-col space-y-2 [mask-image:linear-gradient(to_bottom,black_0%,transparent_180%)] hover:[mask-image:none]">
|
||||||
|
{EXAMPLE_PROMPTS.map((examplePrompt, index: number) => {
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
key={index}
|
||||||
|
onClick={(event) => {
|
||||||
|
sendMessage?.(event, examplePrompt.text);
|
||||||
|
}}
|
||||||
|
className="group flex items-center w-full gap-2 justify-center bg-transparent text-bolt-elements-textTertiary hover:text-bolt-elements-textPrimary transition-theme"
|
||||||
|
>
|
||||||
|
{examplePrompt.text}
|
||||||
|
<div className="i-ph:arrow-bend-down-left" />
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
66
app/components/chat/chatExportAndImport/ImportButton.tsx
Normal file
66
app/components/chat/chatExportAndImport/ImportButton.tsx
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
import type { Message } from 'ai';
|
||||||
|
import { toast } from 'react-toastify';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
export function ImportButton(importChat: ((description: string, messages: Message[]) => Promise<void>) | undefined) {
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col items-center justify-center flex-1 p-4">
|
||||||
|
<input
|
||||||
|
type="file"
|
||||||
|
id="chat-import"
|
||||||
|
className="hidden"
|
||||||
|
accept=".json"
|
||||||
|
onChange={async (e) => {
|
||||||
|
const file = e.target.files?.[0];
|
||||||
|
|
||||||
|
if (file && importChat) {
|
||||||
|
try {
|
||||||
|
const reader = new FileReader();
|
||||||
|
|
||||||
|
reader.onload = async (e) => {
|
||||||
|
try {
|
||||||
|
const content = e.target?.result as string;
|
||||||
|
const data = JSON.parse(content);
|
||||||
|
|
||||||
|
if (!Array.isArray(data.messages)) {
|
||||||
|
toast.error('Invalid chat file format');
|
||||||
|
}
|
||||||
|
|
||||||
|
await importChat(data.description, data.messages);
|
||||||
|
toast.success('Chat imported successfully');
|
||||||
|
} catch (error: unknown) {
|
||||||
|
if (error instanceof Error) {
|
||||||
|
toast.error('Failed to parse chat file: ' + error.message);
|
||||||
|
} else {
|
||||||
|
toast.error('Failed to parse chat file');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
reader.onerror = () => toast.error('Failed to read chat file');
|
||||||
|
reader.readAsText(file);
|
||||||
|
} catch (error) {
|
||||||
|
toast.error(error instanceof Error ? error.message : 'Failed to import chat');
|
||||||
|
}
|
||||||
|
e.target.value = ''; // Reset file input
|
||||||
|
} else {
|
||||||
|
toast.error('Something went wrong');
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<div className="flex flex-col items-center gap-4 max-w-2xl text-center">
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
const input = document.getElementById('chat-import');
|
||||||
|
input?.click();
|
||||||
|
}}
|
||||||
|
className="px-4 py-2 rounded-lg border border-bolt-elements-borderColor bg-bolt-elements-prompt-background text-bolt-elements-textPrimary hover:bg-bolt-elements-background-depth-3 transition-all flex items-center gap-2"
|
||||||
|
>
|
||||||
|
<div className="i-ph:upload-simple" />
|
||||||
|
Import Chat
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user