format fix
This commit is contained in:
parent
07650765d2
commit
6bc21fae4e
@ -413,79 +413,80 @@ export function renderApp(state: AppViewState) {
|
|||||||
: nothing
|
: nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
${state.tab === "chat"
|
${
|
||||||
? renderChat({
|
state.tab === "chat"
|
||||||
sessionKey: state.sessionKey,
|
? renderChat({
|
||||||
onSessionKeyChange: (next) => {
|
sessionKey: state.sessionKey,
|
||||||
state.sessionKey = next;
|
onSessionKeyChange: (next) => {
|
||||||
state.chatMessage = "";
|
state.sessionKey = next;
|
||||||
state.chatAttachments = [];
|
state.chatMessage = "";
|
||||||
state.chatStream = null;
|
state.chatAttachments = [];
|
||||||
state.chatStreamStartedAt = null;
|
state.chatStream = null;
|
||||||
state.chatRunId = null;
|
state.chatStreamStartedAt = null;
|
||||||
state.chatQueue = [];
|
state.chatRunId = null;
|
||||||
state.resetToolStream();
|
state.chatQueue = [];
|
||||||
state.resetChatScroll();
|
state.resetToolStream();
|
||||||
state.applySettings({
|
state.resetChatScroll();
|
||||||
...state.settings,
|
state.applySettings({
|
||||||
sessionKey: next,
|
...state.settings,
|
||||||
lastActiveSessionKey: next,
|
sessionKey: next,
|
||||||
});
|
lastActiveSessionKey: next,
|
||||||
void state.loadAssistantIdentity();
|
});
|
||||||
void loadChatHistory(state);
|
void state.loadAssistantIdentity();
|
||||||
void refreshChatAvatar(state);
|
void loadChatHistory(state);
|
||||||
},
|
void refreshChatAvatar(state);
|
||||||
thinkingLevel: state.chatThinkingLevel,
|
},
|
||||||
showThinking,
|
thinkingLevel: state.chatThinkingLevel,
|
||||||
loading: state.chatLoading,
|
showThinking,
|
||||||
sending: state.chatSending,
|
loading: state.chatLoading,
|
||||||
compactionStatus: state.compactionStatus,
|
sending: state.chatSending,
|
||||||
assistantAvatarUrl: chatAvatarUrl,
|
compactionStatus: state.compactionStatus,
|
||||||
messages: state.chatMessages,
|
assistantAvatarUrl: chatAvatarUrl,
|
||||||
toolMessages: state.chatToolMessages,
|
messages: state.chatMessages,
|
||||||
stream: state.chatStream,
|
toolMessages: state.chatToolMessages,
|
||||||
streamStartedAt: state.chatStreamStartedAt,
|
stream: state.chatStream,
|
||||||
draft: state.chatMessage,
|
streamStartedAt: state.chatStreamStartedAt,
|
||||||
queue: state.chatQueue,
|
draft: state.chatMessage,
|
||||||
connected: state.connected,
|
queue: state.chatQueue,
|
||||||
canSend: state.connected,
|
connected: state.connected,
|
||||||
disabledReason: chatDisabledReason,
|
canSend: state.connected,
|
||||||
error: state.lastError,
|
disabledReason: chatDisabledReason,
|
||||||
sessions: state.sessionsResult,
|
error: state.lastError,
|
||||||
focusMode: chatFocus,
|
sessions: state.sessionsResult,
|
||||||
onRefresh: () => {
|
focusMode: chatFocus,
|
||||||
state.resetToolStream();
|
onRefresh: () => {
|
||||||
return Promise.all([loadChatHistory(state), refreshChatAvatar(state)]);
|
state.resetToolStream();
|
||||||
},
|
return Promise.all([loadChatHistory(state), refreshChatAvatar(state)]);
|
||||||
onToggleFocusMode: () => {
|
},
|
||||||
if (state.onboarding) return;
|
onToggleFocusMode: () => {
|
||||||
state.applySettings({
|
if (state.onboarding) return;
|
||||||
...state.settings,
|
state.applySettings({
|
||||||
chatFocusMode: !state.settings.chatFocusMode,
|
...state.settings,
|
||||||
});
|
chatFocusMode: !state.settings.chatFocusMode,
|
||||||
},
|
});
|
||||||
onChatScroll: (event) => state.handleChatScroll(event),
|
},
|
||||||
onDraftChange: (next) => (state.chatMessage = next),
|
onChatScroll: (event) => state.handleChatScroll(event),
|
||||||
attachments: state.chatAttachments,
|
onDraftChange: (next) => (state.chatMessage = next),
|
||||||
onAttachmentsChange: (next) => (state.chatAttachments = next),
|
attachments: state.chatAttachments,
|
||||||
onSend: () => state.handleSendChat(),
|
onAttachmentsChange: (next) => (state.chatAttachments = next),
|
||||||
canAbort: Boolean(state.chatRunId),
|
onSend: () => state.handleSendChat(),
|
||||||
onAbort: () => void state.handleAbortChat(),
|
canAbort: Boolean(state.chatRunId),
|
||||||
onQueueRemove: (id) => state.removeQueuedMessage(id),
|
onAbort: () => void state.handleAbortChat(),
|
||||||
onNewSession: () =>
|
onQueueRemove: (id) => state.removeQueuedMessage(id),
|
||||||
state.handleSendChat("/new", { restoreDraft: true }),
|
onNewSession: () => state.handleSendChat("/new", { restoreDraft: true }),
|
||||||
// Sidebar props for tool output viewing
|
// Sidebar props for tool output viewing
|
||||||
sidebarOpen: state.sidebarOpen,
|
sidebarOpen: state.sidebarOpen,
|
||||||
sidebarContent: state.sidebarContent,
|
sidebarContent: state.sidebarContent,
|
||||||
sidebarError: state.sidebarError,
|
sidebarError: state.sidebarError,
|
||||||
splitRatio: state.splitRatio,
|
splitRatio: state.splitRatio,
|
||||||
onOpenSidebar: (content: string) => state.handleOpenSidebar(content),
|
onOpenSidebar: (content: string) => state.handleOpenSidebar(content),
|
||||||
onCloseSidebar: () => state.handleCloseSidebar(),
|
onCloseSidebar: () => state.handleCloseSidebar(),
|
||||||
onSplitRatioChange: (ratio: number) => state.handleSplitRatioChange(ratio),
|
onSplitRatioChange: (ratio: number) => state.handleSplitRatioChange(ratio),
|
||||||
assistantName: state.assistantName,
|
assistantName: state.assistantName,
|
||||||
assistantAvatar: state.assistantAvatar,
|
assistantAvatar: state.assistantAvatar,
|
||||||
})
|
})
|
||||||
: nothing}
|
: nothing
|
||||||
|
}
|
||||||
|
|
||||||
${
|
${
|
||||||
state.tab === "config"
|
state.tab === "config"
|
||||||
|
|||||||
@ -35,9 +35,7 @@ function extractImages(message: unknown): ImageBlock[] {
|
|||||||
const data = source.data as string;
|
const data = source.data as string;
|
||||||
const mediaType = (source.media_type as string) || "image/png";
|
const mediaType = (source.media_type as string) || "image/png";
|
||||||
// If data is already a data URL, use it directly
|
// If data is already a data URL, use it directly
|
||||||
const url = data.startsWith("data:")
|
const url = data.startsWith("data:") ? data : `data:${mediaType};base64,${data}`;
|
||||||
? data
|
|
||||||
: `data:${mediaType};base64,${data}`;
|
|
||||||
images.push({ url });
|
images.push({ url });
|
||||||
} else if (typeof b.url === "string") {
|
} else if (typeof b.url === "string") {
|
||||||
images.push({ url: b.url });
|
images.push({ url: b.url });
|
||||||
@ -260,14 +258,18 @@ function renderGroupedMessage(
|
|||||||
<div class="${bubbleClasses}">
|
<div class="${bubbleClasses}">
|
||||||
${canCopyMarkdown ? renderCopyAsMarkdownButton(markdown!) : nothing}
|
${canCopyMarkdown ? renderCopyAsMarkdownButton(markdown!) : nothing}
|
||||||
${renderMessageImages(images)}
|
${renderMessageImages(images)}
|
||||||
${reasoningMarkdown
|
${
|
||||||
? html`<div class="chat-thinking">${unsafeHTML(
|
reasoningMarkdown
|
||||||
toSanitizedMarkdownHtml(reasoningMarkdown),
|
? html`<div class="chat-thinking">${unsafeHTML(
|
||||||
)}</div>`
|
toSanitizedMarkdownHtml(reasoningMarkdown),
|
||||||
: nothing}
|
)}</div>`
|
||||||
${markdown
|
: nothing
|
||||||
? html`<div class="chat-text">${unsafeHTML(toSanitizedMarkdownHtml(markdown))}</div>`
|
}
|
||||||
: nothing}
|
${
|
||||||
|
markdown
|
||||||
|
? html`<div class="chat-text">${unsafeHTML(toSanitizedMarkdownHtml(markdown))}</div>`
|
||||||
|
: nothing
|
||||||
|
}
|
||||||
${toolCards.map((card) => renderToolCardSidebar(card, onOpenSidebar))}
|
${toolCards.map((card) => renderToolCardSidebar(card, onOpenSidebar))}
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|||||||
@ -1,10 +1,6 @@
|
|||||||
import { describe, expect, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
|
|
||||||
import {
|
import { handleChatEvent, type ChatEventPayload, type ChatState } from "./chat";
|
||||||
handleChatEvent,
|
|
||||||
type ChatEventPayload,
|
|
||||||
type ChatState,
|
|
||||||
} from "./chat";
|
|
||||||
|
|
||||||
function createState(overrides: Partial<ChatState> = {}): ChatState {
|
function createState(overrides: Partial<ChatState> = {}): ChatState {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@ -159,11 +159,7 @@ export function handleChatEvent(state: ChatState, payload?: ChatEventPayload) {
|
|||||||
|
|
||||||
// Final from another run (e.g. sub-agent announce): refresh history to show new message.
|
// Final from another run (e.g. sub-agent announce): refresh history to show new message.
|
||||||
// See https://github.com/clawdbot/clawdbot/issues/1909
|
// See https://github.com/clawdbot/clawdbot/issues/1909
|
||||||
if (
|
if (payload.runId && state.chatRunId && payload.runId !== state.chatRunId) {
|
||||||
payload.runId &&
|
|
||||||
state.chatRunId &&
|
|
||||||
payload.runId !== state.chatRunId
|
|
||||||
) {
|
|
||||||
if (payload.state === "final") return "final";
|
if (payload.state === "final") return "final";
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -99,10 +99,7 @@ function generateAttachmentId(): string {
|
|||||||
return `att-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
|
return `att-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function handlePaste(
|
function handlePaste(e: ClipboardEvent, props: ChatProps) {
|
||||||
e: ClipboardEvent,
|
|
||||||
props: ChatProps,
|
|
||||||
) {
|
|
||||||
const items = e.clipboardData?.items;
|
const items = e.clipboardData?.items;
|
||||||
if (!items || !props.onAttachmentsChange) return;
|
if (!items || !props.onAttachmentsChange) return;
|
||||||
|
|
||||||
@ -156,9 +153,7 @@ function renderAttachmentPreview(props: ChatProps) {
|
|||||||
type="button"
|
type="button"
|
||||||
aria-label="Remove attachment"
|
aria-label="Remove attachment"
|
||||||
@click=${() => {
|
@click=${() => {
|
||||||
const next = (props.attachments ?? []).filter(
|
const next = (props.attachments ?? []).filter((a) => a.id !== att.id);
|
||||||
(a) => a.id !== att.id,
|
|
||||||
);
|
|
||||||
props.onAttachmentsChange?.(next);
|
props.onAttachmentsChange?.(next);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@ -305,10 +300,10 @@ export function renderChat(props: ChatProps) {
|
|||||||
(item) => html`
|
(item) => html`
|
||||||
<div class="chat-queue__item">
|
<div class="chat-queue__item">
|
||||||
<div class="chat-queue__text">
|
<div class="chat-queue__text">
|
||||||
${item.text ||
|
${
|
||||||
(item.attachments?.length
|
item.text ||
|
||||||
? `Image (${item.attachments.length})`
|
(item.attachments?.length ? `Image (${item.attachments.length})` : "")
|
||||||
: "")}
|
}
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
class="btn chat-queue__remove"
|
class="btn chat-queue__remove"
|
||||||
@ -343,8 +338,7 @@ export function renderChat(props: ChatProps) {
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (canCompose) props.onSend();
|
if (canCompose) props.onSend();
|
||||||
}}
|
}}
|
||||||
@input=${(e: Event) =>
|
@input=${(e: Event) => props.onDraftChange((e.target as HTMLTextAreaElement).value)}
|
||||||
props.onDraftChange((e.target as HTMLTextAreaElement).value)}
|
|
||||||
@paste=${(e: ClipboardEvent) => handlePaste(e, props)}
|
@paste=${(e: ClipboardEvent) => handlePaste(e, props)}
|
||||||
placeholder=${composePlaceholder}
|
placeholder=${composePlaceholder}
|
||||||
></textarea>
|
></textarea>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user