diff --git a/src/widgets/control-center-sheet/index.ts b/src/widgets/control-center-sheet/index.ts
deleted file mode 100644
index feb4811..0000000
--- a/src/widgets/control-center-sheet/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export * from './ui/ControlCenterSheetWidget';
diff --git a/src/widgets/control-center-sheet/ui/ControlCenterSheetWidget.tsx b/src/widgets/control-center-sheet/ui/ControlCenterSheetWidget.tsx
deleted file mode 100644
index e48ee88..0000000
--- a/src/widgets/control-center-sheet/ui/ControlCenterSheetWidget.tsx
+++ /dev/null
@@ -1,245 +0,0 @@
-'use client';
-
-import { useMemo } from 'react';
-import type { PlanTier } from '@/entities/plan';
-import {
- PRO_FEATURE_CARDS,
-} from '@/entities/plan';
-import type { SceneTheme } from '@/entities/scene';
-import { getSceneCardBackgroundStyle, type SceneAssetMap } from '@/entities/media';
-import { SOUND_PRESETS, type TimerPreset } from '@/entities/session';
-import { copy } from '@/shared/i18n';
-import { cn } from '@/shared/lib/cn';
-import { useReducedMotion } from '@/shared/lib/useReducedMotion';
-import { useDragScroll } from '@/shared/lib/useDragScroll';
-import { Toggle } from '@/shared/ui';
-
-interface ControlCenterSheetWidgetProps {
- plan: PlanTier;
- scenes: SceneTheme[];
- sceneAssetMap?: SceneAssetMap;
- selectedSceneId: string;
- selectedTimerLabel: string;
- selectedSoundPresetId: string;
- sceneRecommendedSoundLabel: string;
- sceneRecommendedTimerLabel: string;
- timerPresets: TimerPreset[];
- autoHideControls: boolean;
- onAutoHideControlsChange: (next: boolean) => void;
- onSelectScene: (sceneId: string) => void;
- onSelectTimer: (timerLabel: string) => void;
- onSelectSound: (presetId: string) => void;
- onSelectProFeature: (featureId: string) => void;
- onLockedClick: (source: string) => void;
-}
-
-const SectionTitle = ({ title, description }: { title: string; description: string }) => {
- return (
-
- {title}
- {description}
-
- );
-};
-
-export const ControlCenterSheetWidget = ({
- plan,
- scenes,
- sceneAssetMap,
- selectedSceneId,
- selectedTimerLabel,
- selectedSoundPresetId,
- sceneRecommendedSoundLabel,
- sceneRecommendedTimerLabel,
- timerPresets,
- autoHideControls,
- onAutoHideControlsChange,
- onSelectScene,
- onSelectTimer,
- onSelectSound,
- onSelectProFeature,
- onLockedClick,
-}: ControlCenterSheetWidgetProps) => {
- const { controlCenter } = copy.space;
- const reducedMotion = useReducedMotion();
- const isPro = plan === 'pro';
- const interactiveMotionClass = reducedMotion
- ? 'transition-none'
- : 'transition-[transform,background-color,border-color,box-shadow,color,opacity] duration-[220ms] ease-out';
- const colorMotionClass = reducedMotion
- ? 'transition-none'
- : 'transition-colors duration-[220ms] ease-out';
-
- const selectedScene = useMemo(() => {
- return scenes.find((scene) => scene.id === selectedSceneId) ?? scenes[0];
- }, [scenes, selectedSceneId]);
-
- const {
- containerRef: sceneContainerRef,
- events: sceneDragEvents,
- isDragging: isSceneDragging,
- shouldSuppressClick: shouldSuppressSceneClick,
- } = useDragScroll();
-
- return (
-
-
-
-
- {scenes.slice(0, 6).map((scene) => {
- const selected = scene.id === selectedSceneId;
-
- return (
-
- );
- })}
-
-
-
-
-
-
- {timerPresets.slice(0, 3).map((preset) => {
- const selected = preset.label === selectedTimerLabel;
-
- return (
-
- );
- })}
-
-
-
-
- preset.id === selectedSoundPresetId)?.label ?? copy.common.default}
- />
-
- {SOUND_PRESETS.slice(0, 6).map((preset) => {
- const selected = preset.id === selectedSoundPresetId;
-
- return (
-
- );
- })}
-
-
-
-
-
{controlCenter.recommendation(sceneRecommendedSoundLabel, sceneRecommendedTimerLabel)}
-
{controlCenter.recommendationHint}
-
-
-
-
-
- {PRO_FEATURE_CARDS.map((feature) => {
- const locked = !isPro;
-
- return (
-
- );
- })}
-
-
-
-
-
-
-
{controlCenter.autoHideTitle}
-
{controlCenter.autoHideDescription}
-
-
-
-
-
- );
-};
diff --git a/src/widgets/focus-dashboard/ui/FocusPlanManageSheet.tsx b/src/widgets/focus-dashboard/ui/FocusPlanManageSheet.tsx
deleted file mode 100644
index 09dc359..0000000
--- a/src/widgets/focus-dashboard/ui/FocusPlanManageSheet.tsx
+++ /dev/null
@@ -1,155 +0,0 @@
-'use client';
-
-import type { RefObject } from 'react';
-import type { FocusPlanItem } from '@/entities/focus-plan';
-import { cn } from '@/shared/lib/cn';
-import { Modal } from '@/shared/ui';
-import { FocusPlanListRow } from './FocusPlanListRow';
-
-export type FocusPlanEditingState =
- | {
- mode: 'new';
- value: string;
- }
- | {
- mode: 'edit';
- itemId: string;
- value: string;
- }
- | null;
-
-interface FocusPlanManageSheetProps {
- isOpen: boolean;
- planItems: FocusPlanItem[];
- selectedPlanItemId: string | null;
- editingState: FocusPlanEditingState;
- isSaving: boolean;
- canAddMore: boolean;
- isPro: boolean;
- inputRef?: RefObject;
- onClose: () => void;
- onAddBlock: () => void;
- onDraftChange: (value: string) => void;
- onSelect: (item: FocusPlanItem) => void;
- onEdit: (item: FocusPlanItem) => void;
- onDelete: (itemId: string) => void;
- onSave: () => void;
- onCancel: () => void;
-}
-
-const manageSheetCopy = {
- title: 'λΈλ‘ μ 리',
- description: 'μ§μ€μ λ€μ΄κ° λ€ μ΄μ΄κ° λΈλ‘λ§ κ°λ³κ² λ¨κ²¨λμΈμ.',
- empty: 'μμ§ μ μ₯λ λΈλ‘μ΄ μμ΄μ.',
- addBlock: '+ λΈλ‘ μΆκ°',
- placeholder: 'μ: 리뷰 μ½λ©νΈ 2κ° μ 리',
- freeUpgradeLabel: 'λ λ²μ§Έ λΈλ‘λΆν°λ PRO',
- maxBlocksReached: 'μ΅λ 5κ°κΉμ§ μ λ¦¬ν΄ λ μ μμ΄μ.',
-};
-
-export const FocusPlanManageSheet = ({
- isOpen,
- planItems,
- selectedPlanItemId,
- editingState,
- isSaving,
- canAddMore,
- isPro,
- inputRef,
- onClose,
- onAddBlock,
- onDraftChange,
- onSelect,
- onEdit,
- onDelete,
- onSave,
- onCancel,
-}: FocusPlanManageSheetProps) => {
- const hasPendingEdit = editingState !== null;
-
- return (
-
-
-
-
- {planItems.length === 0 && editingState?.mode !== 'new' ? (
-
- {manageSheetCopy.empty}
-
- ) : null}
-
- {planItems.map((item, index) => (
-
onSelect(item)}
- onEdit={() => onEdit(item)}
- onDelete={() => onDelete(item.id)}
- onSave={onSave}
- onCancel={onCancel}
- />
- ))}
-
- {editingState?.mode === 'new' ? (
- {}}
- onEdit={() => {}}
- onDelete={() => {}}
- onSave={onSave}
- onCancel={onCancel}
- />
- ) : null}
-
-
-
-
- {isPro && !canAddMore ? (
-
{manageSheetCopy.maxBlocksReached}
- ) : (
-
- )}
-
-
-
- );
-};
diff --git a/src/widgets/space-focus-hud/ui/ReturnPrompt.tsx b/src/widgets/space-focus-hud/ui/ReturnPrompt.tsx
deleted file mode 100644
index 8a2009b..0000000
--- a/src/widgets/space-focus-hud/ui/ReturnPrompt.tsx
+++ /dev/null
@@ -1,199 +0,0 @@
-'use client';
-
-import { copy } from '@/shared/i18n';
-import { cn } from '@/shared/lib/cn';
-import {
- HUD_OPTION_CHEVRON,
- HUD_OPTION_ROW,
- HUD_OPTION_ROW_BREAK,
- HUD_OPTION_ROW_PRIMARY,
- HUD_REVEAL_BASE,
- HUD_REVEAL_HIDDEN,
- HUD_REVEAL_RETURN_BREAK,
- HUD_REVEAL_RETURN_FOCUS,
- HUD_RETURN_BODY,
- HUD_RETURN_TITLE,
- HUD_TRAY_CONTENT,
- HUD_TRAY_HAIRLINE_BREAK,
- HUD_TRAY_HAIRLINE,
- HUD_TRAY_LAYER_BREAK,
- HUD_TRAY_LAYER,
- HUD_TRAY_SHELL_BREAK,
- HUD_TRAY_SHELL,
-} from './overlayStyles';
-
-interface ReturnPromptProps {
- open: boolean;
- mode: 'focus' | 'break';
- isBusy: boolean;
- onContinue: () => void;
- onRefocus: () => void;
- onRest?: () => void;
- onNextGoal?: () => void;
- onFinish: () => void;
-}
-
-export const ReturnPrompt = ({
- open,
- mode,
- isBusy,
- onContinue,
- onRefocus,
- onRest,
- onNextGoal,
- onFinish,
-}: ReturnPromptProps) => {
- const isBreakReturn = mode === 'break';
-
- return (
-
-
-
-
-
-
-
- {copy.space.focusHud.returnPromptEyebrow}
-
-
- {isBreakReturn
- ? copy.space.focusHud.returnPromptBreakTitle
- : copy.space.focusHud.returnPromptFocusTitle}
-
-
- {isBreakReturn
- ? copy.space.focusHud.returnPromptBreakDescription
- : copy.space.focusHud.returnPromptFocusDescription}
-
-
-
- {isBreakReturn ? (
- <>
-
-
-
- >
- ) : (
- <>
-
-
- >
- )}
-
-
-
-
-
- {copy.space.focusHud.returnPromptFinishHint}
-
-
-
-
-
- );
-};
diff --git a/src/widgets/space-sheet-shell/index.ts b/src/widgets/space-sheet-shell/index.ts
deleted file mode 100644
index 8a4fb12..0000000
--- a/src/widgets/space-sheet-shell/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export * from './ui/SpaceSideSheet';
diff --git a/src/widgets/space-sheet-shell/ui/SpaceSideSheet.tsx b/src/widgets/space-sheet-shell/ui/SpaceSideSheet.tsx
deleted file mode 100644
index 3f3890c..0000000
--- a/src/widgets/space-sheet-shell/ui/SpaceSideSheet.tsx
+++ /dev/null
@@ -1,167 +0,0 @@
-'use client';
-
-import { useEffect, useRef, useState, type ReactNode } from 'react';
-import { copy } from '@/shared/i18n';
-import { cn } from '@/shared/lib/cn';
-import { useReducedMotion } from '@/shared/lib/useReducedMotion';
-
-interface SpaceSideSheetProps {
- open: boolean;
- title: string;
- subtitle?: string;
- onClose: () => void;
- children: ReactNode;
- footer?: ReactNode;
- widthClassName?: string;
- dismissible?: boolean;
- headerAction?: ReactNode;
- overlayClassName?: string;
-}
-
-export const SpaceSideSheet = ({
- open,
- title,
- subtitle,
- onClose,
- children,
- footer,
- widthClassName,
- dismissible = true,
- headerAction,
- overlayClassName,
-}: SpaceSideSheetProps) => {
- const reducedMotion = useReducedMotion();
- const transitionMs = reducedMotion ? 0 : 260;
- const closeTimerRef = useRef(null);
- const [shouldRender, setShouldRender] = useState(open);
- const [visible, setVisible] = useState(open);
-
- useEffect(() => {
- if (closeTimerRef.current) {
- window.clearTimeout(closeTimerRef.current);
- closeTimerRef.current = null;
- }
-
- if (open) {
- let nestedRaf = 0;
- const raf = window.requestAnimationFrame(() => {
- setShouldRender(true);
- nestedRaf = window.requestAnimationFrame(() => {
- setVisible(true);
- });
- });
-
- return () => {
- window.cancelAnimationFrame(raf);
- window.cancelAnimationFrame(nestedRaf);
- };
- }
-
- const hideRaf = window.requestAnimationFrame(() => {
- setVisible(false);
- });
- closeTimerRef.current = window.setTimeout(() => {
- setShouldRender(false);
- closeTimerRef.current = null;
- }, transitionMs);
-
- return () => {
- window.cancelAnimationFrame(hideRaf);
- if (closeTimerRef.current) {
- window.clearTimeout(closeTimerRef.current);
- closeTimerRef.current = null;
- }
- };
- }, [open, transitionMs]);
-
- useEffect(() => {
- if (!open) {
- return;
- }
-
- const handleEscape = (event: KeyboardEvent) => {
- if (event.key === 'Escape') {
- onClose();
- }
- };
-
- document.addEventListener('keydown', handleEscape);
-
- return () => {
- document.removeEventListener('keydown', handleEscape);
- };
- }, [open, onClose]);
-
- if (!shouldRender) {
- return null;
- }
-
- return (
- <>
- {dismissible ? (
-
- ) : (
-
- )}
-
-
- >
- );
-};
diff --git a/src/widgets/space-timer-hud/index.ts b/src/widgets/space-timer-hud/index.ts
deleted file mode 100644
index baf6f7c..0000000
--- a/src/widgets/space-timer-hud/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export * from './ui/SpaceTimerHudWidget';
diff --git a/src/widgets/space-timer-hud/ui/SpaceTimerHudWidget.tsx b/src/widgets/space-timer-hud/ui/SpaceTimerHudWidget.tsx
deleted file mode 100644
index c08c383..0000000
--- a/src/widgets/space-timer-hud/ui/SpaceTimerHudWidget.tsx
+++ /dev/null
@@ -1,128 +0,0 @@
-'use client';
-
-import { copy } from '@/shared/i18n';
-import { cn } from '@/shared/lib/cn';
-import {
- RECOVERY_30S_MODE_LABEL,
- Restart30sAction,
- useRestart30s,
-} from '@/features/restart-30s';
-
-interface SpaceTimerHudWidgetProps {
- className?: string;
- hasActiveSession?: boolean;
- sessionPhase?: 'focus' | 'break' | null;
- playbackState?: 'running' | 'paused' | null;
- isControlsDisabled?: boolean;
- canStart?: boolean;
- canPause?: boolean;
- canReset?: boolean;
- onStartClick?: () => void;
- onPauseClick?: () => void;
- onResetClick?: () => void;
-}
-
-const HUD_ACTIONS = copy.space.timerHud.actions;
-
-export const SpaceTimerHudWidget = ({
- className,
- hasActiveSession = false,
- sessionPhase = 'focus',
- playbackState = 'paused',
- isControlsDisabled = false,
- canStart = true,
- canPause = false,
- canReset = false,
- onStartClick,
- onPauseClick,
- onResetClick,
-}: SpaceTimerHudWidgetProps) => {
- const { isBreatheMode, triggerRestart } = useRestart30s();
- const isBreakPhase = hasActiveSession && sessionPhase === 'break';
- const modeLabel = isBreatheMode
- ? RECOVERY_30S_MODE_LABEL
- : !hasActiveSession
- ? copy.space.timerHud.readyMode
- : sessionPhase === 'break'
- ? copy.space.timerHud.breakMode
- : copy.space.timerHud.focusMode;
-
- return (
-
-
-
-
-
- {modeLabel}
-
-
-
-
- {HUD_ACTIONS.map((action) => {
- const isStartAction = action.id === 'start';
- const isPauseAction = action.id === 'pause';
- const isResetAction = action.id === 'reset';
- const isDisabled =
- isControlsDisabled ||
- (isStartAction ? !canStart : isPauseAction ? !canPause : !canReset);
- const isHighlighted =
- (isStartAction && playbackState !== 'running') ||
- (isPauseAction && playbackState === 'running');
-
- return (
-
- );
- })}
-
-
-
-
-
- );
-};
diff --git a/src/widgets/space-tools-dock/index.ts b/src/widgets/space-tools-dock/index.ts
deleted file mode 100644
index 491f34b..0000000
--- a/src/widgets/space-tools-dock/index.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-export * from './model/types';
-export * from './ui/SpaceToolsDockWidget';
diff --git a/src/widgets/space-tools-dock/model/getQuickSoundPresets.ts b/src/widgets/space-tools-dock/model/getQuickSoundPresets.ts
deleted file mode 100644
index 1a59219..0000000
--- a/src/widgets/space-tools-dock/model/getQuickSoundPresets.ts
+++ /dev/null
@@ -1,19 +0,0 @@
-import { PRO_LOCKED_SOUND_IDS } from '@/entities/plan';
-import type { SoundPreset } from '@/entities/session';
-
-const QUICK_SOUND_FALLBACK_IDS = ['rain-focus', 'deep-white', 'silent'] as const;
-
-const isQuickAvailablePreset = (preset: SoundPreset | undefined): preset is SoundPreset => {
- if (!preset) {
- return false;
- }
-
- return !PRO_LOCKED_SOUND_IDS.includes(preset.id);
-};
-
-export const getQuickSoundPresets = (presets: SoundPreset[]) => {
- return QUICK_SOUND_FALLBACK_IDS
- .map((presetId) => presets.find((preset) => preset.id === presetId))
- .filter(isQuickAvailablePreset)
- .slice(0, 3);
-};
diff --git a/src/widgets/space-tools-dock/model/types.ts b/src/widgets/space-tools-dock/model/types.ts
deleted file mode 100644
index 1afbd32..0000000
--- a/src/widgets/space-tools-dock/model/types.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-export type SpaceAnchorPopoverId = 'sound' | 'notes';
-export type SpaceUtilityPanelId = 'control-center' | 'inbox' | 'manage-plan' | 'paywall';
diff --git a/src/widgets/space-tools-dock/model/useSpaceToolsDockHandlers.ts b/src/widgets/space-tools-dock/model/useSpaceToolsDockHandlers.ts
deleted file mode 100644
index df4dd77..0000000
--- a/src/widgets/space-tools-dock/model/useSpaceToolsDockHandlers.ts
+++ /dev/null
@@ -1,206 +0,0 @@
-'use client';
-
-import { useCallback, useState, type KeyboardEvent as ReactKeyboardEvent } from 'react';
-import { usePlanTier } from '@/entities/plan';
-import type { RecentThought } from '@/entities/session';
-import { copy } from '@/shared/i18n';
-import type { HudStatusLinePayload } from '@/shared/lib/useHudStatusLine';
-import type { SpaceAnchorPopoverId, SpaceUtilityPanelId } from './types';
-
-interface UseSpaceToolsDockHandlersParams {
- setIdle: (idle: boolean) => void;
- setOpenPopover: (popover: SpaceAnchorPopoverId | null) => void;
- setUtilityPanel: (panel: SpaceUtilityPanelId | null) => void;
- onCaptureThought: (note: string) => RecentThought | null;
- onDeleteThought: (thoughtId: string) => RecentThought | null;
- onSetThoughtCompleted: (thoughtId: string, isCompleted: boolean) => RecentThought | null;
- onRestoreThought: (thought: RecentThought) => void;
- onRestoreThoughts: (thoughts: RecentThought[]) => void;
- onClearInbox: () => RecentThought[];
- onStatusMessage: (payload: HudStatusLinePayload) => void;
- onSetSoundVolume: (volume: number) => void;
- onSetSoundMuted: (muted: boolean) => void;
- soundVolume: number;
- isSoundMuted: boolean;
- showVolumeFeedback: (volume: number) => void;
-}
-
-export const useSpaceToolsDockHandlers = ({
- setIdle,
- setOpenPopover,
- setUtilityPanel,
- onCaptureThought,
- onDeleteThought,
- onSetThoughtCompleted,
- onRestoreThought,
- onRestoreThoughts,
- onClearInbox,
- onStatusMessage,
- onSetSoundVolume,
- onSetSoundMuted,
- soundVolume,
- isSoundMuted,
- showVolumeFeedback,
-}: UseSpaceToolsDockHandlersParams) => {
- const { toolsDock } = copy.space;
- const [noteDraft, setNoteDraft] = useState('');
- const { plan, setPlan } = usePlanTier();
-
- const openUtilityPanel = useCallback((panel: SpaceUtilityPanelId) => {
- setIdle(false);
- setOpenPopover(null);
- setUtilityPanel(panel);
- }, [setIdle, setOpenPopover, setUtilityPanel]);
-
- const handleNoteSubmit = useCallback(() => {
- const trimmedNote = noteDraft.trim();
-
- if (!trimmedNote) {
- return;
- }
-
- const addedThought = onCaptureThought(trimmedNote);
-
- if (!addedThought) {
- return;
- }
-
- setNoteDraft('');
- onStatusMessage({
- message: toolsDock.inboxSaved,
- durationMs: 4200,
- priority: 'undo',
- action: {
- label: toolsDock.undo,
- onClick: () => {
- const removed = onDeleteThought(addedThought.id);
-
- if (!removed) {
- return;
- }
-
- onStatusMessage({ message: toolsDock.inboxSaveUndone });
- },
- },
- });
- }, [noteDraft, onCaptureThought, onDeleteThought, onStatusMessage, toolsDock.inboxSaved, toolsDock.inboxSaveUndone, toolsDock.undo]);
-
- const handleInboxComplete = useCallback((thought: RecentThought) => {
- onSetThoughtCompleted(thought.id, !thought.isCompleted);
- }, [onSetThoughtCompleted]);
-
- const handleInboxDelete = useCallback((thought: RecentThought) => {
- const removedThought = onDeleteThought(thought.id);
-
- if (!removedThought) {
- return;
- }
-
- onStatusMessage({
- message: toolsDock.deleted,
- durationMs: 4200,
- priority: 'undo',
- action: {
- label: toolsDock.undo,
- onClick: () => {
- onRestoreThought(removedThought);
- onStatusMessage({ message: toolsDock.deleteUndone });
- },
- },
- });
- }, [onDeleteThought, onRestoreThought, onStatusMessage, toolsDock.deleted, toolsDock.deleteUndone, toolsDock.undo]);
-
- const handleInboxClear = useCallback(() => {
- const snapshot = onClearInbox();
-
- if (snapshot.length === 0) {
- onStatusMessage({ message: toolsDock.emptyToClear });
- return;
- }
-
- onStatusMessage({
- message: toolsDock.clearedAll,
- durationMs: 4200,
- priority: 'undo',
- action: {
- label: toolsDock.undo,
- onClick: () => {
- onRestoreThoughts(snapshot);
- onStatusMessage({ message: toolsDock.restored });
- },
- },
- });
- }, [onClearInbox, onRestoreThoughts, onStatusMessage, toolsDock.clearedAll, toolsDock.emptyToClear, toolsDock.restored, toolsDock.undo]);
-
- const handlePlanPillClick = useCallback(() => {
- if (plan === 'pro') {
- openUtilityPanel('manage-plan');
- return;
- }
-
- onStatusMessage({ message: toolsDock.normalPlanInfo });
- }, [openUtilityPanel, onStatusMessage, plan, toolsDock.normalPlanInfo]);
-
- const handleLockedClick = useCallback((source: string) => {
- onStatusMessage({ message: toolsDock.proFeatureLocked(source) });
- openUtilityPanel('paywall');
- }, [onStatusMessage, openUtilityPanel, toolsDock]);
-
- const handleSelectProFeature = useCallback((featureId: string) => {
- const label =
- featureId === 'daily-plan'
- ? toolsDock.featureLabels.dailyPlan
- : featureId === 'rituals'
- ? toolsDock.featureLabels.rituals
- : toolsDock.featureLabels.weeklyReview;
-
- onStatusMessage({ message: toolsDock.proFeaturePending(label) });
- }, [onStatusMessage, toolsDock]);
-
- const handleStartPro = useCallback(() => {
- setPlan('pro');
- onStatusMessage({ message: toolsDock.purchaseMock });
- openUtilityPanel('control-center');
- }, [onStatusMessage, openUtilityPanel, setPlan, toolsDock.purchaseMock]);
-
- const handleVolumeChange = useCallback((nextVolume: number) => {
- const clamped = Math.min(100, Math.max(0, nextVolume));
- onSetSoundVolume(clamped);
-
- if (isSoundMuted && clamped > 0) {
- onSetSoundMuted(false);
- }
-
- showVolumeFeedback(clamped);
- }, [isSoundMuted, onSetSoundMuted, onSetSoundVolume, showVolumeFeedback]);
-
- const handleVolumeKeyDown = useCallback((event: ReactKeyboardEvent) => {
- if (event.key !== 'ArrowLeft' && event.key !== 'ArrowRight') {
- return;
- }
-
- event.preventDefault();
-
- const step = event.shiftKey ? 10 : 5;
- const delta = event.key === 'ArrowRight' ? step : -step;
- handleVolumeChange(soundVolume + delta);
- }, [handleVolumeChange, soundVolume]);
-
- return {
- noteDraft,
- setNoteDraft,
- plan,
- setPlan,
- openUtilityPanel,
- handleNoteSubmit,
- handleInboxComplete,
- handleInboxDelete,
- handleInboxClear,
- handlePlanPillClick,
- handleLockedClick,
- handleSelectProFeature,
- handleStartPro,
- handleVolumeChange,
- handleVolumeKeyDown,
- };
-};
diff --git a/src/widgets/space-tools-dock/model/useSpaceToolsDockState.ts b/src/widgets/space-tools-dock/model/useSpaceToolsDockState.ts
deleted file mode 100644
index bd3bd7d..0000000
--- a/src/widgets/space-tools-dock/model/useSpaceToolsDockState.ts
+++ /dev/null
@@ -1,143 +0,0 @@
-'use client';
-
-import { useEffect, useRef, useState } from 'react';
-import type { SpaceAnchorPopoverId, SpaceUtilityPanelId } from './types';
-
-interface UseSpaceToolsDockStateParams {
- isFocusMode: boolean;
-}
-
-export const useSpaceToolsDockState = ({ isFocusMode }: UseSpaceToolsDockStateParams) => {
- const [openPopover, setOpenPopover] = useState(null);
- const [utilityPanel, setUtilityPanel] = useState(null);
- const [autoHideControls, setAutoHideControls] = useState(true);
- const [isIdle, setIdle] = useState(false);
- const [volumeFeedback, setVolumeFeedback] = useState(null);
- const volumeFeedbackTimerRef = useRef(null);
-
- useEffect(() => {
- return () => {
- if (volumeFeedbackTimerRef.current) {
- window.clearTimeout(volumeFeedbackTimerRef.current);
- volumeFeedbackTimerRef.current = null;
- }
- };
- }, []);
-
- useEffect(() => {
- if (isFocusMode) {
- return;
- }
-
- const rafId = window.requestAnimationFrame(() => {
- setOpenPopover(null);
- setUtilityPanel(null);
- setIdle(false);
- });
-
- return () => {
- window.cancelAnimationFrame(rafId);
- };
- }, [isFocusMode]);
-
- useEffect(() => {
- if (!isFocusMode || openPopover || utilityPanel) {
- return;
- }
-
- let timerId: number | null = null;
-
- const armIdleTimer = () => {
- if (timerId) {
- window.clearTimeout(timerId);
- }
-
- timerId = window.setTimeout(() => {
- setIdle(true);
- }, 3500);
- };
-
- const wake = () => {
- setIdle(false);
- armIdleTimer();
- };
-
- armIdleTimer();
-
- window.addEventListener('pointermove', wake);
- window.addEventListener('keydown', wake);
- window.addEventListener('pointerdown', wake);
-
- return () => {
- if (timerId) {
- window.clearTimeout(timerId);
- }
- window.removeEventListener('pointermove', wake);
- window.removeEventListener('keydown', wake);
- window.removeEventListener('pointerdown', wake);
- };
- }, [isFocusMode, openPopover, utilityPanel]);
-
- useEffect(() => {
- if (utilityPanel !== 'control-center' || !autoHideControls) {
- return;
- }
-
- let timerId: number | null = null;
- const closeDelayMs = 8000;
-
- const armCloseTimer = () => {
- if (timerId) {
- window.clearTimeout(timerId);
- }
-
- timerId = window.setTimeout(() => {
- setUtilityPanel((current) => (current === 'control-center' ? null : current));
- }, closeDelayMs);
- };
-
- const resetTimer = () => {
- armCloseTimer();
- };
-
- armCloseTimer();
- window.addEventListener('pointermove', resetTimer);
- window.addEventListener('pointerdown', resetTimer);
- window.addEventListener('keydown', resetTimer);
-
- return () => {
- if (timerId) {
- window.clearTimeout(timerId);
- }
- window.removeEventListener('pointermove', resetTimer);
- window.removeEventListener('pointerdown', resetTimer);
- window.removeEventListener('keydown', resetTimer);
- };
- }, [autoHideControls, utilityPanel]);
-
- const showVolumeFeedback = (nextVolume: number) => {
- setVolumeFeedback(`${nextVolume}%`);
-
- if (volumeFeedbackTimerRef.current) {
- window.clearTimeout(volumeFeedbackTimerRef.current);
- }
-
- volumeFeedbackTimerRef.current = window.setTimeout(() => {
- setVolumeFeedback(null);
- volumeFeedbackTimerRef.current = null;
- }, 900);
- };
-
- return {
- openPopover,
- setOpenPopover,
- utilityPanel,
- setUtilityPanel,
- autoHideControls,
- setAutoHideControls,
- isIdle,
- setIdle,
- volumeFeedback,
- showVolumeFeedback,
- };
-};
diff --git a/src/widgets/space-tools-dock/ui/FocusModeAnchors.tsx b/src/widgets/space-tools-dock/ui/FocusModeAnchors.tsx
deleted file mode 100644
index 09ebdbe..0000000
--- a/src/widgets/space-tools-dock/ui/FocusModeAnchors.tsx
+++ /dev/null
@@ -1,97 +0,0 @@
-'use client';
-
-import type { KeyboardEvent as ReactKeyboardEvent } from 'react';
-import type { SoundPreset } from '@/entities/session';
-import { copy } from '@/shared/i18n';
-import type { SpaceAnchorPopoverId } from '../model/types';
-import { FocusRightRail } from './FocusRightRail';
-
-interface FocusModeAnchorsProps {
- isFocusMode: boolean;
- isIdle: boolean;
- openPopover: SpaceAnchorPopoverId | null;
- thoughtCount: number;
- noteDraft: string;
- selectedSoundLabel: string;
- isSoundMuted: boolean;
- soundVolume: number;
- volumeFeedback: string | null;
- quickSoundPresets: SoundPreset[];
- selectedPresetId: string;
- onClosePopover: () => void;
- onOpenInbox: () => void;
- onOpenControlCenter: () => void;
- onToggleNotes: () => void;
- onToggleSound: () => void;
- onNoteDraftChange: (value: string) => void;
- onNoteSubmit: () => void;
- onToggleMute: () => void;
- onVolumeChange: (nextVolume: number) => void;
- onVolumeKeyDown: (event: ReactKeyboardEvent) => void;
- onSelectPreset: (presetId: string) => void;
-}
-
-export const FocusModeAnchors = ({
- isFocusMode,
- isIdle,
- openPopover,
- thoughtCount,
- noteDraft,
- selectedSoundLabel,
- isSoundMuted,
- soundVolume,
- volumeFeedback,
- quickSoundPresets,
- selectedPresetId,
- onClosePopover,
- onOpenInbox,
- onOpenControlCenter,
- onToggleNotes,
- onToggleSound,
- onNoteDraftChange,
- onNoteSubmit,
- onToggleMute,
- onVolumeChange,
- onVolumeKeyDown,
- onSelectPreset,
-}: FocusModeAnchorsProps) => {
- if (!isFocusMode) {
- return null;
- }
-
- return (
- <>
- {openPopover ? (
-
- ) : null}
-
-
- >
- );
-};
diff --git a/src/widgets/space-tools-dock/ui/FocusRightRail.tsx b/src/widgets/space-tools-dock/ui/FocusRightRail.tsx
deleted file mode 100644
index 2ffb380..0000000
--- a/src/widgets/space-tools-dock/ui/FocusRightRail.tsx
+++ /dev/null
@@ -1,192 +0,0 @@
-import type { KeyboardEvent as ReactKeyboardEvent } from 'react';
-import type { SoundPreset } from '@/entities/session';
-import { cn } from '@/shared/lib/cn';
-import { copy } from '@/shared/i18n';
-import type { SpaceAnchorPopoverId } from '../model/types';
-import { formatThoughtCount, RAIL_ICON, ANCHOR_ICON } from './constants';
-import { QuickNotesPopover } from './popovers/QuickNotesPopover';
-import { QuickSoundPopover } from './popovers/QuickSoundPopover';
-
-interface FocusRightRailProps {
- isIdle: boolean;
- thoughtCount: number;
- openPopover: SpaceAnchorPopoverId | null;
- noteDraft: string;
- selectedSoundLabel: string;
- isSoundMuted: boolean;
- soundVolume: number;
- volumeFeedback: string | null;
- quickSoundPresets: SoundPreset[];
- selectedPresetId: string;
- onOpenInbox: () => void;
- onOpenControlCenter: () => void;
- onToggleNotes: () => void;
- onToggleSound: () => void;
- onNoteDraftChange: (value: string) => void;
- onNoteSubmit: () => void;
- onToggleMute: () => void;
- onVolumeChange: (nextVolume: number) => void;
- onVolumeKeyDown: (event: ReactKeyboardEvent) => void;
- onSelectPreset: (presetId: string) => void;
-}
-
-export const FocusRightRail = ({
- isIdle,
- thoughtCount,
- openPopover,
- noteDraft,
- selectedSoundLabel,
- isSoundMuted,
- soundVolume,
- volumeFeedback,
- quickSoundPresets,
- selectedPresetId,
- onOpenInbox,
- onOpenControlCenter,
- onToggleNotes,
- onToggleSound,
- onNoteDraftChange,
- onNoteSubmit,
- onToggleMute,
- onVolumeChange,
- onVolumeKeyDown,
- onSelectPreset,
-}: FocusRightRailProps) => {
- return (
-
-
-
- {/* Thought Orb (Brain Dump) */}
-
-
-
- {/* Tooltip */}
-
-
- Brain Dump
-
-
-
- {openPopover === 'notes' ? (
-
- ) : null}
-
-
- {/* Standard Tools */}
-
-
- {/* Sound Toggle */}
-
-
-
- {/* Tooltip */}
-
-
- μ¬μ΄λ
-
-
-
- {openPopover === 'sound' ? (
-
-
-
- ) : null}
-
-
-
-
- {/* Inbox Button */}
-
-
-
-
- {copy.space.inbox.openInboxTitle}
-
-
-
-
- {/* Control Center Button */}
-
-
-
-
- {copy.space.rightRail.openQuickControlsTitle}
-
-
-
-
-
-
-
-
- );
-};
diff --git a/src/widgets/space-tools-dock/ui/SpaceToolsDockWidget.tsx b/src/widgets/space-tools-dock/ui/SpaceToolsDockWidget.tsx
deleted file mode 100644
index 91e0ba7..0000000
--- a/src/widgets/space-tools-dock/ui/SpaceToolsDockWidget.tsx
+++ /dev/null
@@ -1,278 +0,0 @@
-'use client';
-import { useEffect, useMemo } from 'react';
-import type { SceneAssetMap } from '@/entities/media';
-import type { SceneTheme } from '@/entities/scene';
-import { SOUND_PRESETS, type RecentThought, type TimerPreset } from '@/entities/session';
-import { copy } from '@/shared/i18n';
-import { ExitHoldButton } from '@/features/exit-hold';
-import { ManagePlanSheetContent, PaywallSheetContent } from '@/features/paywall-sheet';
-import { PlanPill } from '@/features/plan-pill';
-import type { HudStatusLinePayload } from '@/shared/lib/useHudStatusLine';
-import { cn } from '@/shared/lib/cn';
-import { ControlCenterSheetWidget } from '@/widgets/control-center-sheet';
-import { SpaceSideSheet } from '@/widgets/space-sheet-shell';
-import { getQuickSoundPresets } from '../model/getQuickSoundPresets';
-import { UTILITY_PANEL_TITLE } from './constants';
-import { FocusModeAnchors } from './FocusModeAnchors';
-import { InboxToolPanel } from './panels/InboxToolPanel';
-import { useSpaceToolsDockState } from '../model/useSpaceToolsDockState';
-import { useSpaceToolsDockHandlers } from '../model/useSpaceToolsDockHandlers';
-
-interface SpaceToolsDockWidgetProps {
- isFocusMode: boolean;
- scenes: SceneTheme[];
- sceneAssetMap?: SceneAssetMap;
- selectedSceneId: string;
- selectedTimerLabel: string;
- timerPresets: TimerPreset[];
- selectedPresetId: string;
- soundVolume: number;
- onSetSoundVolume: (volume: number) => void;
- isSoundMuted: boolean;
- onSetSoundMuted: (nextMuted: boolean) => void;
- thoughts: RecentThought[];
- thoughtCount: number;
- sceneRecommendedSoundLabel: string;
- sceneRecommendedTimerLabel: string;
- onSceneSelect: (sceneId: string) => void;
- onTimerSelect: (timerLabel: string) => void;
- onQuickSoundSelect: (presetId: string) => void;
- onCaptureThought: (note: string) => RecentThought | null;
- onDeleteThought: (thoughtId: string) => RecentThought | null;
- onSetThoughtCompleted: (thoughtId: string, isCompleted: boolean) => RecentThought | null;
- onRestoreThought: (thought: RecentThought) => void;
- onRestoreThoughts: (thoughts: RecentThought[]) => void;
- onClearInbox: () => RecentThought[];
- onStatusMessage: (payload: HudStatusLinePayload) => void;
- onExitRequested: () => void;
-}
-
-export const SpaceToolsDockWidget = ({
- isFocusMode,
- scenes,
- sceneAssetMap,
- selectedSceneId,
- selectedTimerLabel,
- timerPresets,
- selectedPresetId,
- soundVolume,
- onSetSoundVolume,
- isSoundMuted,
- onSetSoundMuted,
- thoughts,
- thoughtCount,
- sceneRecommendedSoundLabel,
- sceneRecommendedTimerLabel,
- onSceneSelect,
- onTimerSelect,
- onQuickSoundSelect,
- onCaptureThought,
- onDeleteThought,
- onSetThoughtCompleted,
- onRestoreThought,
- onRestoreThoughts,
- onClearInbox,
- onStatusMessage,
- onExitRequested,
-}: SpaceToolsDockWidgetProps) => {
- const { controlCenter } = copy.space;
-
- const {
- openPopover,
- setOpenPopover,
- utilityPanel,
- setUtilityPanel,
- autoHideControls,
- setAutoHideControls,
- isIdle,
- setIdle,
- volumeFeedback,
- showVolumeFeedback,
- } = useSpaceToolsDockState({ isFocusMode });
-
- const {
- noteDraft,
- setNoteDraft,
- plan,
- openUtilityPanel,
- handleNoteSubmit,
- handleInboxComplete,
- handleInboxDelete,
- handleInboxClear,
- handlePlanPillClick,
- handleLockedClick,
- handleSelectProFeature,
- handleStartPro,
- handleVolumeChange,
- handleVolumeKeyDown,
- } = useSpaceToolsDockHandlers({
- setIdle,
- setOpenPopover,
- setUtilityPanel,
- onCaptureThought,
- onDeleteThought,
- onSetThoughtCompleted,
- onRestoreThought,
- onRestoreThoughts,
- onClearInbox,
- onStatusMessage,
- onSetSoundVolume,
- onSetSoundMuted,
- soundVolume,
- isSoundMuted,
- showVolumeFeedback,
- });
-
- const selectedSoundLabel = useMemo(() => {
- return (
- SOUND_PRESETS.find((preset) => preset.id === selectedPresetId)?.label ?? SOUND_PRESETS[0]?.label ?? copy.common.default
- );
- }, [selectedPresetId]);
-
- const quickSoundPresets = useMemo(() => {
- return getQuickSoundPresets(SOUND_PRESETS);
- }, []);
-
- useEffect(() => {
- if (!openPopover) {
- return;
- }
-
- const handleEscape = (event: globalThis.KeyboardEvent) => {
- if (event.key === 'Escape') {
- setOpenPopover(null);
- }
- };
-
- document.addEventListener('keydown', handleEscape);
-
- return () => {
- document.removeEventListener('keydown', handleEscape);
- };
- }, [openPopover, setOpenPopover]);
-
- return (
- <>
-
-
- setOpenPopover(null)}
- onOpenInbox={() => openUtilityPanel('inbox')}
- onOpenControlCenter={() => openUtilityPanel('control-center')}
- onToggleNotes={() => {
- setIdle(false);
- setOpenPopover((current) => (current === 'notes' ? null : 'notes'));
- }}
- onToggleSound={() => {
- setIdle(false);
- setOpenPopover((current) => (current === 'sound' ? null : 'sound'));
- }}
- onNoteDraftChange={setNoteDraft}
- onNoteSubmit={handleNoteSubmit}
- onToggleMute={() => {
- const nextMuted = !isSoundMuted;
- onSetSoundMuted(nextMuted);
- showVolumeFeedback(nextMuted ? 0 : soundVolume);
- }}
- onVolumeChange={handleVolumeChange}
- onVolumeKeyDown={handleVolumeKeyDown}
- onSelectPreset={(presetId) => {
- onQuickSoundSelect(presetId);
- setOpenPopover(null);
- }}
- />
-
-
- ) : undefined
- }
- widthClassName={utilityPanel === 'control-center' ? 'w-[min(408px,94vw)]' : undefined}
- overlayClassName={utilityPanel === 'control-center' ? 'bg-slate-950/6 backdrop-blur-none' : undefined}
- onClose={() => setUtilityPanel(null)}
- >
- {utilityPanel === 'control-center' ? (
- {
- onSceneSelect(sceneId);
- }}
- onSelectTimer={(label) => {
- onTimerSelect(label);
- }}
- onSelectSound={onQuickSoundSelect}
- onSelectProFeature={handleSelectProFeature}
- onLockedClick={handleLockedClick}
- />
- ) : null}
-
- {utilityPanel === 'inbox' ? (
-
- ) : null}
-
- {utilityPanel === 'paywall' ? (
- setUtilityPanel(null)}
- />
- ) : null}
-
- {utilityPanel === 'manage-plan' ? (
- setUtilityPanel(null)}
- onManage={() => onStatusMessage({ message: copy.space.toolsDock.manageSubscriptionMock })}
- onRestore={() => onStatusMessage({ message: copy.space.toolsDock.restorePurchaseMock })}
- />
- ) : null}
-
- >
- );
-};
diff --git a/src/widgets/space-tools-dock/ui/constants.tsx b/src/widgets/space-tools-dock/ui/constants.tsx
deleted file mode 100644
index 8eecb9d..0000000
--- a/src/widgets/space-tools-dock/ui/constants.tsx
+++ /dev/null
@@ -1,86 +0,0 @@
-import type { SpaceUtilityPanelId } from '../model/types';
-import { copy } from '@/shared/i18n';
-
-export const ANCHOR_ICON = {
- sound: (
-
- ),
- notes: (
-
- ),
-};
-
-export const RAIL_ICON = {
- controlCenter: (
-
- ),
- inbox: (
-
- ),
-};
-
-export const UTILITY_PANEL_TITLE: Record = {
- 'control-center': copy.space.toolsDock.utilityPanelTitle['control-center'],
- inbox: copy.space.toolsDock.utilityPanelTitle.inbox,
- paywall: copy.space.toolsDock.utilityPanelTitle.paywall,
- 'manage-plan': copy.space.toolsDock.utilityPanelTitle['manage-plan'],
-};
-
-export const formatThoughtCount = (count: number) => {
- if (count < 1) {
- return '0';
- }
-
- if (count > 9) {
- return '9+';
- }
-
- return String(count);
-};
diff --git a/src/widgets/space-tools-dock/ui/panels/InboxToolPanel.tsx b/src/widgets/space-tools-dock/ui/panels/InboxToolPanel.tsx
deleted file mode 100644
index e57d836..0000000
--- a/src/widgets/space-tools-dock/ui/panels/InboxToolPanel.tsx
+++ /dev/null
@@ -1,68 +0,0 @@
-import { useState } from 'react';
-import type { RecentThought } from '@/entities/session';
-import { copy } from '@/shared/i18n';
-import { InboxList } from '@/features/inbox';
-
-interface InboxToolPanelProps {
- thoughts: RecentThought[];
- onCompleteThought: (thought: RecentThought) => void;
- onDeleteThought: (thought: RecentThought) => void;
- onClear: () => void;
-}
-
-export const InboxToolPanel = ({
- thoughts,
- onCompleteThought,
- onDeleteThought,
- onClear,
-}: InboxToolPanelProps) => {
- const [confirmOpen, setConfirmOpen] = useState(false);
-
- return (
-
-
-
{copy.space.inbox.readOnly}
-
-
-
-
- {confirmOpen ? (
-
-
-
{copy.space.inbox.clearConfirmTitle}
-
{copy.space.inbox.clearConfirmDescription}
-
-
-
-
-
-
- ) : null}
-
- );
-};
diff --git a/src/widgets/space-tools-dock/ui/panels/SettingsToolPanel.tsx b/src/widgets/space-tools-dock/ui/panels/SettingsToolPanel.tsx
deleted file mode 100644
index 93a6e3d..0000000
--- a/src/widgets/space-tools-dock/ui/panels/SettingsToolPanel.tsx
+++ /dev/null
@@ -1,137 +0,0 @@
-'use client';
-
-import { useState } from 'react';
-import type { SceneTheme } from '@/entities/scene';
-import type { TimerPreset } from '@/entities/session';
-import { copy } from '@/shared/i18n';
-import { DEFAULT_PRESET_OPTIONS } from '@/shared/config/settingsOptions';
-import { cn } from '@/shared/lib/cn';
-
-interface SettingsToolPanelProps {
- scenes: SceneTheme[];
- selectedSceneId: string;
- selectedTimerLabel: string;
- timerPresets: TimerPreset[];
- onSelectScene: (sceneId: string) => void;
- onSelectTimer: (timerLabel: string) => void;
-}
-
-export const SettingsToolPanel = ({
- scenes,
- selectedSceneId,
- selectedTimerLabel,
- timerPresets,
- onSelectScene,
- onSelectTimer,
-}: SettingsToolPanelProps) => {
- const { settingsPanel } = copy.space;
- const [reduceMotion, setReduceMotion] = useState(false);
- const [defaultPresetId, setDefaultPresetId] = useState<
- (typeof DEFAULT_PRESET_OPTIONS)[number]['id']
- >(DEFAULT_PRESET_OPTIONS[0].id);
-
- return (
-
-
-
-
-
{settingsPanel.reduceMotion}
-
{settingsPanel.reduceMotionDescription}
-
-
-
-
-
-
- {settingsPanel.background}
- {settingsPanel.backgroundDescription}
-
- {scenes.slice(0, 4).map((scene) => {
- const selected = scene.id === selectedSceneId;
-
- return (
-
- );
- })}
-
-
-
-
- {settingsPanel.timerPreset}
- {settingsPanel.timerPresetDescription}
-
- {timerPresets.slice(0, 3).map((preset) => {
- const selected = preset.label === selectedTimerLabel;
-
- return (
-
- );
- })}
-
-
-
-
- {settingsPanel.defaultPreset}
-
- {DEFAULT_PRESET_OPTIONS.map((preset) => (
-
- ))}
-
-
-
- );
-};
diff --git a/src/widgets/space-tools-dock/ui/panels/StatsToolPanel.tsx b/src/widgets/space-tools-dock/ui/panels/StatsToolPanel.tsx
deleted file mode 100644
index 45cce03..0000000
--- a/src/widgets/space-tools-dock/ui/panels/StatsToolPanel.tsx
+++ /dev/null
@@ -1,27 +0,0 @@
-import { TODAY_STATS, WEEKLY_STATS } from '@/entities/session';
-import { copy } from '@/shared/i18n';
-
-export const StatsToolPanel = () => {
- const previewStats = [TODAY_STATS[0], TODAY_STATS[1], WEEKLY_STATS[0], WEEKLY_STATS[2]];
-
- return (
-
-
{copy.space.statsPanel.description}
-
-
- {previewStats.map((stat) => (
-
- {stat.label}
- {stat.value}
- {stat.delta}
-
- ))}
-
-
-
-
- {copy.space.statsPanel.graphPlaceholder}
-
-
- );
-};
diff --git a/src/widgets/space-tools-dock/ui/popovers/QuickNotesPopover.tsx b/src/widgets/space-tools-dock/ui/popovers/QuickNotesPopover.tsx
deleted file mode 100644
index 421c9eb..0000000
--- a/src/widgets/space-tools-dock/ui/popovers/QuickNotesPopover.tsx
+++ /dev/null
@@ -1,51 +0,0 @@
-import { copy } from '@/shared/i18n';
-
-interface QuickNotesPopoverProps {
- noteDraft: string;
- onDraftChange: (value: string) => void;
- onDraftEnter: () => void;
- onSubmit: () => void;
-}
-
-export const QuickNotesPopover = ({
- noteDraft,
- onDraftChange,
- onDraftEnter,
- onSubmit,
-}: QuickNotesPopoverProps) => {
- return (
-
-
{copy.space.quickNotes.title}
-
-
onDraftChange(event.target.value)}
- onKeyDown={(event) => {
- if (event.key !== 'Enter') {
- return;
- }
-
- event.preventDefault();
- onDraftEnter();
- }}
- placeholder={copy.space.quickNotes.placeholder}
- autoFocus
- className="w-full border-b border-white/20 bg-transparent pb-2 text-sm text-white placeholder:text-white/30 transition-colors focus:border-white/60 focus:outline-none"
- />
-
-
{copy.space.quickNotes.hint}
-
-
-
-
- );
-};
diff --git a/src/widgets/space-tools-dock/ui/popovers/QuickSoundPopover.tsx b/src/widgets/space-tools-dock/ui/popovers/QuickSoundPopover.tsx
deleted file mode 100644
index 5120271..0000000
--- a/src/widgets/space-tools-dock/ui/popovers/QuickSoundPopover.tsx
+++ /dev/null
@@ -1,101 +0,0 @@
-import { cn } from '@/shared/lib/cn';
-import type { SoundPreset } from '@/entities/session';
-import type { KeyboardEvent as ReactKeyboardEvent } from 'react';
-import { copy } from '@/shared/i18n';
-
-interface QuickSoundPopoverProps {
- selectedSoundLabel: string;
- isSoundMuted: boolean;
- soundVolume: number;
- volumeFeedback: string | null;
- quickSoundPresets: SoundPreset[];
- selectedPresetId: string;
- onToggleMute: () => void;
- onVolumeChange: (nextVolume: number) => void;
- onVolumeKeyDown: (event: ReactKeyboardEvent) => void;
- onSelectPreset: (presetId: string) => void;
-}
-
-export const QuickSoundPopover = ({
- selectedSoundLabel,
- isSoundMuted,
- soundVolume,
- volumeFeedback,
- quickSoundPresets,
- selectedPresetId,
- onToggleMute,
- onVolumeChange,
- onVolumeKeyDown,
- onSelectPreset,
-}: QuickSoundPopoverProps) => {
- return (
-
-
-
{copy.space.quickSound.currentSound}
-
- {volumeFeedback ?? (isSoundMuted ? '0%' : `${soundVolume}%`)}
-
-
-
{selectedSoundLabel}
-
-
-
-
-
-
onVolumeChange(Number(event.target.value))}
- onKeyDown={onVolumeKeyDown}
- aria-label={copy.space.quickSound.volumeAriaLabel}
- className="absolute z-10 w-full cursor-pointer appearance-none bg-transparent accent-white outline-none [&::-webkit-slider-thumb]:h-4 [&::-webkit-slider-thumb]:w-4 [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:bg-white [&::-webkit-slider-thumb]:shadow-md"
- />
-
-
-
-
-
-
-
{copy.space.quickSound.quickSwitch}
-
- {quickSoundPresets.map((preset) => {
- const selected = preset.id === selectedPresetId;
-
- return (
-
- );
- })}
-
-
-
- );
-};
diff --git a/src/widgets/space-workspace/model/useAwayReturnRecovery.ts b/src/widgets/space-workspace/model/useAwayReturnRecovery.ts
deleted file mode 100644
index f5efe4e..0000000
--- a/src/widgets/space-workspace/model/useAwayReturnRecovery.ts
+++ /dev/null
@@ -1,184 +0,0 @@
-'use client';
-
-import { useCallback, useEffect, useRef, useState } from 'react';
-import type { FocusSession } from '@/features/focus-session';
-
-const AWAY_HIDDEN_THRESHOLD_MS = 20_000;
-const AWAY_SLEEP_GAP_THRESHOLD_MS = 90_000;
-const HEARTBEAT_INTERVAL_MS = 15_000;
-
-export type ReturnPromptMode = 'focus' | 'break';
-
-interface UseAwayReturnRecoveryParams {
- currentSession: FocusSession | null;
- isBootstrapping: boolean;
- syncCurrentSession: () => Promise;
-}
-
-interface UseAwayReturnRecoveryResult {
- returnPromptMode: ReturnPromptMode | null;
- dismissReturnPrompt: () => void;
-}
-
-export const useAwayReturnRecovery = ({
- currentSession,
- isBootstrapping,
- syncCurrentSession,
-}: UseAwayReturnRecoveryParams): UseAwayReturnRecoveryResult => {
- const [returnPromptMode, setReturnPromptMode] = useState(null);
- const hiddenAtRef = useRef(null);
- const awayCandidateRef = useRef(false);
- const heartbeatAtRef = useRef(Date.now());
- const isHandlingReturnRef = useRef(false);
-
- const isRunningFocusSession =
- currentSession?.state === 'running' && currentSession.phase === 'focus';
-
- const clearAwayCandidate = useCallback(() => {
- hiddenAtRef.current = null;
- awayCandidateRef.current = false;
- }, []);
-
- const dismissReturnPrompt = useCallback(() => {
- setReturnPromptMode(null);
- clearAwayCandidate();
- }, [clearAwayCandidate]);
-
- useEffect(() => {
- heartbeatAtRef.current = Date.now();
- }, [currentSession?.id]);
-
- useEffect(() => {
- if (!isRunningFocusSession) {
- clearAwayCandidate();
- return;
- }
-
- const intervalId = window.setInterval(() => {
- if (document.visibilityState === 'visible') {
- heartbeatAtRef.current = Date.now();
- }
- }, HEARTBEAT_INTERVAL_MS);
-
- return () => {
- window.clearInterval(intervalId);
- };
- }, [clearAwayCandidate, isRunningFocusSession]);
-
- useEffect(() => {
- if (currentSession?.state !== 'running') {
- if (returnPromptMode === 'focus') {
- setReturnPromptMode(null);
- }
- return;
- }
-
- if (currentSession.phase !== 'break' && returnPromptMode === 'break') {
- setReturnPromptMode(null);
- }
- }, [currentSession?.phase, currentSession?.state, returnPromptMode]);
-
- useEffect(() => {
- if (isBootstrapping) {
- return;
- }
-
- const maybeHandleReturn = async () => {
- if (isHandlingReturnRef.current) {
- return;
- }
-
- const hiddenDuration =
- hiddenAtRef.current == null ? 0 : Date.now() - hiddenAtRef.current;
- const sleepGap = Date.now() - heartbeatAtRef.current;
-
- if (!awayCandidateRef.current) {
- if (
- isRunningFocusSession &&
- document.visibilityState === 'visible' &&
- sleepGap >= AWAY_SLEEP_GAP_THRESHOLD_MS
- ) {
- awayCandidateRef.current = true;
- } else {
- return;
- }
- }
-
- if (hiddenAtRef.current != null && hiddenDuration < AWAY_HIDDEN_THRESHOLD_MS) {
- clearAwayCandidate();
- heartbeatAtRef.current = Date.now();
- return;
- }
-
- isHandlingReturnRef.current = true;
-
- try {
- const syncedSession = await syncCurrentSession();
- const resolvedSession = syncedSession ?? currentSession;
-
- clearAwayCandidate();
- heartbeatAtRef.current = Date.now();
-
- if (!resolvedSession || resolvedSession.state !== 'running') {
- setReturnPromptMode(null);
- return;
- }
-
- if (resolvedSession.phase === 'focus') {
- setReturnPromptMode('focus');
- return;
- }
-
- if (resolvedSession.phase === 'break') {
- setReturnPromptMode('break');
- return;
- }
-
- setReturnPromptMode(null);
- } finally {
- isHandlingReturnRef.current = false;
- }
- };
-
- const handleVisibilityChange = () => {
- if (document.visibilityState === 'hidden') {
- if (isRunningFocusSession) {
- hiddenAtRef.current = Date.now();
- awayCandidateRef.current = true;
- }
-
- return;
- }
-
- void maybeHandleReturn();
- };
-
- const handlePageHide = () => {
- if (isRunningFocusSession) {
- hiddenAtRef.current = Date.now();
- awayCandidateRef.current = true;
- }
- };
-
- const handleWindowFocus = () => {
- void maybeHandleReturn();
- };
-
- document.addEventListener('visibilitychange', handleVisibilityChange);
- window.addEventListener('pagehide', handlePageHide);
- window.addEventListener('focus', handleWindowFocus);
- window.addEventListener('pageshow', handleWindowFocus);
-
- return () => {
- document.removeEventListener('visibilitychange', handleVisibilityChange);
- window.removeEventListener('pagehide', handlePageHide);
- window.removeEventListener('focus', handleWindowFocus);
- window.removeEventListener('pageshow', handleWindowFocus);
- };
- }, [clearAwayCandidate, currentSession, isBootstrapping, isRunningFocusSession, syncCurrentSession]);
-
- return {
- returnPromptMode,
- dismissReturnPrompt,
- };
-};