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

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),

View File

@ -724,6 +724,7 @@
CODE_SIGN_STYLE = Manual;
"DEVELOPMENT_TEAM[sdk=macosx*]" = LD76P8L42W;
ENABLE_HARDENED_RUNTIME = YES;
CODE_SIGN_ALLOW_ENTITLEMENTS_MODIFICATION = YES;
OTHER_CODE_SIGN_FLAGS = "--timestamp --options runtime";
COMBINE_HIDPI_IMAGES = YES;
INFOPLIST_FILE = Runner/Info.plist;

View File

@ -2,11 +2,7 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.files.user-selected.read-only</key>
<true/>
<key>com.apple.security.files.home-directory.read-only</key>
<true/>
<key>com.apple.security.get-task-allow</key>
<false/>
</dict>
</plist>