refactor(focus): 몰입모드 토글 제거 및 Focus-First 기본 구조로 전환
맥락: - 몰입모드 토글은 상태 인지 비용을 높여 집중 흐름을 끊고, Quick Controls 헤더를 대시보드형으로 보이게 만들고 있었습니다. 변경사항: - Quick Controls에서 기본/몰입 모드 토글 UI를 완전히 제거했습니다. - Focus 화면의 HUD 톤은 외부 모드 상태 없이 항상 몰입 톤으로 렌더링되도록 고정했습니다. - workspace/tools-dock/focus-hud 간 모드 토글 상태 전달 경로를 정리해, 컨트롤은 패널을 열었을 때만 보이는 Focus-First 흐름으로 단순화했습니다. 검증: - npx tsc --noEmit 세션-상태: 모드 토글 없이 패널 열림 상태만으로 컨트롤 노출이 정의됩니다. 세션-다음: (선택) 컨트롤 자동 숨김 표시 정책 옵션을 패널 내부에 추가합니다. 세션-리스크: 자동 숨김 정책이 아직 없어 패널을 열어둔 채 방치되는 경우 수동 닫기가 필요합니다.
This commit is contained in:
@@ -16,11 +16,9 @@ interface ControlCenterSheetWidgetProps {
|
|||||||
rooms: RoomTheme[];
|
rooms: RoomTheme[];
|
||||||
selectedRoomId: string;
|
selectedRoomId: string;
|
||||||
selectedTimerLabel: string;
|
selectedTimerLabel: string;
|
||||||
isImmersionMode: boolean;
|
|
||||||
sceneRecommendedSoundLabel: string;
|
sceneRecommendedSoundLabel: string;
|
||||||
sceneRecommendedTimerLabel: string;
|
sceneRecommendedTimerLabel: string;
|
||||||
timerPresets: TimerPreset[];
|
timerPresets: TimerPreset[];
|
||||||
onImmersionModeChange: (next: boolean) => void;
|
|
||||||
onSelectRoom: (roomId: string) => void;
|
onSelectRoom: (roomId: string) => void;
|
||||||
onSelectTimer: (timerLabel: string) => void;
|
onSelectTimer: (timerLabel: string) => void;
|
||||||
onLockedClick: (source: string) => void;
|
onLockedClick: (source: string) => void;
|
||||||
@@ -49,11 +47,9 @@ export const ControlCenterSheetWidget = ({
|
|||||||
rooms,
|
rooms,
|
||||||
selectedRoomId,
|
selectedRoomId,
|
||||||
selectedTimerLabel,
|
selectedTimerLabel,
|
||||||
isImmersionMode,
|
|
||||||
sceneRecommendedSoundLabel,
|
sceneRecommendedSoundLabel,
|
||||||
sceneRecommendedTimerLabel,
|
sceneRecommendedTimerLabel,
|
||||||
timerPresets,
|
timerPresets,
|
||||||
onImmersionModeChange,
|
|
||||||
onSelectRoom,
|
onSelectRoom,
|
||||||
onSelectTimer,
|
onSelectTimer,
|
||||||
onLockedClick,
|
onLockedClick,
|
||||||
@@ -74,41 +70,6 @@ export const ControlCenterSheetWidget = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<section className="space-y-2 rounded-2xl border border-white/12 bg-black/22 p-3 backdrop-blur-md">
|
|
||||||
<div className="flex items-center justify-between gap-2">
|
|
||||||
<p className="text-[11px] text-white/64">모드</p>
|
|
||||||
<div className="inline-flex items-center rounded-full border border-white/14 bg-white/[0.03] p-0.5">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={() => onImmersionModeChange(false)}
|
|
||||||
className={cn(
|
|
||||||
'h-7 rounded-full border px-3 text-[11px] transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-sky-200/70',
|
|
||||||
!isImmersionMode
|
|
||||||
? 'border-white/15 bg-white/10 text-white/90'
|
|
||||||
: 'border-white/10 bg-transparent text-white/60 hover:text-white/78',
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
기본
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={() => onImmersionModeChange(true)}
|
|
||||||
className={cn(
|
|
||||||
'h-7 rounded-full border px-3 text-[11px] transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-sky-200/70',
|
|
||||||
isImmersionMode
|
|
||||||
? 'border-white/15 bg-white/10 text-white/90'
|
|
||||||
: 'border-white/10 bg-transparent text-white/60 hover:text-white/78',
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
몰입
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<p className="text-[11px] text-white/54">
|
|
||||||
{isImmersionMode ? '필수만 남기고 숨김' : '모든 컨트롤 표시'}
|
|
||||||
</p>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section className="space-y-2.5 rounded-2xl border border-white/12 bg-black/22 p-3.5 backdrop-blur-md">
|
<section className="space-y-2.5 rounded-2xl border border-white/12 bg-black/22 p-3.5 backdrop-blur-md">
|
||||||
<SectionTitle title="Scene" description={selectedRoom?.name ?? '공간'} />
|
<SectionTitle title="Scene" description={selectedRoom?.name ?? '공간'} />
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ interface SpaceFocusHudWidgetProps {
|
|||||||
goal: string;
|
goal: string;
|
||||||
timerLabel: string;
|
timerLabel: string;
|
||||||
visible: boolean;
|
visible: boolean;
|
||||||
isImmersionMode: boolean;
|
|
||||||
onGoalUpdate: (nextGoal: string) => void;
|
onGoalUpdate: (nextGoal: string) => void;
|
||||||
statusLine: HudStatusLineItem | null;
|
statusLine: HudStatusLineItem | null;
|
||||||
onStatusAction: () => void;
|
onStatusAction: () => void;
|
||||||
@@ -20,7 +19,6 @@ export const SpaceFocusHudWidget = ({
|
|||||||
goal,
|
goal,
|
||||||
timerLabel,
|
timerLabel,
|
||||||
visible,
|
visible,
|
||||||
isImmersionMode,
|
|
||||||
onGoalUpdate,
|
onGoalUpdate,
|
||||||
statusLine,
|
statusLine,
|
||||||
onStatusAction,
|
onStatusAction,
|
||||||
@@ -110,7 +108,7 @@ export const SpaceFocusHudWidget = ({
|
|||||||
}
|
}
|
||||||
: null
|
: null
|
||||||
}
|
}
|
||||||
isImmersionMode={isImmersionMode}
|
isImmersionMode
|
||||||
className="pr-[4.2rem]"
|
className="pr-[4.2rem]"
|
||||||
onGoalCompleteRequest={handleOpenCompleteSheet}
|
onGoalCompleteRequest={handleOpenCompleteSheet}
|
||||||
onStatusAction={onStatusAction}
|
onStatusAction={onStatusAction}
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ import { QuickSoundPopover } from './popovers/QuickSoundPopover';
|
|||||||
import { InboxToolPanel } from './panels/InboxToolPanel';
|
import { InboxToolPanel } from './panels/InboxToolPanel';
|
||||||
interface SpaceToolsDockWidgetProps {
|
interface SpaceToolsDockWidgetProps {
|
||||||
isFocusMode: boolean;
|
isFocusMode: boolean;
|
||||||
isImmersionMode: boolean;
|
|
||||||
rooms: RoomTheme[];
|
rooms: RoomTheme[];
|
||||||
selectedRoomId: string;
|
selectedRoomId: string;
|
||||||
selectedTimerLabel: string;
|
selectedTimerLabel: string;
|
||||||
@@ -43,14 +42,12 @@ interface SpaceToolsDockWidgetProps {
|
|||||||
onRestoreThought: (thought: RecentThought) => void;
|
onRestoreThought: (thought: RecentThought) => void;
|
||||||
onClearInbox: () => RecentThought[];
|
onClearInbox: () => RecentThought[];
|
||||||
onResetToSceneRecommended: () => void;
|
onResetToSceneRecommended: () => void;
|
||||||
onImmersionModeChange: (next: boolean) => void;
|
|
||||||
onStatusMessage: (payload: HudStatusLinePayload) => void;
|
onStatusMessage: (payload: HudStatusLinePayload) => void;
|
||||||
onExitRequested: () => void;
|
onExitRequested: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SpaceToolsDockWidget = ({
|
export const SpaceToolsDockWidget = ({
|
||||||
isFocusMode,
|
isFocusMode,
|
||||||
isImmersionMode,
|
|
||||||
rooms,
|
rooms,
|
||||||
selectedRoomId,
|
selectedRoomId,
|
||||||
selectedTimerLabel,
|
selectedTimerLabel,
|
||||||
@@ -73,7 +70,6 @@ export const SpaceToolsDockWidget = ({
|
|||||||
onRestoreThought,
|
onRestoreThought,
|
||||||
onClearInbox,
|
onClearInbox,
|
||||||
onResetToSceneRecommended,
|
onResetToSceneRecommended,
|
||||||
onImmersionModeChange,
|
|
||||||
onStatusMessage,
|
onStatusMessage,
|
||||||
onExitRequested,
|
onExitRequested,
|
||||||
}: SpaceToolsDockWidgetProps) => {
|
}: SpaceToolsDockWidgetProps) => {
|
||||||
@@ -415,11 +411,9 @@ export const SpaceToolsDockWidget = ({
|
|||||||
rooms={rooms}
|
rooms={rooms}
|
||||||
selectedRoomId={selectedRoomId}
|
selectedRoomId={selectedRoomId}
|
||||||
selectedTimerLabel={selectedTimerLabel}
|
selectedTimerLabel={selectedTimerLabel}
|
||||||
isImmersionMode={isImmersionMode}
|
|
||||||
sceneRecommendedSoundLabel={sceneRecommendedSoundLabel}
|
sceneRecommendedSoundLabel={sceneRecommendedSoundLabel}
|
||||||
sceneRecommendedTimerLabel={sceneRecommendedTimerLabel}
|
sceneRecommendedTimerLabel={sceneRecommendedTimerLabel}
|
||||||
timerPresets={timerPresets}
|
timerPresets={timerPresets}
|
||||||
onImmersionModeChange={onImmersionModeChange}
|
|
||||||
onSelectRoom={(roomId) => {
|
onSelectRoom={(roomId) => {
|
||||||
onRoomSelect(roomId);
|
onRoomSelect(roomId);
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -167,7 +167,6 @@ export const SpaceWorkspaceWidget = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const [workspaceMode, setWorkspaceMode] = useState<WorkspaceMode>('setup');
|
const [workspaceMode, setWorkspaceMode] = useState<WorkspaceMode>('setup');
|
||||||
const [isImmersionMode, setImmersionMode] = useState(false);
|
|
||||||
const [selectedRoomId, setSelectedRoomId] = useState(initialRoomId);
|
const [selectedRoomId, setSelectedRoomId] = useState(initialRoomId);
|
||||||
const [selectedTimerLabel, setSelectedTimerLabel] = useState(initialTimerLabel);
|
const [selectedTimerLabel, setSelectedTimerLabel] = useState(initialTimerLabel);
|
||||||
const [goalInput, setGoalInput] = useState(initialGoal);
|
const [goalInput, setGoalInput] = useState(initialGoal);
|
||||||
@@ -378,7 +377,6 @@ export const SpaceWorkspaceWidget = () => {
|
|||||||
goal={goalInput.trim()}
|
goal={goalInput.trim()}
|
||||||
timerLabel={selectedTimerLabel}
|
timerLabel={selectedTimerLabel}
|
||||||
visible={isFocusMode}
|
visible={isFocusMode}
|
||||||
isImmersionMode={isImmersionMode}
|
|
||||||
statusLine={activeStatus}
|
statusLine={activeStatus}
|
||||||
onStatusAction={runActiveAction}
|
onStatusAction={runActiveAction}
|
||||||
onStatusMessage={pushStatusLine}
|
onStatusMessage={pushStatusLine}
|
||||||
@@ -407,8 +405,6 @@ export const SpaceWorkspaceWidget = () => {
|
|||||||
onSetSoundVolume={setMasterVolume}
|
onSetSoundVolume={setMasterVolume}
|
||||||
isSoundMuted={isMuted}
|
isSoundMuted={isMuted}
|
||||||
onSetSoundMuted={setMuted}
|
onSetSoundMuted={setMuted}
|
||||||
isImmersionMode={isImmersionMode}
|
|
||||||
onImmersionModeChange={setImmersionMode}
|
|
||||||
onCaptureThought={(note) => addThought(note, selectedRoom.name)}
|
onCaptureThought={(note) => addThought(note, selectedRoom.name)}
|
||||||
onDeleteThought={removeThought}
|
onDeleteThought={removeThought}
|
||||||
onSetThoughtCompleted={setThoughtCompleted}
|
onSetThoughtCompleted={setThoughtCompleted}
|
||||||
|
|||||||
Reference in New Issue
Block a user