From 3c5154178d3190181f6a608778bdf20a9afa095d Mon Sep 17 00:00:00 2001 From: corpi Date: Sun, 15 Mar 2026 23:10:29 +0900 Subject: [PATCH] =?UTF-8?q?fix(space):=20break=EC=99=80=20recovery=20?= =?UTF-8?q?=EC=83=81=ED=83=9C=EC=9D=98=20=EC=99=84=EB=A3=8C=20=EA=B2=BD?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=B5=EA=B5=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/10_refocus_system_spec.md | 19 ++++++++++++++ docs/90_current_state.md | 4 +++ docs/session_brief.md | 4 +++ src/shared/i18n/messages/space.ts | 6 +++++ .../ui/NextMicroStepPrompt.tsx | 16 ++++++++++++ .../space-focus-hud/ui/PauseRefocusPrompt.tsx | 16 ++++++++++++ .../space-focus-hud/ui/ReturnPrompt.tsx | 26 +++++++++++++++++++ .../ui/SpaceFocusHudWidget.tsx | 14 +++++++++- 8 files changed, 104 insertions(+), 1 deletion(-) diff --git a/docs/10_refocus_system_spec.md b/docs/10_refocus_system_spec.md index b48f231..5fb353f 100644 --- a/docs/10_refocus_system_spec.md +++ b/docs/10_refocus_system_spec.md @@ -178,6 +178,25 @@ Refocus는 아래 5개 상태만 가진다. ## 8. 상태별 상세 UX +### CTA Matrix + +Refocus family에서 중요한 원칙은 하나다. + +> 세션이 아직 살아 있는 상태라면, 사용자는 언제든 `계속 / 다시 잡기 / 마무리` 중 하나로 갈 수 있어야 한다. + +상태별 CTA는 아래처럼 고정한다. + +| 상태 | 보여야 하는 주 행동 | goal complete 접근 | +| --- | --- | --- | +| `Focused` | `수정`, `microStep 완료`, `이번 목표 완료`, timer `pause/reset` | base intent card에서 직접 노출 | +| `Paused` | `한 조각 다시 잡기`, `바로 이어가기` | pause tray의 low-emphasis `여기서 마무리하기` | +| `Return (focus)` | `멈춘 자리에서 이어가기`, `한 조각 다시 잡기` | return tray의 low-emphasis `여기서 마무리하기` | +| `Break` | break 유지, 다음 블록으로 이어가기, goal closure | base intent card에서 직접 노출 | +| `Return (break)` | `쉬기 이어가기`, `다음 블록 이어가기`, `한 조각 다시 잡기` | return tray의 low-emphasis `여기서 마무리하기` | +| `Next Beat` | `목표만 두고 계속하기`, `다음 단계 적기` | next-beat tray의 low-emphasis `이 목표는 여기서 마무리하기` | + +`Refocus` 편집 시트 자체는 편집에만 집중하고, complete 액션은 별도 decision tray나 base intent card에서 수행한다. + ### 8.1 Focused 목표: diff --git a/docs/90_current_state.md b/docs/90_current_state.md index ab231e1..ef823c5 100644 --- a/docs/90_current_state.md +++ b/docs/90_current_state.md @@ -61,6 +61,10 @@ Last Updated: 2026-03-15 - `Pause` tray는 빠르게 다시 붙잡는 recovery reveal로 조정 - `Return(focus)`는 짧은 re-entry settle motion으로, `Return(break)`는 더 느슨한 release reveal로 분리 - `Goal Complete`도 같은 recovery family 안에서 가장 느린 closure motion을 가지도록 조정 +- `/space` goal closure CTA matrix 정렬: + - `break` phase에서도 expanded intent card 안에 `이번 목표 완료`를 유지하도록 수정 + - base card가 잠기는 recovery overlay(`pause / return / next-beat`) 안에서도 low-emphasis `여기서 마무리하기` 경로를 추가 + - 이제 active session 상태에서는 `계속 / 다시 잡기 / 마무리` 중 최소 한 경로가 항상 보이도록 정리 - `/space` intent HUD collapsed / expanded 재설계: - 상시 큰 goal 카드 대신 idle에서는 goal 1줄만 남는 collapsed glass rail 구조로 변경 - hover / focus / rail tap에서만 expanded card로 열리며, 이때만 microStep과 `이번 목표 완료` 액션이 노출됨 diff --git a/docs/session_brief.md b/docs/session_brief.md index 1c4f96a..7d59af8 100644 --- a/docs/session_brief.md +++ b/docs/session_brief.md @@ -66,6 +66,10 @@ Last Updated: 2026-03-15 - `Pause`는 빠르게 다시 붙잡는 recovery reveal로, - `Return(focus)`는 재진입에 맞는 짧은 settle motion으로, - `Return(break)`와 `Goal Complete`는 더 느슨한 release/closure reveal로 분리했다. +- `/space` active session에서는 goal closure 경로가 항상 남도록 정리했다. + - `break`에서도 expanded goal card 안에 `이번 목표 완료`가 보인다. + - `pause / return / next-beat`처럼 base card가 잠기는 overlay 안에는 low-emphasis `여기서 마무리하기`가 추가됐다. + - 그래서 사용자는 recovery 상태에서도 `계속 / 다시 잡기 / 마무리` 중 하나를 바로 고를 수 있다. - `/space` 목표 카드를 collapsed / expanded 구조로 재설계했다. - idle에서는 goal 1줄만 남는 얇은 glass rail로 줄였다. - microStep과 `이번 목표 완료`는 expanded 상태에서만 드러난다. diff --git a/src/shared/i18n/messages/space.ts b/src/shared/i18n/messages/space.ts index 4a3522d..21f1e22 100644 --- a/src/shared/i18n/messages/space.ts +++ b/src/shared/i18n/messages/space.ts @@ -62,6 +62,8 @@ export const space = { pausePromptRefocusHint: '목표는 그대로 두고, 지금 다시 시작할 한 줄만 정리해요.', pausePromptKeep: '바로 이어가기', pausePromptKeepHint: '지금 방향을 유지한 채, 멈춘 자리에서 다시 시작해요.', + pausePromptFinish: '여기서 마무리하기', + pausePromptFinishHint: '다시 이어가기보다, 지금 블록을 여기서 조용히 닫습니다.', returnPromptEyebrow: '다시 돌아왔어요', returnPromptFocusTitle: '흐름은 아직 남아 있어요.', returnPromptFocusDescription: '멈춘 자리에서 바로 이어가거나, 다시 시작할 한 조각만 조용히 다듬을 수 있어요.', @@ -75,6 +77,8 @@ export const space = { returnPromptNextHint: '다음 한 조각을 정하고, 같은 흐름 안에서 부드럽게 이어갑니다.', returnPromptRefocus: '한 조각 다시 잡기', returnPromptRefocusHint: '왜 멈췄는지는 건너뛰고, 지금 다시 시작할 한 줄만 남깁니다.', + returnPromptFinish: '여기서 마무리하기', + returnPromptFinishHint: '이 흐름은 여기서 닫고, 다음 진입은 가볍게 남겨둡니다.', microStepCompleteAriaLabel: '현재 한 조각 완료', microStepPromptEyebrow: '다음 단계', microStepPromptTitle: '바로 이어서 할 다음 단계가 있나요?', @@ -83,6 +87,8 @@ export const space = { microStepPromptKeepHint: '다음 단계는 비워두고, 같은 목표 안에서 이어서 집중해요.', microStepPromptDefine: '다음 단계 적기', microStepPromptDefineHint: '바로 손을 움직일 수 있는 가장 작은 다음 행동을 한 줄로 남겨요.', + microStepPromptFinish: '이 목표는 여기서 마무리하기', + microStepPromptFinishHint: '다음 단계를 늘리지 않고, 지금 블록을 깔끔하게 닫습니다.', microStepCleared: '지금 할 한 조각을 비우고 목표만 유지해요.', completeAction: '이번 목표 완료', }, diff --git a/src/widgets/space-focus-hud/ui/NextMicroStepPrompt.tsx b/src/widgets/space-focus-hud/ui/NextMicroStepPrompt.tsx index a13f304..3be9956 100644 --- a/src/widgets/space-focus-hud/ui/NextMicroStepPrompt.tsx +++ b/src/widgets/space-focus-hud/ui/NextMicroStepPrompt.tsx @@ -22,6 +22,7 @@ interface NextMicroStepPromptProps { error: string | null; onKeepGoalOnly: () => void; onDefineNext: () => void; + onFinish: () => void; } export const NextMicroStepPrompt = ({ @@ -31,6 +32,7 @@ export const NextMicroStepPrompt = ({ error, onKeepGoalOnly, onDefineNext, + onFinish, }: NextMicroStepPromptProps) => { const trimmedGoal = goal.trim(); @@ -106,6 +108,20 @@ export const NextMicroStepPrompt = ({ + +
+ +

