Revert "Pro 1364 ux and codebase improvements"

This commit is contained in:
Strider
2025-05-28 12:01:16 -04:00
committed by GitHub
parent 50328938a4
commit e605b2bf24
34 changed files with 926 additions and 1402 deletions

View File

@@ -1,64 +0,0 @@
.BaseChat {
&[data-chat-visible='false'] {
--workbench-inner-width: 100%;
--workbench-left: 0;
.Chat {
--at-apply: bolt-ease-cubic-bezier;
transition-property: transform, opacity;
transition-duration: 0.3s;
will-change: transform, opacity;
transform: translateX(-50%);
opacity: 0;
}
}
}
.Chat {
opacity: 1;
}
.PromptEffectContainer {
--prompt-container-offset: 50px;
--prompt-line-stroke-width: 1px;
position: absolute;
pointer-events: none;
inset: calc(var(--prompt-container-offset) / -2);
width: calc(100% + var(--prompt-container-offset));
height: calc(100% + var(--prompt-container-offset));
}
.PromptEffectLine {
width: calc(100% - var(--prompt-container-offset) + var(--prompt-line-stroke-width));
height: calc(100% - var(--prompt-container-offset) + var(--prompt-line-stroke-width));
x: calc(var(--prompt-container-offset) / 2 - var(--prompt-line-stroke-width) / 2);
y: calc(var(--prompt-container-offset) / 2 - var(--prompt-line-stroke-width) / 2);
rx: calc(8px - var(--prompt-line-stroke-width));
fill: transparent;
stroke-width: var(--prompt-line-stroke-width);
stroke: url(#line-gradient);
stroke-dasharray: 35px 65px;
stroke-dashoffset: 10;
}
.PromptShine {
fill: url(#shine-gradient);
mix-blend-mode: overlay;
}
.filterInput {
margin-bottom: 1rem;
width: 100%;
input {
width: 100%;
padding: 0.5rem;
border: 1px solid var(--border-color);
border-radius: 4px;
font-size: 1rem;
&::placeholder {
color: var(--text-secondary);
}
}
}

View File

@@ -1,215 +0,0 @@
/*
* @ts-nocheck
* Preventing TS checks with files presented in the video for a better presentation.
*/
import React, { type RefCallback, useCallback } from 'react';
import { ClientOnly } from 'remix-utils/client-only';
import { Menu } from '~/components/sidebar/Menu.client';
import { Workbench } from '~/components/workbench/Workbench.client';
import { classNames } from '~/utils/classNames';
import { Messages } from '~/components/chat/Messages.client';
import { type Message } from '~/lib/persistence/message';
import * as Tooltip from '@radix-ui/react-tooltip';
import { IntroSection } from '~/components/chat/BaseChat/components/IntroSection/IntroSection';
import { SearchInput } from '~/components/chat/SearchInput/SearchInput';
import { ChatPromptContainer } from '~/components/chat/BaseChat/components/ChatPromptContainer/ChatPromptContainer';
import { useSpeechRecognition } from '~/hooks/useSpeechRecognition';
import styles from './BaseChat.module.scss';
import { ExamplePrompts } from '~/components/chat/ExamplePrompts';
import { ExampleLibraryApps } from '~/components/app-library/ExampleLibraryApps';
import type { RejectChangeData } from '~/components/chat/ApproveChange';
import { type MessageInputProps } from '~/components/chat/MessageInput/MessageInput';
export const TEXTAREA_MIN_HEIGHT = 76;
interface BaseChatProps {
textareaRef?: React.RefObject<HTMLTextAreaElement>;
messageRef?: RefCallback<HTMLDivElement>;
scrollRef?: RefCallback<HTMLDivElement>;
showChat?: boolean;
chatStarted?: boolean;
hasPendingMessage?: boolean;
pendingMessageStatus?: string;
messages?: Message[];
input?: string;
handleStop?: () => void;
sendMessage?: (messageInput?: string) => void;
handleInputChange?: (event: React.ChangeEvent<HTMLTextAreaElement>) => void;
uploadedFiles?: File[];
setUploadedFiles?: (files: File[]) => void;
imageDataList?: string[];
setImageDataList?: (dataList: string[]) => void;
onApproveChange?: (messageId: string) => void;
onRejectChange?: (messageId: string, data: RejectChangeData) => void;
}
type ExtendedMessage = Message & {
repositoryId?: string;
peanuts?: boolean;
approved?: boolean;
};
export const BaseChat = React.forwardRef<HTMLDivElement, BaseChatProps>(
(
{
textareaRef,
messageRef,
scrollRef,
showChat = true,
chatStarted = false,
hasPendingMessage = false,
pendingMessageStatus = '',
input = '',
handleInputChange,
sendMessage,
handleStop,
uploadedFiles = [],
setUploadedFiles,
imageDataList = [],
setImageDataList,
messages,
onApproveChange,
onRejectChange,
},
ref,
) => {
const TEXTAREA_MAX_HEIGHT = chatStarted ? 400 : 200;
const [rejectFormOpen, setRejectFormOpen] = React.useState(false);
const [filterText, setFilterText] = React.useState('');
const onTranscriptChange = useCallback(
(transcript: string) => {
if (handleInputChange) {
const syntheticEvent = {
target: { value: transcript },
} as React.ChangeEvent<HTMLTextAreaElement>;
handleInputChange(syntheticEvent);
}
},
[handleInputChange],
);
const { isListening, startListening, stopListening, abortListening } = useSpeechRecognition({
onTranscriptChange,
});
const handleSendMessage = (event: React.UIEvent, messageInput?: string) => {
if (sendMessage) {
sendMessage(messageInput);
abortListening();
if (handleInputChange) {
const syntheticEvent = {
target: { value: '' },
} as React.ChangeEvent<HTMLTextAreaElement>;
handleInputChange(syntheticEvent);
}
}
};
const approveChangeMessageId = (() => {
if (hasPendingMessage || !messages) {
return undefined;
}
for (let i = messages.length - 1; i >= 0; i--) {
const message = messages[i] as ExtendedMessage;
if (message.repositoryId && message.peanuts) {
return message.approved ? undefined : message.id;
}
if (message.role === 'user') {
return undefined;
}
}
return undefined;
})();
const messageInputProps = {
textareaRef,
input,
handleInputChange,
handleSendMessage,
handleStop,
hasPendingMessage,
chatStarted,
uploadedFiles,
setUploadedFiles,
imageDataList,
setImageDataList,
isListening,
onStartListening: startListening,
onStopListening: stopListening,
minHeight: TEXTAREA_MIN_HEIGHT,
maxHeight: TEXTAREA_MAX_HEIGHT,
};
const baseChat = (
<div
ref={ref}
className={classNames(styles.BaseChat, 'relative flex h-full w-full overflow-hidden p-6')}
data-chat-visible={showChat}
>
<ClientOnly>{() => <Menu />}</ClientOnly>
<div ref={scrollRef} className="flex flex-col lg:flex-row w-full h-full">
<div className={classNames(styles.Chat, 'flex flex-col flex-grow lg:min-w-[var(--chat-min-width)] h-full')}>
{!chatStarted && <IntroSection />}
<div
className={classNames('px-2 sm:px-6', {
'h-full flex flex-col': chatStarted,
})}
>
<ClientOnly>
{() => {
return chatStarted ? (
<Messages
ref={messageRef}
className="flex flex-col w-full flex-1 max-w-chat pb-6 mx-auto z-1 overflow-y-auto"
messages={messages}
hasPendingMessage={hasPendingMessage}
pendingMessageStatus={pendingMessageStatus}
/>
) : null;
}}
</ClientOnly>
<ChatPromptContainer
chatStarted={chatStarted}
uploadedFiles={uploadedFiles}
setUploadedFiles={setUploadedFiles!}
imageDataList={imageDataList}
setImageDataList={setImageDataList!}
approveChangeMessageId={approveChangeMessageId}
rejectFormOpen={rejectFormOpen}
setRejectFormOpen={setRejectFormOpen}
onApproveChange={onApproveChange}
onRejectChange={onRejectChange}
messageInputProps={messageInputProps as MessageInputProps}
/>
</div>
{!chatStarted && (
<>
{ExamplePrompts((event: React.UIEvent, messageInput?: string) => {
if (hasPendingMessage) {
handleStop?.();
return;
}
handleSendMessage(event, messageInput);
})}
<div className="text-2xl lg:text-4xl font-bold text-bolt-elements-textPrimary mt-8 mb-4 animate-fade-in text-center max-w-chat mx-auto">
Arboretum
</div>
<div className="text-bolt-elements-textSecondary text-center max-w-chat mx-auto">
Browse these auto-generated apps for a place to start
</div>
<SearchInput onSearch={setFilterText} />
<ExampleLibraryApps filterText={filterText} />
</>
)}
</div>
<ClientOnly>{() => <Workbench chatStarted={chatStarted} />}</ClientOnly>
</div>
</div>
);
return <Tooltip.Provider delayDuration={200}>{baseChat}</Tooltip.Provider>;
},
);

View File

@@ -1,101 +0,0 @@
import React from 'react';
import { classNames } from '~/utils/classNames';
import FilePreview from '~/components/chat/FilePreview';
import { ScreenshotStateManager } from '~/components/chat/ScreenshotStateManager';
import { ClientOnly } from 'remix-utils/client-only';
import ApproveChange from '~/components/chat/ApproveChange';
import { MessageInput } from '~/components/chat/MessageInput/MessageInput';
import styles from '~/components/chat/BaseChat/BaseChat.module.scss';
interface ChatPromptContainerProps {
chatStarted: boolean;
uploadedFiles: File[];
setUploadedFiles: (files: File[]) => void;
imageDataList: string[];
setImageDataList: (dataList: string[]) => void;
approveChangeMessageId?: string;
rejectFormOpen: boolean;
setRejectFormOpen: (open: boolean) => void;
onApproveChange?: (messageId: string) => void;
onRejectChange?: (messageId: string, data: any) => void;
messageInputProps: Partial<React.ComponentProps<typeof MessageInput>>;
}
export const ChatPromptContainer: React.FC<ChatPromptContainerProps> = ({
chatStarted,
uploadedFiles,
setUploadedFiles,
imageDataList,
setImageDataList,
approveChangeMessageId,
rejectFormOpen,
setRejectFormOpen,
onApproveChange,
onRejectChange,
messageInputProps,
}) => {
return (
<div
className={classNames(
'bg-bolt-elements-background-depth-2 p-3 rounded-lg border border-bolt-elements-borderColor relative w-full max-w-chat mx-auto z-prompt',
{
'sticky bottom-2': chatStarted,
},
)}
>
<svg className={classNames(styles.PromptEffectContainer)}>
<defs>
<linearGradient
id="line-gradient"
x1="20%"
y1="0%"
x2="-14%"
y2="10%"
gradientUnits="userSpaceOnUse"
gradientTransform="rotate(-45)"
>
<stop offset="0%" stopColor="#b44aff" stopOpacity="0%"></stop>
<stop offset="40%" stopColor="#b44aff" stopOpacity="80%"></stop>
<stop offset="50%" stopColor="#b44aff" stopOpacity="80%"></stop>
<stop offset="100%" stopColor="#b44aff" stopOpacity="0%"></stop>
</linearGradient>
<linearGradient id="shine-gradient">
<stop offset="0%" stopColor="white" stopOpacity="0%"></stop>
<stop offset="40%" stopColor="#ffffff" stopOpacity="80%"></stop>
<stop offset="50%" stopColor="#ffffff" stopOpacity="80%"></stop>
<stop offset="100%" stopColor="white" stopOpacity="0%"></stop>
</linearGradient>
</defs>
<rect className={classNames(styles.PromptEffectLine)} pathLength="100" strokeLinecap="round"></rect>
<rect className={classNames(styles.PromptShine)} x="48" y="24" width="70" height="1"></rect>
</svg>
<FilePreview
files={uploadedFiles}
imageDataList={imageDataList}
onRemove={(index) => {
setUploadedFiles(uploadedFiles.filter((_, i) => i !== index));
setImageDataList(imageDataList.filter((_, i) => i !== index));
}}
/>
<ClientOnly>
{() => (
<ScreenshotStateManager
setUploadedFiles={setUploadedFiles}
setImageDataList={setImageDataList}
uploadedFiles={uploadedFiles}
imageDataList={imageDataList}
/>
)}
</ClientOnly>
{approveChangeMessageId && (
<ApproveChange
rejectFormOpen={rejectFormOpen}
setRejectFormOpen={setRejectFormOpen}
onApprove={() => onApproveChange?.(approveChangeMessageId)}
onReject={(data) => onRejectChange?.(approveChangeMessageId, data)}
/>
)}
{!rejectFormOpen && <MessageInput {...messageInputProps} />}
</div>
);
};

View File

@@ -1,14 +0,0 @@
import React from 'react';
export const IntroSection: React.FC = () => {
return (
<div id="intro" className="mt-[16vh] max-w-chat mx-auto text-center px-4 lg:px-0">
<h1 className="text-3xl lg:text-6xl font-bold text-bolt-elements-textPrimary mb-4 animate-fade-in">
Get what you want
</h1>
<p className="text-md lg:text-xl mb-8 text-bolt-elements-textSecondary animate-fade-in animation-delay-200">
Write, test, and fix your app all from one prompt
</p>
</div>
);
};