fix: two-row header layout, flexible path input, button error handling
- Split header into two rows: breadcrumb + count on top, path/buttons below - Path input is now flexible (fills available width) instead of fixed 320px - Fixes Row overflow on narrow windows - Load/Browse buttons now same height (36px) as path input - Added try/catch with snackbar error feedback for Load and Browse - Removed initialDirectory from file picker (could fail in non-sandbox) - Browse also updates the path input field when a folder is selected - Signed, notarized, stapled DMG
This commit is contained in:
parent
53ed5a6cd1
commit
f6b496dad7
@ -332,12 +332,25 @@ class _HomeScreenState extends State<HomeScreen> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _pickFolder() async {
|
Future<void> _pickFolder() async {
|
||||||
|
try {
|
||||||
final result = await FilePicker.platform.getDirectoryPath(
|
final result = await FilePicker.platform.getDirectoryPath(
|
||||||
dialogTitle: 'Select a folder containing Claude session files',
|
dialogTitle: 'Select a folder containing Claude session files',
|
||||||
);
|
);
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
|
_pathController.text = result;
|
||||||
await _scanFolder(result);
|
await _scanFolder(result);
|
||||||
}
|
}
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint('[Browse] Error: $e');
|
||||||
|
if (mounted) {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(
|
||||||
|
content: Text('Could not open folder picker: $e'),
|
||||||
|
backgroundColor: AppColors.error,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _scanFolder(String folderPath) async {
|
Future<void> _scanFolder(String folderPath) async {
|
||||||
@ -393,15 +406,25 @@ class _HomeScreenState extends State<HomeScreen> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _loadSingleFile() async {
|
Future<void> _loadSingleFile() async {
|
||||||
final home = _getRealHome() ?? '';
|
try {
|
||||||
final result = await FilePicker.platform.pickFiles(
|
final result = await FilePicker.platform.pickFiles(
|
||||||
type: FileType.custom,
|
type: FileType.custom,
|
||||||
allowedExtensions: ['jsonl'],
|
allowedExtensions: ['jsonl'],
|
||||||
initialDirectory: '$home/.claude/projects',
|
|
||||||
);
|
);
|
||||||
if (result != null && result.files.single.path != null) {
|
if (result != null && result.files.single.path != null) {
|
||||||
await _loadFile(result.files.single.path!);
|
await _loadFile(result.files.single.path!);
|
||||||
}
|
}
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint('[Load] Error: $e');
|
||||||
|
if (mounted) {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(
|
||||||
|
content: Text('Could not open file picker: $e'),
|
||||||
|
backgroundColor: AppColors.error,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _loadFile(String path) async {
|
Future<void> _loadFile(String path) async {
|
||||||
@ -491,11 +514,13 @@ class _HomeScreenState extends State<HomeScreen> {
|
|||||||
// ─── Header with breadcrumb ──────────────────────────────
|
// ─── Header with breadcrumb ──────────────────────────────
|
||||||
|
|
||||||
Widget _buildHeader(SessionProvider provider) {
|
Widget _buildHeader(SessionProvider provider) {
|
||||||
return Row(
|
return Column(
|
||||||
|
children: [
|
||||||
|
// Row 1: breadcrumb + count
|
||||||
|
Row(
|
||||||
children: [
|
children: [
|
||||||
const Icon(Icons.terminal, size: 24, color: AppColors.assistant),
|
const Icon(Icons.terminal, size: 24, color: AppColors.assistant),
|
||||||
const SizedBox(width: 10),
|
const SizedBox(width: 10),
|
||||||
// Breadcrumb
|
|
||||||
InkWell(
|
InkWell(
|
||||||
onTap: () => setState(() => _selectedProject = null),
|
onTap: () => setState(() => _selectedProject = null),
|
||||||
borderRadius: BorderRadius.circular(4),
|
borderRadius: BorderRadius.circular(4),
|
||||||
@ -538,9 +563,16 @@ class _HomeScreenState extends State<HomeScreen> {
|
|||||||
: '${_projects.length} projects',
|
: '${_projects.length} projects',
|
||||||
style: const TextStyle(fontSize: 12, color: AppColors.textMuted),
|
style: const TextStyle(fontSize: 12, color: AppColors.textMuted),
|
||||||
),
|
),
|
||||||
// Path input field
|
],
|
||||||
SizedBox(
|
),
|
||||||
width: 320,
|
const SizedBox(height: 12),
|
||||||
|
// Row 2: path input + Load + Browse
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
// Path input field — flexible to fill available space
|
||||||
|
Expanded(
|
||||||
|
child: SizedBox(
|
||||||
|
height: 36,
|
||||||
child: TextField(
|
child: TextField(
|
||||||
controller: _pathController,
|
controller: _pathController,
|
||||||
focusNode: _pathFocusNode,
|
focusNode: _pathFocusNode,
|
||||||
@ -585,15 +617,20 @@ class _HomeScreenState extends State<HomeScreen> {
|
|||||||
onSubmitted: (_) => _scanFromPathInput(),
|
onSubmitted: (_) => _scanFromPathInput(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
ElevatedButton.icon(
|
SizedBox(
|
||||||
|
height: 36,
|
||||||
|
child: ElevatedButton.icon(
|
||||||
onPressed: _loadSingleFile,
|
onPressed: _loadSingleFile,
|
||||||
icon: const Icon(Icons.insert_drive_file_outlined, size: 14),
|
icon: const Icon(Icons.insert_drive_file_outlined, size: 14),
|
||||||
label: const Text('Load'),
|
label: const Text('Load'),
|
||||||
style: ElevatedButton.styleFrom(
|
style: ElevatedButton.styleFrom(
|
||||||
backgroundColor: AppColors.surfaceLight,
|
backgroundColor: AppColors.surfaceLight,
|
||||||
foregroundColor: AppColors.textPrimary,
|
foregroundColor: AppColors.textPrimary,
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 8),
|
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 0),
|
||||||
|
minimumSize: Size.zero,
|
||||||
|
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.circular(6),
|
borderRadius: BorderRadius.circular(6),
|
||||||
side: const BorderSide(color: AppColors.surfaceBorder),
|
side: const BorderSide(color: AppColors.surfaceBorder),
|
||||||
@ -601,15 +638,20 @@ class _HomeScreenState extends State<HomeScreen> {
|
|||||||
textStyle: const TextStyle(fontSize: 12),
|
textStyle: const TextStyle(fontSize: 12),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
ElevatedButton.icon(
|
SizedBox(
|
||||||
|
height: 36,
|
||||||
|
child: ElevatedButton.icon(
|
||||||
onPressed: _pickFolder,
|
onPressed: _pickFolder,
|
||||||
icon: const Icon(Icons.folder_open, size: 14),
|
icon: const Icon(Icons.folder_open, size: 14),
|
||||||
label: const Text('Browse'),
|
label: const Text('Browse'),
|
||||||
style: ElevatedButton.styleFrom(
|
style: ElevatedButton.styleFrom(
|
||||||
backgroundColor: AppColors.surfaceLight,
|
backgroundColor: AppColors.surfaceLight,
|
||||||
foregroundColor: AppColors.textPrimary,
|
foregroundColor: AppColors.textPrimary,
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 8),
|
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 0),
|
||||||
|
minimumSize: Size.zero,
|
||||||
|
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.circular(6),
|
borderRadius: BorderRadius.circular(6),
|
||||||
side: const BorderSide(color: AppColors.surfaceBorder),
|
side: const BorderSide(color: AppColors.surfaceBorder),
|
||||||
@ -617,6 +659,9 @@ class _HomeScreenState extends State<HomeScreen> {
|
|||||||
textStyle: const TextStyle(fontSize: 12),
|
textStyle: const TextStyle(fontSize: 12),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user