Skip to content

Commit f1a857e

Browse files
nedtwiggclaude
andcommitted
Neutralize active pane colors when window loses focus.
Adds WindowFocusedContext so tab headers, selection overlay, and doors all respond to window blur — making it visually clear the window is inactive. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent ac6e2b5 commit f1a857e

File tree

3 files changed

+15
-5
lines changed

3 files changed

+15
-5
lines changed

lib/src/components/Baseboard.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { useRef, useState, useMemo, useLayoutEffect, useContext, useSyncExternalStore, type ReactNode } from 'react';
22
import { CaretLeftIcon, CaretRightIcon } from '@phosphor-icons/react';
33
import { Door } from './Door';
4-
import { DoorElementsContext, type DetachedItem } from './Pond';
4+
import { DoorElementsContext, WindowFocusedContext, type DetachedItem } from './Pond';
55
import { DEFAULT_SESSION_UI_STATE, getSessionStateSnapshot, subscribeToSessionStateChanges } from '../lib/terminal-registry';
66

77
export interface BaseboardProps {
@@ -13,6 +13,7 @@ export interface BaseboardProps {
1313

1414
export function Baseboard({ items, activeId, onReattach, notice }: BaseboardProps) {
1515
const { elements: doorElements, bumpVersion } = useContext(DoorElementsContext);
16+
const windowFocused = useContext(WindowFocusedContext);
1617
const sessionStates = useSyncExternalStore(subscribeToSessionStateChanges, getSessionStateSnapshot);
1718
const containerRef = useRef<HTMLDivElement>(null);
1819
const [containerWidth, setContainerWidth] = useState(0);
@@ -176,6 +177,7 @@ export function Baseboard({ items, activeId, onReattach, notice }: BaseboardProp
176177
doorId={item.id}
177178
title={item.title}
178179
isActive={activeId === item.id}
180+
windowFocused={windowFocused}
179181
status={sessionState.status}
180182

181183
todo={sessionState.todo}

lib/src/components/Door.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ export interface DoorProps {
55
doorId?: string;
66
title: string;
77
isActive?: boolean;
8+
windowFocused?: boolean;
89
status?: SessionStatus;
910
todo?: TodoState;
1011
onClick?: () => void;
@@ -14,6 +15,7 @@ export function Door({
1415
doorId,
1516
title,
1617
isActive = false,
18+
windowFocused = true,
1719
status = 'ALARM_DISABLED',
1820
todo = false,
1921
onClick,
@@ -46,7 +48,7 @@ export function Door({
4648
onClick={onClick}
4749
title={title}
4850
>
49-
<span className={['min-w-0 flex-1 truncate', isActive ? 'text-foreground' : 'text-muted'].join(' ')}>
51+
<span className={['min-w-0 flex-1 truncate', (isActive && windowFocused) ? 'text-foreground' : 'text-muted'].join(' ')}>
5052
{title}
5153
</span>
5254
{(todo || alarmEnabled) && (
@@ -60,7 +62,7 @@ export function Door({
6062
</span>
6163
)}
6264
{alarmEnabled && (
63-
<span className={['relative', alarmRinging ? 'text-warning' : isActive ? 'text-foreground' : 'text-muted'].join(' ')}>
65+
<span className={['relative', alarmRinging ? 'text-warning' : (isActive && windowFocused) ? 'text-foreground' : 'text-muted'].join(' ')}>
6466
<BellIcon size={11} weight="fill" />
6567
{(status === 'MIGHT_BE_BUSY' || status === 'BUSY' || status === 'MIGHT_NEED_ATTENTION') && (
6668
<span className={[

lib/src/components/Pond.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,7 @@ export const PondActionsContext = createContext<PondActions>({
430430

431431
export const RenamingIdContext = createContext<string | null>(null);
432432
export const ZoomedContext = createContext(false);
433+
export const WindowFocusedContext = createContext(true);
433434

434435
const ARROW_OPPOSITES: Record<string, string> = {
435436
ArrowLeft: 'ArrowRight', ArrowRight: 'ArrowLeft',
@@ -487,11 +488,12 @@ export function TerminalPaneHeader({ api }: IDockviewPanelHeaderProps) {
487488
const selectedId = useContext(SelectedIdContext);
488489
const renamingId = useContext(RenamingIdContext);
489490
const zoomed = useContext(ZoomedContext);
491+
const windowFocused = useContext(WindowFocusedContext);
490492
const sessionStates = useSyncExternalStore(subscribeToSessionStateChanges, getSessionStateSnapshot);
491493
const actions = useContext(PondActionsContext);
492494
const sessionState = sessionStates.get(api.id) ?? DEFAULT_SESSION_UI_STATE;
493495
const isSelected = selectedId === api.id;
494-
const showSelectedHeader = mode === 'passthrough' && isSelected;
496+
const showSelectedHeader = mode === 'passthrough' && isSelected && windowFocused;
495497
const isRenaming = renamingId === api.id;
496498
const tabRef = useRef<HTMLDivElement>(null);
497499
const suppressAlarmClickRef = useRef(false);
@@ -812,7 +814,7 @@ function SelectionOverlay({ apiRef, selectedId, selectedType, mode }: {
812814
const { elements: panelElements, version: panelVersion } = useContext(PanelElementsContext);
813815
const { elements: doorElements, version: doorVersion } = useContext(DoorElementsContext);
814816
const selectionColor = useSelectionColor();
815-
const windowFocused = useWindowFocused();
817+
const windowFocused = useContext(WindowFocusedContext);
816818
const [rect, setRect] = useState<{ top: number; left: number; width: number; height: number } | null>(null);
817819
const isDoor = selectedType === 'door';
818820

@@ -998,6 +1000,8 @@ export function Pond({
9981000
const [selectedId, setSelectedId] = useState<string | null>(null);
9991001
const [selectedType, setSelectedType] = useState<'pane' | 'door'>('pane');
10001002

1003+
const windowFocused = useWindowFocused();
1004+
10011005
// UI state
10021006
const [confirmKill, setConfirmKill] = useState<ConfirmKill | null>(null);
10031007
useEffect(() => { if (!confirmKill) { clearTimeout(shakeTimerRef.current!); } }, [confirmKill]);
@@ -1816,6 +1820,7 @@ export function Pond({
18161820
<DoorElementsContext.Provider value={{ elements: doorElements, version: doorElementsVersion, bumpVersion: bumpDoorElementsVersion }}>
18171821
<RenamingIdContext.Provider value={renamingPaneId}>
18181822
<ZoomedContext.Provider value={zoomed}>
1823+
<WindowFocusedContext.Provider value={windowFocused}>
18191824
<div className="flex-1 min-h-0 flex flex-col bg-surface text-foreground font-sans overflow-hidden">
18201825
{/* Dockview */}
18211826
<div className="flex-1 min-h-0 relative p-1.5">
@@ -1844,6 +1849,7 @@ export function Pond({
18441849
)}
18451850

18461851
</div>
1852+
</WindowFocusedContext.Provider>
18471853
</ZoomedContext.Provider>
18481854
</RenamingIdContext.Provider>
18491855
</DoorElementsContext.Provider>

0 commit comments

Comments
 (0)