bell: move it left, and let its colour say what it will do
the bell sat on the right between the connection dot and the end-session plug, which put a SETTING in among the STATUS. everything on the right reports what the chat is doing now; the bell says what it will do later. it moves to the left of the header, on its own. and it is coloured by preference, so the setting is readable without opening the menu: green for every end of turn, amber for input-needed, muted for none. green is `green-500` rather than the `text-success` token deliberately — it has to be the SAME green as the connection dot a few elements away, and a semantic token that drifted from it would leave two greens in one header meaning two different things. amber is `text-warning` (#f59e0b), which is the conditional setting: it will speak, but only sometimes. `none` keeps the struck-through bell as well as the muted colour. colour alone is not a state anyone should have to learn, and it is the one setting where being wrong is silent.
This commit is contained in:
@@ -108,6 +108,11 @@ function DetailBar({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="shrink-0 flex items-center px-4 py-2 border-b border-border bg-background/60">
|
<div className="shrink-0 flex items-center px-4 py-2 border-b border-border bg-background/60">
|
||||||
|
{/* Left, away from the connection dot and the end-session plug. It is a setting, not a status: it
|
||||||
|
says what this chat WILL do, while everything on the right reports what it is doing now. */}
|
||||||
|
<div className="flex items-center">
|
||||||
|
{sessionId && <NotifyMenu preference={notifyPreference ?? 'none'} onChange={commitNotifyPreference} />}
|
||||||
|
</div>
|
||||||
<div className="flex-1 min-w-0 px-3 text-center">
|
<div className="flex-1 min-w-0 px-3 text-center">
|
||||||
<EditableTitle
|
<EditableTitle
|
||||||
value={sessionTitle ?? 'New chat'}
|
value={sessionTitle ?? 'New chat'}
|
||||||
@@ -133,7 +138,6 @@ function DetailBar({
|
|||||||
<span className="inline-block h-2 w-2 rounded-full bg-green-500" />
|
<span className="inline-block h-2 w-2 rounded-full bg-green-500" />
|
||||||
)}
|
)}
|
||||||
<span>{!isConnected ? 'Disconnected' : isGenerating ? 'Working...' : ''}</span>
|
<span>{!isConnected ? 'Disconnected' : isGenerating ? 'Working...' : ''}</span>
|
||||||
{sessionId && <NotifyMenu preference={notifyPreference ?? 'none'} onChange={commitNotifyPreference} />}
|
|
||||||
{isConnected && onDisconnect && (
|
{isConnected && onDisconnect && (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
|||||||
@@ -10,6 +10,23 @@ const OPTIONS: { value: NotifyPreference; label: string }[] = [
|
|||||||
{ value: 'none', label: 'None' },
|
{ value: 'none', label: 'None' },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The bell says what it will do without being opened.
|
||||||
|
*
|
||||||
|
* Green is `green-500` on purpose rather than `text-success` — it has to be the SAME green as the
|
||||||
|
* connection dot two elements away, and a semantic token that drifted from it would leave two greens in
|
||||||
|
* one header meaning two different things.
|
||||||
|
*
|
||||||
|
* Amber for `input_needed` because it is the conditional setting: it will speak, but only sometimes.
|
||||||
|
* Muted for `none`, which is also the only state that draws a struck-through bell — colour alone is not
|
||||||
|
* a state anyone should have to learn.
|
||||||
|
*/
|
||||||
|
const BELL_TONE: Record<NotifyPreference, string> = {
|
||||||
|
every_turn: 'text-green-500',
|
||||||
|
input_needed: 'text-warning',
|
||||||
|
none: 'text-muted-foreground',
|
||||||
|
};
|
||||||
|
|
||||||
type NotifyMenuProps = {
|
type NotifyMenuProps = {
|
||||||
preference: NotifyPreference;
|
preference: NotifyPreference;
|
||||||
onChange: (preference: NotifyPreference) => void;
|
onChange: (preference: NotifyPreference) => void;
|
||||||
@@ -40,7 +57,7 @@ export const NotifyMenu = ({ preference, onChange }: NotifyMenuProps) => {
|
|||||||
type="button"
|
type="button"
|
||||||
title={`Notifications: ${OPTIONS.find((o) => o.value === preference)?.label ?? 'None'}`}
|
title={`Notifications: ${OPTIONS.find((o) => o.value === preference)?.label ?? 'None'}`}
|
||||||
aria-label="Notification preference"
|
aria-label="Notification preference"
|
||||||
className="p-1 rounded hover:bg-muted transition-colors cursor-pointer"
|
className={`rounded p-1 transition-colors hover:bg-muted cursor-pointer ${BELL_TONE[preference]}`}
|
||||||
>
|
>
|
||||||
{preference === 'none' ? <BellOff className="h-3.5 w-3.5" /> : <Bell className="h-3.5 w-3.5" />}
|
{preference === 'none' ? <BellOff className="h-3.5 w-3.5" /> : <Bell className="h-3.5 w-3.5" />}
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
Reference in New Issue
Block a user