+ {copy.space.focusHud.microStepPromptFinishHint} +

+
diff --git a/src/widgets/space-focus-hud/ui/PauseRefocusPrompt.tsx b/src/widgets/space-focus-hud/ui/PauseRefocusPrompt.tsx index 360dca0..2b9bc64 100644 --- a/src/widgets/space-focus-hud/ui/PauseRefocusPrompt.tsx +++ b/src/widgets/space-focus-hud/ui/PauseRefocusPrompt.tsx @@ -23,6 +23,7 @@ interface PauseRefocusPromptProps { isBusy: boolean; onRefocus: () => void; onKeepCurrent: () => void; + onFinish: () => void; } export const PauseRefocusPrompt = ({ @@ -30,6 +31,7 @@ export const PauseRefocusPrompt = ({ isBusy, onRefocus, onKeepCurrent, + onFinish, }: PauseRefocusPromptProps) => { return (
+ +
+ +

+ {copy.space.focusHud.pausePromptFinishHint} +

+
diff --git a/src/widgets/space-focus-hud/ui/ReturnPrompt.tsx b/src/widgets/space-focus-hud/ui/ReturnPrompt.tsx index bd1e472..8a2009b 100644 --- a/src/widgets/space-focus-hud/ui/ReturnPrompt.tsx +++ b/src/widgets/space-focus-hud/ui/ReturnPrompt.tsx @@ -30,6 +30,7 @@ interface ReturnPromptProps { onRefocus: () => void; onRest?: () => void; onNextGoal?: () => void; + onFinish: () => void; } export const ReturnPrompt = ({ @@ -40,6 +41,7 @@ export const ReturnPrompt = ({ onRefocus, onRest, onNextGoal, + onFinish, }: ReturnPromptProps) => { const isBreakReturn = mode === 'break'; @@ -166,6 +168,30 @@ export const ReturnPrompt = ({ )} + +
+ +

+ {copy.space.focusHud.returnPromptFinishHint} +

+
diff --git a/src/widgets/space-focus-hud/ui/SpaceFocusHudWidget.tsx b/src/widgets/space-focus-hud/ui/SpaceFocusHudWidget.tsx index 2c97945..3963b68 100644 --- a/src/widgets/space-focus-hud/ui/SpaceFocusHudWidget.tsx +++ b/src/widgets/space-focus-hud/ui/SpaceFocusHudWidget.tsx @@ -285,7 +285,7 @@ export const SpaceFocusHudWidget = ({ goal={normalizedGoal} microStep={microStep} canRefocus={Boolean(hasActiveSession)} - canComplete={hasActiveSession && sessionPhase === 'focus'} + canComplete={hasActiveSession && (sessionPhase === 'focus' || sessionPhase === 'break')} showActions={!isIntentOverlayOpen} onOpenRefocus={() => openRefocus('goal', 'manual')} onMicroStepDone={() => { @@ -317,6 +317,10 @@ export const SpaceFocusHudWidget = ({ handleDismissReturnPrompt(); handleOpenCompleteSheet('next'); }} + onFinish={() => { + handleDismissReturnPrompt(); + handleOpenCompleteSheet('choice'); + }} /> { + setOverlay('none'); + handleOpenCompleteSheet('choice'); + }} /> { + setIntentError(null); + handleOpenCompleteSheet('choice'); + }} />