Skip to content

Commit 3bdd7b1

Browse files
Color-code chat mode selector like Cursor's mode picker
Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 866d4a7 commit 3bdd7b1

1 file changed

Lines changed: 46 additions & 1 deletion

File tree

packages/app/src/pages/Chat.tsx

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,14 @@ type Msg = {
2020

2121
const EFFORTS = ['low', 'medium', 'high', 'max'] as const;
2222
const MODES = ['agent', 'plan', 'debug', 'review'] as const;
23+
24+
/** Per-mode accent colors so the selector reads like Cursor's mode picker. */
25+
const MODE_META: Record<string, { label: string; dot: string; text: string; chip: string }> = {
26+
agent: { label: 'Agent', dot: 'bg-emerald-500', text: 'text-emerald-500', chip: 'border-emerald-500/40 bg-emerald-500/10' },
27+
plan: { label: 'Plan', dot: 'bg-sky-500', text: 'text-sky-500', chip: 'border-sky-500/40 bg-sky-500/10' },
28+
debug: { label: 'Debug', dot: 'bg-amber-500', text: 'text-amber-500', chip: 'border-amber-500/40 bg-amber-500/10' },
29+
review: { label: 'Review', dot: 'bg-violet-500', text: 'text-violet-500', chip: 'border-violet-500/40 bg-violet-500/10' },
30+
};
2331
/** Sentinel option value: triggers the folder picker rather than selecting a project. */
2432
const ADD_FOLDER = '__add_folder__';
2533

@@ -363,7 +371,7 @@ function Composer({
363371
...(onAddFolder ? [{ value: ADD_FOLDER, label: '+ Add folder…' }] : []),
364372
]}
365373
/>
366-
<Pill value={mode} onChange={setMode} title="Mode" options={MODES.map((m) => ({ value: m, label: m }))} />
374+
<ModePill value={mode} onChange={setMode} />
367375
<Pill value={effort} onChange={setEffort} title="Reasoning effort" capitalize options={EFFORTS.map((e) => ({ value: e, label: e }))} />
368376

369377
<div className="ml-auto flex items-center gap-1.5">
@@ -464,6 +472,43 @@ function useVoiceInput(onText: (t: string) => void): { supported: boolean; liste
464472
return { supported, listening, toggle };
465473
}
466474

475+
/** Mode selector where each mode carries its own color, Cursor-style. */
476+
function ModePill({ value, onChange }: { value: string; onChange: (v: string) => void }): React.ReactElement {
477+
const meta = MODE_META[value] ?? MODE_META.agent;
478+
return (
479+
<Dropdown
480+
value={value}
481+
onChange={onChange}
482+
title="Mode"
483+
size="sm"
484+
menuWidth="w-44"
485+
options={MODES.map((m) => {
486+
const mm = MODE_META[m];
487+
return {
488+
value: m,
489+
searchText: m,
490+
label: (
491+
<span className="flex items-center gap-2">
492+
<span className={cls('h-2 w-2 shrink-0 rounded-full', mm.dot)} />
493+
<span className={cls('font-medium', mm.text)}>{mm.label}</span>
494+
</span>
495+
),
496+
buttonLabel: (
497+
<span className="flex items-center gap-1.5">
498+
<span className={cls('h-2 w-2 shrink-0 rounded-full', mm.dot)} />
499+
<span className={cls('font-medium', mm.text)}>{mm.label}</span>
500+
</span>
501+
),
502+
};
503+
})}
504+
className={cls(
505+
'flex items-center justify-between gap-1.5 rounded-md border px-2 py-1 text-xs outline-none transition-colors',
506+
meta.chip,
507+
)}
508+
/>
509+
);
510+
}
511+
467512
function Pill({
468513
value,
469514
onChange,

0 commit comments

Comments
 (0)