From ef77858d82f6ae6fc397d56546105b014eab2aea Mon Sep 17 00:00:00 2001
From: Matthias Nott <mnott@mnsoft.org>
Date: Sun, 22 Mar 2026 07:46:08 +0100
Subject: [PATCH] fix: keyboard dismiss on tap outside, line breaks, session switch
---
TODO.md | 37 +++++++++++++++++++++++++++++++++++++
lib/widgets/input_bar.dart | 3 +--
lib/screens/chat_screen.dart | 9 +++++++--
3 files changed, 45 insertions(+), 4 deletions(-)
diff --git a/TODO.md b/TODO.md
new file mode 100644
index 0000000..b2fc759
--- /dev/null
+++ b/TODO.md
@@ -0,0 +1,37 @@
+# PAILot Flutter - TODO
+
+## Pending Features
+
+### File Transfer (send/receive arbitrary files)
+- File picker in app (PDFs, Word docs, attachments, etc.)
+- New `file` message type in WebSocket protocol
+- Gateway handler to save received files and route to session
+- Session can send files back via `pailot_send_file`
+- Display file attachments in chat bubbles (icon + filename + tap to open)
+
+### Voice+Image Combined Message
+- When voice caption is recorded with images, hold images on server until voice transcript arrives
+- Deliver transcript + images together as one message to Claude
+- Ensures voice prefix sets reply channel correctly
+
+### Push Notifications (iOS APNs)
+- Notify when messages arrive while app is backgrounded/closed
+- Requires Apple Developer Portal APNs key setup
+- Server-side message queue for offline delivery
+
+### App Name Renaming (Runner → PAILot)
+- Rename Xcode target from Runner to PAILot (like Glidr did)
+- Update scheme names, bundle paths
+
+## Known Issues
+
+### Audio
+- Background audio may not survive full app termination (only screen lock)
+- Audio session category may conflict with phone calls
+
+### UI
+- Launch image still uses default Flutter placeholder
+- No app splash screen with PAILot branding
+
+### Navigation
+- vi keys (0, G, dd) are sent as literal text paste — works for Claude Code but may not for other terminals
diff --git a/lib/screens/chat_screen.dart b/lib/screens/chat_screen.dart
index f1d4436..b773415 100644
--- a/lib/screens/chat_screen.dart
+++ b/lib/screens/chat_screen.dart
@@ -364,9 +364,10 @@
}
Future<void> _switchSession(String sessionId) async {
- // Stop any playing audio when switching sessions
+ // Stop any playing audio and dismiss keyboard when switching sessions
await AudioService.stopPlayback();
setState(() => _playingMessageId = null);
+ if (mounted) FocusScope.of(context).unfocus();
ref.read(activeSessionIdProvider.notifier).state = sessionId;
await ref.read(messagesProvider.notifier).switchSession(sessionId);
@@ -391,6 +392,7 @@
ref.read(messagesProvider.notifier).addMessage(message);
_textController.clear();
+ FocusScope.of(context).unfocus(); // dismiss keyboard
// Send as plain text (not command) — gateway handles plain messages
_ws?.send({
@@ -822,7 +824,9 @@
final unreadCounts = ref.watch(unreadCountsProvider);
final inputMode = ref.watch(inputModeProvider);
- return Scaffold(
+ return GestureDetector(
+ onTap: () => FocusScope.of(context).unfocus(),
+ child: Scaffold(
key: _scaffoldKey,
appBar: AppBar(
leading: IconButton(
@@ -927,6 +931,7 @@
),
],
),
+ ),
);
}
}
diff --git a/lib/widgets/input_bar.dart b/lib/widgets/input_bar.dart
index 278534e..5ba3fa2 100644
--- a/lib/widgets/input_bar.dart
+++ b/lib/widgets/input_bar.dart
@@ -111,8 +111,7 @@
const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
isDense: true,
),
- textInputAction: TextInputAction.send,
- onSubmitted: (_) => onSendText(),
+ textInputAction: TextInputAction.newline,
maxLines: 4,
minLines: 1,
),
--
Gitblit v1.3.1