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:
@@ -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),
|
||||
|
||||
Reference in New Issue
Block a user