diff --git a/CHANGELOG.md b/CHANGELOG.md index 9332c5e..6fda593 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,14 @@ 본 프로젝트의 모든 의미있는 변경은 본 파일에 기록한다. 형식: [Keep a Changelog](https://keepachangelog.com/) · 버전: [SemVer](https://semver.org/). +## [0.4.2] — 2026-06-15 (hotfix, dev) + +### Fixed (Redmine #342) +- **ChatScreen 하단 잘림** — Android edge-to-edge 모드에서 시스템 nav bar (3-button / gesture handle) 가 입력창을 덮던 문제. `Scaffold.body` 를 `SafeArea(top: false, …)` 로 감쌈. AppBar 가 이미 top inset 처리하므로 top 만 false. + +### Dev +- **LLM 실패 빨간 배너에 full message + stack trace** — 단말 진단을 위해 release 빌드에서도 노출. `LLM 응답 실패: \n\n--- STACK ---\n` 형식. SelectableText + monospace + 최대 화면 1/3 높이 + scroll. 사용자 친화 메시지로 좁히는 작업은 #342 종료 후 follow-up. + ## [0.4.1] — 2026-06-15 ### Added — ChatScreen LLM warm-up (Redmine #311, follow-up of #260) diff --git a/app/lib/state/chat_providers.dart b/app/lib/state/chat_providers.dart index 8a5c1e6..a4b6704 100644 --- a/app/lib/state/chat_providers.dart +++ b/app/lib/state/chat_providers.dart @@ -206,12 +206,16 @@ class ChatSessionController extends StateNotifier { clearStreamingText: true, error: '도구 호출 루프가 너무 길어 중단했습니다.', ); - } catch (e) { + } catch (e, st) { if (!mounted) return; + // 개발 단계 (#342) — 실 단말 진단을 위해 release 빌드에서도 full + // message + stack 노출. 사용자 친화 메시지로 다시 좁히는 작업은 + // #342 종료 후 follow-up. + final detail = 'LLM 응답 실패: ${e.runtimeType}\n$e\n\n--- STACK ---\n$st'; state = state.copyWith( isStreaming: false, clearStreamingText: true, - error: 'LLM 응답 실패: ${e.runtimeType}', + error: detail, ); } } diff --git a/app/lib/ui/screens/chat_screen.dart b/app/lib/ui/screens/chat_screen.dart index 085377f..597d335 100644 --- a/app/lib/ui/screens/chat_screen.dart +++ b/app/lib/ui/screens/chat_screen.dart @@ -76,10 +76,16 @@ class _ChatScreenState extends ConsumerState { ), ], ), - body: depsAsync.when( - loading: () => const Center(child: CircularProgressIndicator()), - error: (e, _) => Center(child: Text('초기화 실패: $e')), - data: (_) => _buildBody(context), + // Android edge-to-edge: 시스템 nav bar (3-button / gesture handle) 가 + // 입력창을 가리지 않도록 SafeArea 로 감싼다. AppBar 가 이미 top inset + // 을 처리하므로 top 만 false. + body: SafeArea( + top: false, + child: depsAsync.when( + loading: () => const Center(child: CircularProgressIndicator()), + error: (e, _) => Center(child: Text('초기화 실패: $e')), + data: (_) => _buildBody(context), + ), ), ); } @@ -105,14 +111,23 @@ class _ChatScreenState extends ConsumerState { children: [ if (warmup is ChatWarmupFailed) _WarmupErrorBanner(warmup: warmup), if (state.error != null) + // #342 dev — 단말에서 원인 진단을 위해 stack 까지 노출되는 케이스를 + // 위해 multi-line + scrollable + selectable. 높이는 화면의 1/3 까지만. Container( width: double.infinity, color: theme.colorScheme.errorContainer, padding: const EdgeInsets.all(12), - child: Text( - state.error!, - style: TextStyle( - color: theme.colorScheme.onErrorContainer, + constraints: BoxConstraints( + maxHeight: MediaQuery.of(context).size.height / 3, + ), + child: SingleChildScrollView( + child: SelectableText( + state.error!, + style: TextStyle( + color: theme.colorScheme.onErrorContainer, + fontFamily: 'monospace', + fontSize: 12, + ), ), ), ), diff --git a/app/pubspec.yaml b/app/pubspec.yaml index 06de7ed..934ddef 100644 --- a/app/pubspec.yaml +++ b/app/pubspec.yaml @@ -1,7 +1,7 @@ name: life_helper description: "Huberman + Atomic Habits + Tiny Habits + If-Then. Local-first habit/checklist/todo." publish_to: 'none' -version: 0.4.1+5 +version: 0.4.2+6 environment: sdk: ^3.12.2