mirror of
https://github.com/stackblitz-labs/bolt.diy
synced 2025-06-26 18:26:38 +00:00
Final UI V3
# UI V3 Changelog Major updates and improvements in this release: ## Core Changes - Complete NEW REWRITTEN UI system overhaul (V3) with semantic design tokens - New settings management system with drag-and-drop capabilities - Enhanced provider system supporting multiple AI services - Improved theme system with better dark mode support - New component library with consistent design patterns ## Technical Updates - Reorganized project architecture for better maintainability - Performance optimizations and bundle size improvements - Enhanced security features and access controls - Improved developer experience with better tooling - Comprehensive testing infrastructure ## New Features - Background rays effect for improved visual feedback - Advanced tab management system - Automatic and manual update support - Enhanced error handling and visualization - Improved accessibility across all components For detailed information about all changes and improvements, please see the full changelog.
This commit is contained in:
@@ -23,6 +23,7 @@ import type { ProviderInfo } from '~/types/model';
|
||||
import { useSearchParams } from '@remix-run/react';
|
||||
import { createSampler } from '~/utils/sampler';
|
||||
import { getTemplates, selectStarterTemplate } from '~/utils/selectStarterTemplate';
|
||||
import { logStore } from '~/lib/stores/logs';
|
||||
|
||||
const toastAnimation = cssTransition({
|
||||
enter: 'animated fadeInRight',
|
||||
@@ -114,8 +115,8 @@ export const ChatImpl = memo(
|
||||
|
||||
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
||||
const [chatStarted, setChatStarted] = useState(initialMessages.length > 0);
|
||||
const [uploadedFiles, setUploadedFiles] = useState<File[]>([]); // Move here
|
||||
const [imageDataList, setImageDataList] = useState<string[]>([]); // Move here
|
||||
const [uploadedFiles, setUploadedFiles] = useState<File[]>([]);
|
||||
const [imageDataList, setImageDataList] = useState<string[]>([]);
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
const [fakeLoading, setFakeLoading] = useState(false);
|
||||
const files = useStore(workbenchStore.files);
|
||||
@@ -161,6 +162,11 @@ export const ChatImpl = memo(
|
||||
sendExtraMessageFields: true,
|
||||
onError: (e) => {
|
||||
logger.error('Request failed\n\n', e, error);
|
||||
logStore.logError('Chat request failed', e, {
|
||||
component: 'Chat',
|
||||
action: 'request',
|
||||
error: e.message,
|
||||
});
|
||||
toast.error(
|
||||
'There was an error processing your request: ' + (e.message ? e.message : 'No details were returned'),
|
||||
);
|
||||
@@ -171,8 +177,14 @@ export const ChatImpl = memo(
|
||||
|
||||
if (usage) {
|
||||
console.log('Token usage:', usage);
|
||||
|
||||
// You can now use the usage data as needed
|
||||
logStore.logProvider('Chat response completed', {
|
||||
component: 'Chat',
|
||||
action: 'response',
|
||||
model,
|
||||
provider: provider.name,
|
||||
usage,
|
||||
messageLength: message.content.length,
|
||||
});
|
||||
}
|
||||
|
||||
logger.debug('Finished streaming');
|
||||
@@ -231,6 +243,13 @@ export const ChatImpl = memo(
|
||||
stop();
|
||||
chatStore.setKey('aborted', true);
|
||||
workbenchStore.abortAllActions();
|
||||
|
||||
logStore.logProvider('Chat response aborted', {
|
||||
component: 'Chat',
|
||||
action: 'abort',
|
||||
model,
|
||||
provider: provider.name,
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
@@ -262,9 +281,9 @@ export const ChatImpl = memo(
|
||||
};
|
||||
|
||||
const sendMessage = async (_event: React.UIEvent, messageInput?: string) => {
|
||||
const _input = messageInput || input;
|
||||
const messageContent = messageInput || input;
|
||||
|
||||
if (!_input) {
|
||||
if (!messageContent?.trim()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -280,7 +299,7 @@ export const ChatImpl = memo(
|
||||
|
||||
if (autoSelectTemplate) {
|
||||
const { template, title } = await selectStarterTemplate({
|
||||
message: _input,
|
||||
message: messageContent,
|
||||
model,
|
||||
provider,
|
||||
});
|
||||
@@ -302,7 +321,7 @@ export const ChatImpl = memo(
|
||||
{
|
||||
id: `${new Date().getTime()}`,
|
||||
role: 'user',
|
||||
content: _input,
|
||||
content: messageContent,
|
||||
},
|
||||
{
|
||||
id: `${new Date().getTime()}`,
|
||||
@@ -332,7 +351,7 @@ export const ChatImpl = memo(
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: `[Model: ${model}]\n\n[Provider: ${provider.name}]\n\n${_input}`,
|
||||
text: `[Model: ${model}]\n\n[Provider: ${provider.name}]\n\n${messageContent}`,
|
||||
},
|
||||
...imageDataList.map((imageData) => ({
|
||||
type: 'image',
|
||||
@@ -356,31 +375,20 @@ export const ChatImpl = memo(
|
||||
chatStore.setKey('aborted', false);
|
||||
|
||||
if (fileModifications !== undefined) {
|
||||
/**
|
||||
* If we have file modifications we append a new user message manually since we have to prefix
|
||||
* the user input with the file modifications and we don't want the new user input to appear
|
||||
* in the prompt. Using `append` is almost the same as `handleSubmit` except that we have to
|
||||
* manually reset the input and we'd have to manually pass in file attachments. However, those
|
||||
* aren't relevant here.
|
||||
*/
|
||||
append({
|
||||
role: 'user',
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: `[Model: ${model}]\n\n[Provider: ${provider.name}]\n\n${_input}`,
|
||||
text: `[Model: ${model}]\n\n[Provider: ${provider.name}]\n\n${messageContent}`,
|
||||
},
|
||||
...imageDataList.map((imageData) => ({
|
||||
type: 'image',
|
||||
image: imageData,
|
||||
})),
|
||||
] as any, // Type assertion to bypass compiler check
|
||||
] as any,
|
||||
});
|
||||
|
||||
/**
|
||||
* After sending a new message we reset all modifications since the model
|
||||
* should now be aware of all the changes.
|
||||
*/
|
||||
workbenchStore.resetAllFileModifications();
|
||||
} else {
|
||||
append({
|
||||
@@ -388,20 +396,19 @@ export const ChatImpl = memo(
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: `[Model: ${model}]\n\n[Provider: ${provider.name}]\n\n${_input}`,
|
||||
text: `[Model: ${model}]\n\n[Provider: ${provider.name}]\n\n${messageContent}`,
|
||||
},
|
||||
...imageDataList.map((imageData) => ({
|
||||
type: 'image',
|
||||
image: imageData,
|
||||
})),
|
||||
] as any, // Type assertion to bypass compiler check
|
||||
] as any,
|
||||
});
|
||||
}
|
||||
|
||||
setInput('');
|
||||
Cookies.remove(PROMPT_COOKIE_KEY);
|
||||
|
||||
// Add file cleanup here
|
||||
setUploadedFiles([]);
|
||||
setImageDataList([]);
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import { generateId } from '~/utils/fileUtils';
|
||||
import { useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { LoadingOverlay } from '~/components/ui/LoadingOverlay';
|
||||
import { RepositorySelectionDialog } from '~/components/settings/connections/components/RepositorySelectionDialog';
|
||||
import { RepositorySelectionDialog } from '~/components/@settings/tabs/connections/components/RepositorySelectionDialog';
|
||||
import { classNames } from '~/utils/classNames';
|
||||
import { Button } from '~/components/ui/Button';
|
||||
import type { IChatMetadata } from '~/lib/persistence/db';
|
||||
|
||||
Reference in New Issue
Block a user