fix: disable sandbox for ~/.claude access, add path input field for hidden folders

- Remove App Sandbox from Release entitlements (developer tool needs
  filesystem access to ~/.claude/projects — same as VS Code, iTerm2)
- Explicitly set get-task-allow=false in entitlements for notarization
- Add path input field in header so users can type paths with hidden
  folders (e.g. ~/.claude/projects) — press Enter or click arrow to scan
- Field pre-populated with ~/.claude/projects on launch
- Signed, notarized, stapled DMG
This commit is contained in:
Mathias Beaulieu-Duncan
2026-04-07 13:55:24 -04:00
parent 5c693bf3d8
commit 53ed5a6cd1
3 changed files with 91 additions and 7 deletions
+88 -1
View File
@@ -101,13 +101,24 @@ class _HomeScreenState extends State<HomeScreen> {
String _searchQuery = '';
_Project? _selectedProject;
_Project? _customFolderProject;
final _pathController = TextEditingController();
final _pathFocusNode = FocusNode();
@override
void initState() {
super.initState();
final home = _getRealHome() ?? '';
_pathController.text = '$home/.claude/projects';
_scanProjects();
}
@override
void dispose() {
_pathController.dispose();
_pathFocusNode.dispose();
super.dispose();
}
/// Returns the real user home directory, even inside App Sandbox.
/// In sandbox, HOME points to ~/Library/Containers/<bundleid>/Data.
String? _getRealHome() {
@@ -291,6 +302,35 @@ class _HomeScreenState extends State<HomeScreen> {
}
}
/// Resolve a path string, expanding ~ to the home directory.
String _resolvePath(String input) {
final home = _getRealHome() ?? Platform.environment['HOME'] ?? '';
var path = input.trim();
if (path.startsWith('~/')) {
path = '$home${path.substring(1)}';
} else if (path == '~') {
path = home;
}
return path;
}
Future<void> _scanFromPathInput() async {
final resolved = _resolvePath(_pathController.text);
final dir = Directory(resolved);
if (await dir.exists()) {
await _scanFolder(resolved);
} else {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Folder not found: $resolved'),
backgroundColor: AppColors.error,
),
);
}
}
}
Future<void> _pickFolder() async {
final result = await FilePicker.platform.getDirectoryPath(
dialogTitle: 'Select a folder containing Claude session files',
@@ -498,7 +538,54 @@ class _HomeScreenState extends State<HomeScreen> {
: '${_projects.length} projects',
style: const TextStyle(fontSize: 12, color: AppColors.textMuted),
),
const SizedBox(width: 12),
// Path input field
SizedBox(
width: 320,
child: TextField(
controller: _pathController,
focusNode: _pathFocusNode,
style: const TextStyle(
fontSize: 12,
color: AppColors.textPrimary,
fontFamily: 'JetBrains Mono',
),
decoration: InputDecoration(
hintText: '~/ .claude/projects',
hintStyle: const TextStyle(
fontSize: 12,
color: AppColors.textMuted,
fontFamily: 'JetBrains Mono',
),
isDense: true,
contentPadding: const EdgeInsets.symmetric(
horizontal: 10,
vertical: 8,
),
filled: true,
fillColor: AppColors.surface,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(6),
borderSide: const BorderSide(color: AppColors.surfaceBorder),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(6),
borderSide: const BorderSide(color: AppColors.surfaceBorder),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(6),
borderSide: const BorderSide(color: AppColors.assistant),
),
suffixIcon: IconButton(
icon: const Icon(Icons.arrow_forward, size: 14,
color: AppColors.textMuted),
tooltip: 'Scan this folder',
onPressed: _scanFromPathInput,
),
),
onSubmitted: (_) => _scanFromPathInput(),
),
),
const SizedBox(width: 8),
ElevatedButton.icon(
onPressed: _loadSingleFile,
icon: const Icon(Icons.insert_drive_file_outlined, size: 14),