fix(space): rail과 수정 액션 역할 분리
This commit is contained in:
@@ -55,7 +55,6 @@ Intent Card는 아래 2개 상태만 가진다.
|
|||||||
보이는 것:
|
보이는 것:
|
||||||
|
|
||||||
- goal 1줄
|
- goal 1줄
|
||||||
- 작은 expand affordance 1개
|
|
||||||
|
|
||||||
보이지 않는 것:
|
보이지 않는 것:
|
||||||
|
|
||||||
@@ -98,7 +97,7 @@ Intent Card는 아래 2개 상태만 가진다.
|
|||||||
|
|
||||||
- desktop에서 hover
|
- desktop에서 hover
|
||||||
- focus 진입
|
- focus 진입
|
||||||
- expand affordance 클릭/tap
|
- rail 전체 클릭/tap
|
||||||
|
|
||||||
### Collapsed로 돌아가는 경우
|
### Collapsed로 돌아가는 경우
|
||||||
|
|
||||||
@@ -142,8 +141,9 @@ Intent Card는 아래 2개 상태만 가진다.
|
|||||||
- 1줄 truncate
|
- 1줄 truncate
|
||||||
- medium weight
|
- medium weight
|
||||||
- affordance:
|
- affordance:
|
||||||
- 우측에 작은 chevron / expand control
|
- 별도 chevron / dropdown button은 두지 않는다
|
||||||
- pill/button처럼 보이면 안 된다
|
- rail 자체가 expand trigger로 동작한다
|
||||||
|
- desktop에서는 hover로, mobile에서는 tap으로 자연스럽게 열린다
|
||||||
|
|
||||||
### Expanded card
|
### Expanded card
|
||||||
|
|
||||||
@@ -165,9 +165,10 @@ Intent Card는 아래 2개 상태만 가진다.
|
|||||||
### Goal row
|
### Goal row
|
||||||
|
|
||||||
- 좌측: goal 1줄
|
- 좌측: goal 1줄
|
||||||
- 우측: expand/collapse affordance
|
- 우측: expanded 상태에서만 보이는 `수정` 액션
|
||||||
- goal 텍스트는 expanded 상태에서 클릭 시 refocus 진입
|
- collapsed 상태에서는 rail 전체가 expand trigger로만 동작한다
|
||||||
- collapsed 상태에서는 goal 클릭이 바로 refocus를 여는 대신, rail 자체의 anchor로만 동작해도 괜찮다
|
- expanded 상태에서도 goal 텍스트 자체는 edit trigger가 아니다
|
||||||
|
- refocus는 expanded 상태의 명시적 `수정` 액션으로만 진입한다
|
||||||
|
|
||||||
### MicroStep row
|
### MicroStep row
|
||||||
|
|
||||||
@@ -192,9 +193,9 @@ Intent Card는 아래 2개 상태만 가진다.
|
|||||||
|
|
||||||
### Goal 수정
|
### Goal 수정
|
||||||
|
|
||||||
- expanded 상태에서 goal 텍스트 클릭 -> refocus
|
- expanded 상태의 `수정` 액션 클릭 -> refocus
|
||||||
- collapsed 상태에서는 goal 클릭으로 바로 열지 않거나, 제품 감각을 해치지 않는 범위에서만 허용한다
|
- collapsed 상태의 rail 클릭은 절대 refocus를 열지 않는다
|
||||||
- 핵심은 `expanded`가 먼저 읽히고, `edit`는 그다음이어야 한다
|
- 핵심은 `expand`와 `edit`의 역할이 섞이지 않게 하는 것이다
|
||||||
|
|
||||||
### microStep 완료
|
### microStep 완료
|
||||||
|
|
||||||
@@ -237,6 +238,8 @@ Intent Card는 아래 2개 상태만 가진다.
|
|||||||
- Now chip 재도입 금지
|
- Now chip 재도입 금지
|
||||||
- toolbar/pill/button cluster처럼 보이게 만들기 금지
|
- toolbar/pill/button cluster처럼 보이게 만들기 금지
|
||||||
- 배경보다 card가 먼저 읽히게 만들기 금지
|
- 배경보다 card가 먼저 읽히게 만들기 금지
|
||||||
|
- rail 클릭과 edit 진입을 같은 액션으로 섞기 금지
|
||||||
|
- 별도 chevron/dropdown button을 상시 노출하기 금지
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -57,10 +57,11 @@ Last Updated: 2026-03-14
|
|||||||
- `Return(focus)`는 짧은 re-entry settle motion으로, `Return(break)`는 더 느슨한 release reveal로 분리
|
- `Return(focus)`는 짧은 re-entry settle motion으로, `Return(break)`는 더 느슨한 release reveal로 분리
|
||||||
- `Goal Complete`도 같은 recovery family 안에서 가장 느린 closure motion을 가지도록 조정
|
- `Goal Complete`도 같은 recovery family 안에서 가장 느린 closure motion을 가지도록 조정
|
||||||
- `/space` intent HUD collapsed / expanded 재설계:
|
- `/space` intent HUD collapsed / expanded 재설계:
|
||||||
- 상시 큰 goal 카드 대신 idle에서는 goal 1줄과 작은 expand affordance만 남는 collapsed glass rail 구조로 변경
|
- 상시 큰 goal 카드 대신 idle에서는 goal 1줄만 남는 collapsed glass rail 구조로 변경
|
||||||
- hover / focus / toggle에서만 expanded card로 열리며, 이때만 microStep과 `이번 목표 완료` 액션이 노출됨
|
- hover / focus / rail tap에서만 expanded card로 열리며, 이때만 microStep과 `이번 목표 완료` 액션이 노출됨
|
||||||
- recovery tray(`pause / return / next-beat / complete / refocus`)가 열릴 때는 base card가 강제로 collapsed 상태를 유지하도록 정리
|
- recovery tray(`pause / return / next-beat / complete / refocus`)가 열릴 때는 base card가 강제로 collapsed 상태를 유지하도록 정리
|
||||||
- expanded rail은 outside click으로 접히지만, recovery tray는 outside click으로 닫히지 않고 명시적 액션으로만 닫힘
|
- expanded rail은 outside click으로 접히지만, recovery tray는 outside click으로 닫히지 않고 명시적 액션으로만 닫힘
|
||||||
|
- rail 클릭은 expand/collapse만 담당하고, refocus는 expanded 상태의 `수정` 액션으로만 진입
|
||||||
|
|
||||||
- Focus Entry Surface / Execution Surface 재정의:
|
- Focus Entry Surface / Execution Surface 재정의:
|
||||||
- `/app`을 planning home이 아니라 hero-first focus entry surface로 재구성
|
- `/app`을 planning home이 아니라 hero-first focus entry surface로 재구성
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ Last Updated: 2026-03-14
|
|||||||
- microStep과 `이번 목표 완료`는 expanded 상태에서만 드러난다.
|
- microStep과 `이번 목표 완료`는 expanded 상태에서만 드러난다.
|
||||||
- recovery tray가 열리면 base card는 자동으로 collapsed 상태를 유지한다.
|
- recovery tray가 열리면 base card는 자동으로 collapsed 상태를 유지한다.
|
||||||
- expanded rail은 outside click으로 접히지만, decision tray는 outside click으로 닫히지 않는다.
|
- expanded rail은 outside click으로 접히지만, decision tray는 outside click으로 닫히지 않는다.
|
||||||
|
- rail 클릭은 펼침/접힘만 담당하고, 수정은 expanded 상태의 `수정` 액션으로만 진입한다.
|
||||||
- `/app`을 single-goal commitment gate로 다시 줄였다.
|
- `/app`을 single-goal commitment gate로 다시 줄였다.
|
||||||
- 2-step ritual setup을 제거했다.
|
- 2-step ritual setup을 제거했다.
|
||||||
- current session이 있으면 `Resume` UI만 보여주고, `/space`로 이어가기만 제안한다.
|
- current session이 있으면 `Resume` UI만 보여주고, `/space`로 이어가기만 제안한다.
|
||||||
|
|||||||
@@ -39,7 +39,6 @@ export const space = {
|
|||||||
intentLabel: '이번 세션 목표',
|
intentLabel: '이번 세션 목표',
|
||||||
microStepLabel: '지금 할 한 조각',
|
microStepLabel: '지금 할 한 조각',
|
||||||
intentExpandAriaLabel: '목표 카드 펼치기',
|
intentExpandAriaLabel: '목표 카드 펼치기',
|
||||||
intentCollapseAriaLabel: '목표 카드 접기',
|
|
||||||
refocusButton: '목표 수정',
|
refocusButton: '목표 수정',
|
||||||
intentEditLabel: '수정',
|
intentEditLabel: '수정',
|
||||||
refocusTitle: '다시 방향 잡기',
|
refocusTitle: '다시 방향 잡기',
|
||||||
|
|||||||
@@ -64,25 +64,12 @@ export const IntentCapsule = ({
|
|||||||
};
|
};
|
||||||
}, [isExpanded, showActions]);
|
}, [isExpanded, showActions]);
|
||||||
|
|
||||||
const handleGoalClick = () => {
|
const handleExpand = () => {
|
||||||
if (!canInteract) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isExpanded) {
|
|
||||||
setPinnedExpanded(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
onOpenRefocus();
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleToggleExpanded = () => {
|
|
||||||
if (!showActions) {
|
if (!showActions) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setPinnedExpanded((current) => !current);
|
setPinnedExpanded(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -130,76 +117,56 @@ export const IntentCapsule = ({
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<div className="flex items-center gap-3">
|
{isExpanded ? (
|
||||||
<button
|
<div className="flex items-center gap-3">
|
||||||
type="button"
|
<div className="min-w-0 flex-1">
|
||||||
onClick={handleGoalClick}
|
<p className="min-w-0 truncate text-[18px] font-medium tracking-tight text-white/96 md:text-[20px]">
|
||||||
disabled={!canInteract}
|
|
||||||
aria-label={copy.space.focusHud.refocusButton}
|
|
||||||
className="min-w-0 flex-1 text-left transition-opacity hover:opacity-90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/14 disabled:cursor-default disabled:hover:opacity-100"
|
|
||||||
>
|
|
||||||
<span className="flex min-w-0 items-center gap-2">
|
|
||||||
<p
|
|
||||||
className={cn(
|
|
||||||
'min-w-0 truncate font-medium tracking-tight text-white/96 transition-[font-size,line-height] duration-200 ease-out',
|
|
||||||
isExpanded ? 'text-[18px] md:text-[20px]' : 'text-[15px] md:text-[16px]',
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{goal}
|
{goal}
|
||||||
</p>
|
</p>
|
||||||
{isExpanded && canInteract ? (
|
</div>
|
||||||
<span className="inline-flex shrink-0 items-center gap-1 rounded-full border border-white/10 bg-black/10 px-2 py-1 text-[11px] font-medium tracking-[0.06em] text-white/54 transition-colors duration-200 group-hover:text-white/72">
|
|
||||||
<svg
|
|
||||||
aria-hidden
|
|
||||||
viewBox="0 0 16 16"
|
|
||||||
className="h-3 w-3"
|
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
strokeLinecap="round"
|
|
||||||
strokeLinejoin="round"
|
|
||||||
strokeWidth="1.5"
|
|
||||||
>
|
|
||||||
<path d="m10.9 2.6 2.5 2.5" />
|
|
||||||
<path d="M3.4 12.6 2.6 15l2.4-.8 7.7-7.7-1.6-1.6-7.7 7.7Z" />
|
|
||||||
</svg>
|
|
||||||
<span>{copy.space.focusHud.intentEditLabel}</span>
|
|
||||||
</span>
|
|
||||||
) : null}
|
|
||||||
</span>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
{showActions ? (
|
{canInteract ? (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={handleToggleExpanded}
|
onClick={onOpenRefocus}
|
||||||
aria-label={
|
aria-label={copy.space.focusHud.refocusButton}
|
||||||
isExpanded
|
className="inline-flex shrink-0 items-center gap-1 rounded-full border border-white/10 bg-black/10 px-2 py-1 text-[11px] font-medium tracking-[0.06em] text-white/54 transition-colors duration-200 hover:text-white/72 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/14"
|
||||||
? copy.space.focusHud.intentCollapseAriaLabel
|
>
|
||||||
: copy.space.focusHud.intentExpandAriaLabel
|
<svg
|
||||||
}
|
aria-hidden
|
||||||
|
viewBox="0 0 16 16"
|
||||||
|
className="h-3 w-3"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
strokeWidth="1.5"
|
||||||
|
>
|
||||||
|
<path d="m10.9 2.6 2.5 2.5" />
|
||||||
|
<path d="M3.4 12.6 2.6 15l2.4-.8 7.7-7.7-1.6-1.6-7.7 7.7Z" />
|
||||||
|
</svg>
|
||||||
|
<span>{copy.space.focusHud.intentEditLabel}</span>
|
||||||
|
</button>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={handleExpand}
|
||||||
|
disabled={!showActions}
|
||||||
|
aria-label={copy.space.focusHud.intentExpandAriaLabel}
|
||||||
|
className="flex min-w-0 w-full items-center text-left transition-opacity hover:opacity-92 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/14 disabled:cursor-default disabled:hover:opacity-100"
|
||||||
|
>
|
||||||
|
<p
|
||||||
className={cn(
|
className={cn(
|
||||||
'inline-flex h-8 w-8 shrink-0 items-center justify-center rounded-full border border-white/8 bg-black/10 text-white/56 transition-all duration-200 hover:border-white/14 hover:bg-black/16 hover:text-white/82 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/12',
|
'min-w-0 flex-1 truncate font-medium tracking-tight text-white/96 transition-[font-size,line-height] duration-200 ease-out',
|
||||||
isExpanded && 'border-white/12 bg-black/14 text-white/82',
|
'text-[15px] md:text-[16px]',
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<svg
|
{goal}
|
||||||
aria-hidden
|
</p>
|
||||||
viewBox="0 0 16 16"
|
</button>
|
||||||
className={cn(
|
)}
|
||||||
'h-3.5 w-3.5 transition-transform duration-200 ease-out',
|
|
||||||
isExpanded ? 'rotate-180' : 'rotate-0',
|
|
||||||
)}
|
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
strokeLinecap="round"
|
|
||||||
strokeLinejoin="round"
|
|
||||||
strokeWidth="1.6"
|
|
||||||
>
|
|
||||||
<path d="M4.5 6.5 8 10l3.5-3.5" />
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
|
|||||||
Reference in New Issue
Block a user