Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions apps/extension/entrypoints/sidepanel/agent-chat-panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { browser, storage } from '#imports';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import type { JSX, ReactNode } from 'react';
import { useAtomValue, useSetAtom, useStore } from 'jotai';
import { AlertTriangle } from 'lucide-react';
import {
compactingConversationIdsAtom,
contextUsageAtomFamily,
Expand Down Expand Up @@ -863,8 +864,9 @@ export const AgentChatPanel = ({
<ConversationList items={groupedEvents} streamingMessageId={streamingMessageId} />

{remoteMcpToolWarning === undefined ? null : (
<p className="border-t border-amber-500/30 bg-amber-950/20 px-4 py-2 text-xs text-amber-300">
{remoteMcpToolWarning}
<p className="flex items-start gap-2 border-t border-status-yellow-500/30 bg-status-yellow-500/10 px-4 py-2 text-xs text-status-yellow-300">
<AlertTriangle aria-hidden="true" className="size-3.5 shrink-0 text-status-yellow-400" />
<span className="min-w-0">{remoteMcpToolWarning}</span>
</p>
)}

Expand All @@ -876,7 +878,7 @@ export const AgentChatPanel = ({
onSubmit={submitDraft}
/>

<footer className="border-t border-zinc-900 bg-zinc-950 px-4 py-2">
<footer className="border-t border-border bg-surface-raised px-4 py-2">
<AgentFooterControls
contextDonut={contextDonut}
inspectableTabs={inspectableTabs}
Expand Down
65 changes: 40 additions & 25 deletions apps/extension/entrypoints/sidepanel/agent-conversation-events.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,8 @@ const MessageEvent = ({
<div
className={
isUser
? 'max-w-[88%] rounded-lg bg-zinc-100 px-3 py-2 text-sm leading-5 text-zinc-950'
: 'max-w-[88%] rounded-lg border border-zinc-800 bg-zinc-900 px-3 py-2 text-sm leading-5 text-zinc-200'
? 'max-w-[88%] rounded-lg border border-border bg-surface-raised px-3 py-2 text-sm leading-5 text-foreground'
: 'max-w-[88%] rounded-lg px-3 py-2 text-sm leading-5 text-foreground'
}
>
<div className="agent-message-markdown">
Expand All @@ -145,11 +145,11 @@ const ThinkingEvent = ({
}: {
event: Extract<AgentConversationEvent, { readonly type: 'thinking' }>;
}): JSX.Element => (
<details className="group rounded-md border border-zinc-800 bg-zinc-900/60 px-3 py-2">
<summary className="cursor-pointer list-none text-xs font-semibold text-zinc-400 outline-none transition hover:text-zinc-200 focus-visible:ring-2 focus-visible:ring-[#EDFF00] focus-visible:ring-offset-2 focus-visible:ring-offset-zinc-950">
<details className="group rounded-lg border border-border bg-surface-inset px-3 py-2">
<summary className="cursor-pointer list-none text-xs font-semibold text-foreground-muted outline-none transition hover:text-foreground focus-visible:ring-2 focus-visible:ring-brand-primary-ring focus-visible:ring-offset-2 focus-visible:ring-offset-surface-background">
thinking
</summary>
<div className="agent-message-markdown mt-2 text-xs leading-5 text-zinc-400">
<div className="agent-message-markdown mt-2 text-xs leading-5 text-foreground-muted">
<ReactMarkdown remarkPlugins={remarkPlugins}>{event.text}</ReactMarkdown>
</div>
</details>
Expand All @@ -166,22 +166,30 @@ const ToolExchangeEvent = ({
: undefined;

const panelClassName = isSuccessful
? 'group min-w-0 rounded-md border border-zinc-800 bg-zinc-900/70 px-3 py-2'
: 'group min-w-0 rounded-md border border-red-500/30 bg-red-950/20 px-3 py-2';
? 'group min-w-0 rounded-lg border border-border bg-surface-inset px-3 py-2'
: 'group min-w-0 rounded-lg border border-status-red-500 bg-diff-delete-surface px-3 py-2';
const titleClassName = isSuccessful
? 'text-xs font-semibold text-zinc-300'
: 'text-xs font-semibold text-red-200';
const tabClassName = isSuccessful ? 'text-[11px] text-zinc-500' : 'text-[11px] text-red-200/70';
? 'text-xs font-semibold text-foreground'
: 'text-xs font-semibold text-status-red-300';
const tabClassName = isSuccessful
? 'text-xs text-foreground-subtle'
: 'text-xs text-status-red-400';
const codeLabelClassName = isSuccessful
? 'text-[11px] font-medium text-zinc-300'
: 'text-[11px] font-medium text-red-200/80';
? 'text-xs font-medium text-foreground-muted'
: 'text-xs font-medium text-status-red-400';
const codeBlockClassName = isSuccessful
? 'mt-1 max-h-28 min-w-0 overflow-x-hidden overflow-y-auto whitespace-pre-wrap break-words font-mono text-[11px] leading-4 text-zinc-400'
: 'mt-1 max-h-28 min-w-0 overflow-x-hidden overflow-y-auto whitespace-pre-wrap break-words font-mono text-[11px] leading-4 text-red-100/90';
? 'mt-1 max-h-28 min-w-0 overflow-x-hidden overflow-y-auto whitespace-pre-wrap break-words font-mono text-xs leading-4 text-foreground-muted'
: 'mt-1 max-h-28 min-w-0 overflow-x-hidden overflow-y-auto whitespace-pre-wrap break-words font-mono text-xs leading-4 text-status-red-300/80';
const resultLabelClassName = isSuccessful
? 'text-xs font-medium text-foreground-muted'
: 'text-xs font-medium text-status-red-300';
const resultBlockClassName = isSuccessful
? 'mt-1 max-h-28 min-w-0 overflow-x-hidden overflow-y-auto whitespace-pre-wrap break-words font-mono text-xs leading-4 text-foreground-muted'
: 'mt-1 max-h-28 min-w-0 overflow-x-hidden overflow-y-auto whitespace-pre-wrap break-words font-mono text-xs leading-4 text-status-red-300/80';

return (
<details className={panelClassName}>
<summary className="flex cursor-pointer list-none items-center justify-between gap-2 outline-none transition focus-visible:ring-2 focus-visible:ring-[#EDFF00] focus-visible:ring-offset-2 focus-visible:ring-offset-zinc-950">
<summary className="flex cursor-pointer list-none items-center justify-between gap-2 outline-none transition focus-visible:ring-2 focus-visible:ring-brand-primary-ring focus-visible:ring-offset-2 focus-visible:ring-offset-surface-background">
<span className={titleClassName}>
{item.toolCall.name} {isSuccessful ? 'completed' : 'failed'}
</span>
Expand All @@ -203,17 +211,15 @@ const ToolExchangeEvent = ({
</div>
) : null}
<div className="min-w-0">
<p className="text-[11px] font-medium text-zinc-300">
{isSuccessful ? 'Result' : 'Error'}
</p>
<p className={resultLabelClassName}>{isSuccessful ? 'Result' : 'Error'}</p>
{screenshotDataUrl === undefined ? (
<pre className="mt-1 max-h-28 min-w-0 overflow-x-hidden overflow-y-auto whitespace-pre-wrap break-words font-mono text-[11px] leading-4 text-zinc-400">
<pre className={resultBlockClassName}>
{isSuccessful ? formatToolValue(item.result.value) : item.result.error}
</pre>
) : (
<img
alt="Viewport screenshot captured by get_viewport_screenshot"
className="mt-1 max-h-40 max-w-full rounded border border-zinc-800 object-contain"
className="mt-1 max-h-40 max-w-full rounded-md border border-border object-contain"
src={screenshotDataUrl}
/>
)}
Expand All @@ -238,12 +244,21 @@ const StandaloneToolEvent = ({
body = formatToolValue(event.value);
}

const isFailure = event.type === 'tool-result' && !event.ok;
const rootClassName = isFailure
? 'rounded-lg border border-status-red-500 bg-diff-delete-surface px-3 py-2'
: 'rounded-lg border border-border bg-surface-inset px-3 py-2';
const titleClassName = isFailure
? 'text-xs font-semibold text-status-red-300'
: 'text-xs font-semibold text-foreground';
const bodyClassName = isFailure
? 'mt-2 max-h-28 overflow-x-hidden overflow-y-auto whitespace-pre-wrap break-words font-mono text-xs leading-4 text-status-red-300/80'
: 'mt-2 max-h-28 overflow-x-hidden overflow-y-auto whitespace-pre-wrap break-words font-mono text-xs leading-4 text-foreground-muted';

return (
<div className="rounded-md border border-zinc-800 bg-zinc-900/70 px-3 py-2">
<p className="text-xs font-semibold text-zinc-300">{title}</p>
<pre className="mt-2 max-h-28 overflow-x-hidden overflow-y-auto whitespace-pre-wrap break-words font-mono text-[11px] leading-4 text-zinc-400">
{body}
</pre>
<div className={rootClassName}>
<p className={titleClassName}>{title}</p>
<pre className={bodyClassName}>{body}</pre>
</div>
);
};
Expand Down
20 changes: 10 additions & 10 deletions apps/extension/entrypoints/sidepanel/agent-footer-controls.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const ModeIcon = ({
icon: ReturnType<typeof getFooterControlDisplay>['modeIcon'];
tone: ReturnType<typeof getFooterControlDisplay>['modeIconTone'];
}): JSX.Element => {
const toneClassName = tone === 'safe' ? 'text-[#EDFF00]' : 'text-red-400';
const toneClassName = tone === 'safe' ? 'text-status-green-500' : 'text-status-yellow-500';

const Icon = icon === 'shield' ? Shield : TriangleAlert;

Expand All @@ -46,7 +46,7 @@ const CompactSelectControl = ({
}): JSX.Element => (
<select
aria-label={ariaLabel}
className={`h-8 min-w-0 rounded-md border border-zinc-800 bg-zinc-950 text-xs font-medium text-zinc-200 outline-none transition hover:border-zinc-700 focus:border-[#EDFF00] focus:ring-2 focus:ring-[#EDFF00]/30 disabled:cursor-not-allowed disabled:text-zinc-600 ${className}`}
className={`h-8 min-w-0 rounded-md border border-border-strong bg-input-bg type-label text-foreground outline-none transition focus-visible:ring-2 focus-visible:ring-brand-primary-ring ring-offset-2 ring-offset-surface-background disabled:cursor-not-allowed disabled:text-foreground-subtle ${className}`}
disabled={disabled}
onChange={event => {
onChange(event.currentTarget.value);
Expand Down Expand Up @@ -74,7 +74,7 @@ const ModeControl = ({
<button
aria-expanded={isOpen}
aria-label={`${display.modeLabel} mode: ${display.modeDescription}`}
className="flex h-8 w-10 items-center justify-center rounded-md border border-zinc-800 bg-zinc-950 outline-none transition hover:border-zinc-700 focus:border-[#EDFF00] focus:ring-2 focus:ring-[#EDFF00]/30"
className="flex h-8 w-10 items-center justify-center rounded-md border border-border bg-surface-overlay text-foreground-on-secondary outline-none transition hover:bg-surface-hover focus-visible:ring-2 focus-visible:ring-brand-primary-ring ring-offset-2 ring-offset-surface-background"
disabled={disabled}
onClick={() => {
setIsOpen(current => !current);
Expand All @@ -86,7 +86,7 @@ const ModeControl = ({
</button>

{isOpen && !disabled ? (
<div className="absolute bottom-10 left-0 z-10 grid w-56 gap-1 rounded-md border border-zinc-800 bg-zinc-950 p-1">
<div className="absolute bottom-10 left-0 z-10 grid w-56 gap-1 rounded-lg border border-border bg-surface-overlay p-1 shadow-lg shadow-black/50">
{modeOptions.map(option => {
const optionDisplay = getFooterControlDisplay({
mode: option.value,
Expand All @@ -98,8 +98,8 @@ const ModeControl = ({
<button
className={
option.value === mode
? 'flex items-start gap-2 rounded-sm bg-zinc-900 px-2 py-2 text-left text-zinc-100'
: 'flex items-start gap-2 rounded-sm px-2 py-2 text-left text-zinc-400 transition hover:bg-zinc-900 hover:text-zinc-100 focus:outline-none focus:ring-2 focus:ring-[#EDFF00] focus:ring-offset-1 focus:ring-offset-zinc-950'
? 'flex items-start gap-2 rounded-sm bg-surface-selected px-2 py-2 text-left text-foreground'
: 'flex items-start gap-2 rounded-sm px-2 py-2 text-left text-foreground-muted outline-none transition hover:bg-surface-hover focus-visible:ring-2 focus-visible:ring-brand-primary-ring ring-offset-2 ring-offset-surface-background'
}
key={option.value}
onClick={() => {
Expand All @@ -115,7 +115,7 @@ const ModeControl = ({
/>
<span className="grid gap-0.5">
<span className="text-xs font-medium">{option.label}</span>
<span className="text-[11px] leading-4 text-zinc-500">
<span className="type-label text-foreground-muted">
{optionDisplay.modeDescription}
</span>
</span>
Expand Down Expand Up @@ -209,7 +209,7 @@ export const AgentFooterControls = ({
</CompactSelectControl>
</div>
{tabDebuggerError === undefined ? null : (
<p className="text-xs leading-4 text-red-300">{tabDebuggerError}</p>
<p className="type-label text-status-red-400">{tabDebuggerError}</p>
)}
<div className="flex min-w-0 items-center gap-2">
<ModeControl
Expand Down Expand Up @@ -255,9 +255,9 @@ export const AgentFooterControls = ({
</div>
{modelLoadError === undefined ? null : (
<div className="flex items-center justify-between gap-2">
<p className="text-xs leading-4 text-red-300">{modelLoadError}</p>
<p className="type-label text-status-red-400">{modelLoadError}</p>
<button
className="h-7 shrink-0 rounded-md border border-zinc-700 px-2 text-xs font-medium text-zinc-200 transition hover:border-zinc-600 hover:bg-zinc-900 focus:outline-none focus:ring-2 focus:ring-[#EDFF00] focus:ring-offset-2 focus:ring-offset-zinc-950"
className="h-8 shrink-0 rounded-md border border-border bg-surface-overlay px-2 type-label text-foreground-on-secondary outline-none transition hover:bg-surface-hover focus-visible:ring-2 focus-visible:ring-brand-primary-ring ring-offset-2 ring-offset-surface-background"
onClick={() => {
void onRetryModels();
}}
Expand Down
Loading