Skip to content

Commit e0dcfb6

Browse files
nedtwiggclaude
andcommitted
Rework alarm icon: tilt for escalation, rock for ringing
Replace the flashing colored dots with progressive bell rotations (-22.5°/45°/60° for MIGHT_BE_BUSY/BUSY/MIGHT_NEED_ATTENTION) and replace the subtle button pulse with a bell rocking ±45° for ALARM_RINGING. No background tint on the ringing state — just the warning foreground color plus motion carries the signal. Adds cfg.alarm.ringingPaused (flipped under Chromatic UA, mirroring the marching-ants pattern) so visual-regression snapshots freeze the bell at +45° instead of capturing random animation frames. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 266e2fa commit e0dcfb6

5 files changed

Lines changed: 43 additions & 29 deletions

File tree

lib/.storybook/preview.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,10 @@ import { cfg } from '../src/cfg';
1717
// Initialize fake platform once at module scope
1818
const fakePlatform = initPlatform('fake');
1919

20-
// Pin marching-ants animation at T=0 for deterministic Chromatic snapshots
20+
// Pin animations at T=0 for deterministic Chromatic snapshots
2121
if (window?.navigator?.userAgent?.includes('Chromatic')) {
2222
cfg.marchingAnts.paused = true;
23+
cfg.alarm.ringingPaused = true;
2324
}
2425

2526
// Collect all CSS variable names across all themes for cleanup

