-
- 프리플로우
+
+ {ROUTES.map((route) => (
+
+
+
+
{route.name}
+
+ {route.tag}
+
+
+
{route.durationMinutes}min
+
+
{route.description}
+
+ 바로 출항
+
-
무제한
-
-
-
-
- {/* ✅ 버튼 위에 설명 (버튼 안에 설명 X) */}
-
-
- 시간 고정 세션
-
-
- 한 번 실행되고 끝나면 요약으로 이동
-
+
+ 정거장에서 3명이 대기 중
-
- {/* ✅ row(2열) */}
-
- openDialog("sprint")}
- />
- openDialog("deepwork")}
- />
-
-
-
-
-
- );
-}
-
-function ModeTile({
- title,
- meta,
- onClick,
-}: {
- title: string;
- meta: string;
- onClick: () => void;
-}) {
- return (
-
-
-
- );
-}
-
-function SessionGoalDialog({
- open,
- onOpenChange,
- mode,
- meta,
- goal,
- setGoal,
- onStart,
-}: {
- open: boolean;
- onOpenChange: (v: boolean) => void;
- mode: Mode | null;
- meta: string;
- goal: string;
- setGoal: (v: string) => void;
- onStart: () => void;
-}) {
- const title = mode ? `${modeLabel(mode)} · ${meta}` : "세션";
-
- return (
-
+
+
);
}
diff --git a/src/components/LobbyBackground.tsx b/src/components/LobbyBackground.tsx
new file mode 100644
index 0000000..e665728
--- /dev/null
+++ b/src/components/LobbyBackground.tsx
@@ -0,0 +1,59 @@
+export default function LobbyBackground() {
+ return (
+
+ {/* Orion (Approximate) - Bottom Left */}
+
+
+
+
+ {/* Lyra (Approximate) - Top Right */}
+
+
+
+
+ {/* Ursa Major (Big Dipper) - Top Left */}
+
+
+ );
+}
diff --git a/src/lib/constants.ts b/src/lib/constants.ts
new file mode 100644
index 0000000..0a9677e
--- /dev/null
+++ b/src/lib/constants.ts
@@ -0,0 +1,25 @@
+import { Route } from "@/types";
+
+export const ROUTES: Route[] = [
+ {
+ id: 'orion',
+ name: '오리온',
+ durationMinutes: 180,
+ tag: '딥워크',
+ description: '집필, 코딩 등 긴 호흡이 필요한 작업'
+ },
+ {
+ id: 'lyra',
+ name: '거문고',
+ durationMinutes: 60,
+ tag: '정리/기획',
+ description: '기획안 작성, 문서 정리'
+ },
+ {
+ id: 'cygnus',
+ name: '백조',
+ durationMinutes: 30,
+ tag: '리뷰/회고',
+ description: '하루 회고, 코드 리뷰'
+ },
+];
diff --git a/src/lib/store.ts b/src/lib/store.ts
new file mode 100644
index 0000000..6a6229b
--- /dev/null
+++ b/src/lib/store.ts
@@ -0,0 +1,43 @@
+import { Voyage, UserPreferences } from "@/types";
+
+const KEYS = {
+ HISTORY: 'focustella_history_v1',
+ CURRENT: 'focustella_current_v1',
+ PREFS: 'focustella_prefs_v1',
+};
+
+export const getHistory = (): Voyage[] => {
+ if (typeof window === 'undefined') return [];
+ const item = localStorage.getItem(KEYS.HISTORY);
+ return item ? JSON.parse(item) : [];
+};
+
+export const saveToHistory = (voyage: Voyage) => {
+ const history = getHistory();
+ // Add to beginning
+ localStorage.setItem(KEYS.HISTORY, JSON.stringify([voyage, ...history]));
+};
+
+export const getCurrentVoyage = (): Voyage | null => {
+ if (typeof window === 'undefined') return null;
+ const item = localStorage.getItem(KEYS.CURRENT);
+ return item ? JSON.parse(item) : null;
+};
+
+export const saveCurrentVoyage = (voyage: Voyage | null) => {
+ if (voyage === null) {
+ localStorage.removeItem(KEYS.CURRENT);
+ } else {
+ localStorage.setItem(KEYS.CURRENT, JSON.stringify(voyage));
+ }
+};
+
+export const getPreferences = (): UserPreferences => {
+ if (typeof window === 'undefined') return { hideSeconds: false };
+ const item = localStorage.getItem(KEYS.PREFS);
+ return item ? JSON.parse(item) : { hideSeconds: false };
+};
+
+export const savePreferences = (prefs: UserPreferences) => {
+ localStorage.setItem(KEYS.PREFS, JSON.stringify(prefs));
+};
diff --git a/src/types/index.ts b/src/types/index.ts
new file mode 100644
index 0000000..f19e094
--- /dev/null
+++ b/src/types/index.ts
@@ -0,0 +1,28 @@
+export interface Route {
+ id: string;
+ name: string;
+ durationMinutes: number;
+ tag: string;
+ description: string;
+}
+
+export type VoyageStatus = 'completed' | 'partial' | 'reoriented' | 'aborted' | 'in_progress';
+
+export interface Voyage {
+ id: string;
+ routeId: string;
+ routeName: string;
+ startedAt: number;
+ endedAt?: number;
+ durationMinutes: number;
+ status: VoyageStatus;
+ missionText: string;
+ notes?: string;
+ debriefProgress?: string;
+ nextAction?: string;
+ blockerTag?: string;
+}
+
+export interface UserPreferences {
+ hideSeconds: boolean;
+}