feat: add streaming state to markdown quick actions

- Pass isStreaming prop through message components to disable actions during streaming
- Improve action button styling with icons and better spacing
- Disable buttons while streaming to prevent concurrent actions
This commit is contained in:
KevIsDev 2025-05-26 17:57:10 +01:00
parent 74605e96e3
commit de0a41b5f1
3 changed files with 30 additions and 5 deletions

View File

@ -16,6 +16,7 @@ interface AssistantMessageProps {
append?: (message: Message) => void; append?: (message: Message) => void;
chatMode?: 'discuss' | 'build'; chatMode?: 'discuss' | 'build';
setChatMode?: (mode: 'discuss' | 'build') => void; setChatMode?: (mode: 'discuss' | 'build') => void;
isStreaming?: boolean;
} }
function openArtifactInWorkbench(filePath: string) { function openArtifactInWorkbench(filePath: string) {
@ -43,7 +44,17 @@ function normalizedFilePath(path: string) {
} }
export const AssistantMessage = memo( export const AssistantMessage = memo(
({ content, annotations, messageId, onRewind, onFork, append, chatMode, setChatMode }: AssistantMessageProps) => { ({
content,
annotations,
messageId,
onRewind,
onFork,
append,
chatMode,
setChatMode,
isStreaming,
}: AssistantMessageProps) => {
const filteredAnnotations = (annotations?.filter( const filteredAnnotations = (annotations?.filter(
(annotation: JSONValue) => (annotation: JSONValue) =>
annotation && typeof annotation === 'object' && Object.keys(annotation).includes('type'), annotation && typeof annotation === 'object' && Object.keys(annotation).includes('type'),
@ -141,7 +152,7 @@ export const AssistantMessage = memo(
</div> </div>
</div> </div>
</> </>
<Markdown append={append} chatMode={chatMode} setChatMode={setChatMode} html> <Markdown append={append} chatMode={chatMode} setChatMode={setChatMode} isStreaming={isStreaming} html>
{content} {content}
</Markdown> </Markdown>
</div> </div>

View File

@ -18,10 +18,11 @@ interface MarkdownProps {
append?: (message: Message) => void; append?: (message: Message) => void;
chatMode?: 'discuss' | 'build'; chatMode?: 'discuss' | 'build';
setChatMode?: (mode: 'discuss' | 'build') => void; setChatMode?: (mode: 'discuss' | 'build') => void;
isStreaming?: boolean;
} }
export const Markdown = memo( export const Markdown = memo(
({ children, html = false, limitedMarkdown = false, append, setChatMode }: MarkdownProps) => { ({ children, html = false, limitedMarkdown = false, append, setChatMode, isStreaming }: MarkdownProps) => {
logger.trace('Render'); logger.trace('Render');
const components = useMemo(() => { const components = useMemo(() => {
@ -44,7 +45,7 @@ export const Markdown = memo(
} }
if (className?.includes('__boltQuickAction__') || dataProps?.dataBoltQuickAction) { if (className?.includes('__boltQuickAction__') || dataProps?.dataBoltQuickAction) {
return <div className="w-full grid grid-cols-2 gap-4">{children}</div>; return <div className="flex items-center gap-2 flex-wrap mt-3.5">{children}</div>;
} }
return ( return (
@ -84,13 +85,24 @@ export const Markdown = memo(
const path = dataProps['data-path'] || dataProps.dataPath; const path = dataProps['data-path'] || dataProps.dataPath;
const href = dataProps['data-href'] || dataProps.dataHref; const href = dataProps['data-href'] || dataProps.dataHref;
const iconClassMap: Record<string, string> = {
file: 'i-ph:file',
message: 'i-ph:chats',
implement: 'i-ph:code',
link: 'i-ph:link',
};
const safeType = typeof type === 'string' ? type : '';
const iconClass = iconClassMap[safeType] ?? 'i-ph:question';
return ( return (
<button <button
className=" p-2 rounded-md bg-bolt-elements-item-backgroundAccent hover:opacity-50 text-bolt-elements-item-contentAccent" className="rounded-md justify-center px-3 py-1.5 text-xs bg-bolt-elements-item-backgroundAccent text-bolt-elements-item-contentAccent opacity-90 hover:opacity-100 flex items-center gap-2 cursor-pointer"
data-type={type} data-type={type}
data-message={message} data-message={message}
data-path={path} data-path={path}
data-href={href} data-href={href}
disabled={isStreaming}
onClick={() => { onClick={() => {
if (type === 'file') { if (type === 'file') {
openArtifactInWorkbench(path); openArtifactInWorkbench(path);
@ -118,6 +130,7 @@ export const Markdown = memo(
} }
}} }}
> >
<div className={`text-lg ${iconClass}`} />
{children} {children}
</button> </button>
); );

View File

@ -100,6 +100,7 @@ export const Messages = forwardRef<HTMLDivElement, MessagesProps>(
append={props.append} append={props.append}
chatMode={props.chatMode} chatMode={props.chatMode}
setChatMode={props.setChatMode} setChatMode={props.setChatMode}
isStreaming={isStreaming}
/> />
)} )}
</div> </div>