lib/src/cfg.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ export const cfg = {
2626
resizeDebounce: 500,
2727
/** ms — attention idle expiry. How long before "looking at this pane" wears off. */
2828
userAttention: 15_000,
29+
/** When true, the ALARM_RINGING bell-ring animation is frozen at T=0 (for deterministic Chromatic snapshots). */
30+
ringingPaused: false,
2931
},
3032
todoBucket: {
3133
/** Seconds of idle time needed to un-strike one letter of the soft-TODO pill.

lib/src/components/Door.tsx

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { BellIcon } from '@phosphor-icons/react';
22
import { TODO_OFF, isSoftTodo, type SessionStatus, type TodoState } from '../lib/terminal-registry';
33
import { useTodoPillContent } from './TodoPillBody';
4+
import { cfg } from '../cfg';
45

56
export interface DoorProps {
67
doorId?: string;
@@ -37,9 +38,7 @@ export function Door({
3738
className={[
3839
'relative flex h-6 max-w-[220px] min-w-[68px] items-center gap-2 overflow-hidden px-2.5',
3940
'rounded-t-md',
40-
alarmRinging
41-
? 'bg-warning/10 motion-safe:animate-pulse motion-reduce:animate-none ring-1 ring-warning/60'
42-
: 'bg-surface',
41+
'bg-surface',
4342
'text-[10px] font-medium font-mono tracking-[0.02em]',
4443
'transition-colors hover:bg-surface-raised',
4544
].join(' ')}
@@ -66,16 +65,22 @@ export function Door({
6665
</span>
6766
)}
6867
{alarmEnabled && (
69-
<span className={['relative', alarmRinging ? 'text-warning' : (isActive && windowFocused) ? 'text-foreground' : 'text-muted'].join(' ')}>
70-
<BellIcon size={11} weight="fill" />
71-
{(status === 'MIGHT_BE_BUSY' || status === 'BUSY' || status === 'MIGHT_NEED_ATTENTION') && (
72-
<span className={[
73-
'absolute -top-0.5 -right-0.5 h-1 w-1 rounded-full',
74-
status === 'MIGHT_BE_BUSY' && 'bg-foreground/40',
75-
status === 'BUSY' && 'bg-accent motion-safe:animate-alarm-dot motion-reduce:animate-none',
76-
status === 'MIGHT_NEED_ATTENTION' && 'bg-warning/60 motion-safe:animate-alarm-dot motion-reduce:animate-none',
77-
].filter(Boolean).join(' ')} />
78-
)}
68+
<span className={[alarmRinging ? 'text-warning' : (isActive && windowFocused) ? 'text-foreground' : 'text-muted'].join(' ')}>
69+
<BellIcon
70+
size={11}
71+
weight="fill"
72+
className={[
73+
'transition-transform',
74+
status === 'MIGHT_BE_BUSY' && '-rotate-[22.5deg]',
75+
status === 'BUSY' && 'rotate-45',
76+
status === 'MIGHT_NEED_ATTENTION' && 'rotate-[60deg]',
77+
status === 'ALARM_RINGING' && (
78+
cfg.alarm.ringingPaused
79+
? 'rotate-45'
80+
: 'motion-safe:animate-bell-ring motion-reduce:rotate-45'
81+
),
82+
].filter(Boolean).join(' ')}
83+
/>
7984
</span>
8085
)}
8186
</span>

lib/src/components/Pond.tsx

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -702,7 +702,7 @@ export function TerminalPaneHeader({ api }: IDockviewPanelHeaderProps) {
702702
className={[
703703
'flex h-5 min-w-5 items-center justify-center rounded transition-colors shrink-0',
704704
sessionState.status === 'ALARM_RINGING'
705-
? 'bg-warning/15 text-warning hover:bg-warning/20 motion-safe:animate-pulse motion-reduce:animate-none'
705+
? 'text-warning hover:bg-foreground/10'
706706
: 'text-muted hover:bg-foreground/10 hover:text-foreground',
707707
].join(' ')}
708708
onMouseDownCapture={(e) => {
@@ -725,19 +725,25 @@ export function TerminalPaneHeader({ api }: IDockviewPanelHeaderProps) {
725725
tooltip={alarmButtonTooltip}
726726
dataAlarmButtonFor={api.id}
727727
>
728-
<span className="relative flex items-center justify-center">
728+
<span className="flex items-center justify-center">
729729
{sessionState.status === 'ALARM_DISABLED' ? (
730730
<BellSlashIcon size={14} />
731731
) : (
732-
<BellIcon size={14} weight="fill" />
733-
)}
734-
{(sessionState.status === 'MIGHT_BE_BUSY' || sessionState.status === 'BUSY' || sessionState.status === 'MIGHT_NEED_ATTENTION') && (
735-
<span className={[
736-
'absolute -top-0.5 -right-0.5 h-[6px] w-[6px] rounded-full border border-surface-alt',
737-
sessionState.status === 'MIGHT_BE_BUSY' && 'bg-foreground/40',
738-
sessionState.status === 'BUSY' && 'bg-accent motion-safe:animate-alarm-dot motion-reduce:animate-none',
739-
sessionState.status === 'MIGHT_NEED_ATTENTION' && 'bg-warning/60 motion-safe:animate-alarm-dot motion-reduce:animate-none',
740-
].filter(Boolean).join(' ')} />
732+
<BellIcon
733+
size={14}
734+
weight="fill"
735+
className={[
736+
'transition-transform',
737+
sessionState.status === 'MIGHT_BE_BUSY' && '-rotate-[22.5deg]',
738+
sessionState.status === 'BUSY' && 'rotate-45',
739+
sessionState.status === 'MIGHT_NEED_ATTENTION' && 'rotate-[60deg]',
740+
sessionState.status === 'ALARM_RINGING' && (
741+
cfg.alarm.ringingPaused
742+
? 'rotate-45'
743+
: 'motion-safe:animate-bell-ring motion-reduce:rotate-45'
744+
),
745+
].filter(Boolean).join(' ')}
746+
/>
741747
)}
742748
</span>
743749
</HeaderActionButton>

lib/src/theme.css

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@
6767
--color-button-hover-bg: var(--vscode-button-hoverBackground, #1177bb);
6868

6969
/* Animation */
70-
--animate-alarm-dot: alarm-dot 2s ease-in-out infinite;
70+
--animate-bell-ring: bell-ring 800ms ease-in-out infinite;
7171
--animate-shake-x: shake-x 400ms ease-out;
7272
}
7373

@@ -133,9 +133,9 @@ body.vscode-light {
133133
}
134134
}
135135

136-
@keyframes alarm-dot {
137-
0%, 100% { opacity: 1; }
138-
50% { opacity: 0.4; }
136+
@keyframes bell-ring {
137+
0%, 100% { transform: rotate(45deg); }
138+
50% { transform: rotate(-45deg); }
139139
}
140140

141141
@keyframes shake-x {

0 commit comments

Comments
 (0)