(null)
// 단계적 Esc: 새 대화 화면에선 목록으로.
useEscapeKey(() => navigate(PATHS.SNAP))
// 첫 전송: 세션 생성 → 채팅 페이지로 이동하며 firstMessage 전달(거기서 스트림).
- const start = async (text: string, images: SnapImageInput[]) => {
- if (pending) return
- setPending(true)
- const session = await createSession.mutateAsync()
- navigate(`/snap/s/${session.id}`, { state: { firstMessage: text, firstImages: images } })
+ // mutate + 콜백(SnapUserControls 의 logout 과 같은 패턴) — mutateAsync 는 거부가 밖으로 새서 안 씀.
+ const start = (text: string, images: SnapImageInput[]) => {
+ if (pending !== null) return
+ setPending(text.trim() || (images.length ? `이미지 ${images.length}장` : "…"))
+ createSession.mutate(undefined, {
+ onSuccess: (session) =>
+ navigate(`/snap/s/${session.id}`, { state: { firstMessage: text, firstImages: images } }),
+ onError: () => {
+ // 세션 생성 실패(OpenCode 죽음 등) — 헤로로 되돌리고 알림. 안 풀면 "응답 중" 에 영원히 갇힘.
+ setPending(null)
+ toast.error("대화를 시작하지 못했어. 서버 상태를 확인하고 다시 보내봐.")
+ },
+ })
}
return (
@@ -39,12 +50,27 @@ export default function NewChatPage() {
Esc
-
- void start(text, [])} disabled={pending} />
-
+ {pending === null ? (
+
+ start(text, [])} disabled={false} />
+
+ ) : (
+ // 세션 만드는 동안 — SessionChatPage 첫 화면과 같은 모양(내 말풍선 + 생각 중)
+
+
+
+
+ CODEASSIST
+
+
+ 생각하는 중…
+
+
+
+ )}
diff --git a/z-my-docs/work-log/2026-09/2026-09-21.md b/z-my-docs/work-log/2026-09/2026-09-21.md
index 4783228..dc87951 100644
--- a/z-my-docs/work-log/2026-09/2026-09-21.md
+++ b/z-my-docs/work-log/2026-09/2026-09-21.md
@@ -16,3 +16,4 @@
| 14:54 | 로컬도 이미지 요청만 vision 모델 — OPENCODE_VISION_MODEL(openrouter/google/gemma-4-31b-it), 백엔드가 prompt 에 model 실음. 텍스트는 GLM 5.2 그대로 |
| 14:58 | 팀원용 가이드 — ABAP_INDEXING 을 FabriX 로 돌리기. 직접 붙이면 401(헤더 3종)이라 CodeAssist 게이트웨이(/api/ito) 경유, LLM_MODEL=339, 확인 항목 3개 |
| 15:21 | taste-skill 설치·적용 — 랜딩용이라 대부분 해당 없고 3개만: 삼성 팔레트 글씨 순검정→#111, radius 규칙(버튼 알약/카드·입력 10px), 버튼 눌림 피드백 |
+| 15:28 | 새 대화 전송 시 세션 생성(OpenCode 왕복) 동안 헤로에 머물던 것 — 즉시 내 말풍선+'생각하는 중' 시머로 바꿔 넘어간 느낌, 실패하면 헤로 복귀+토스트(전엔 pending 안 풀려 영원히 '응답 중'). mutate 콜백 패턴으로 |