style: format frontend with prettier

Mechanical one-shot pass of pnpm format over src/, tests/, tools/, and
root configs. No functional changes; build and type-check verified.
This commit is contained in:
2026-07-04 20:23:32 +01:00
parent 32c6ae09d6
commit 827e1a8ecf
152 changed files with 9204 additions and 7445 deletions
+24 -20
View File
@@ -1,6 +1,6 @@
import { DirEntry } from "../../store";
import { Tooltip } from "../Tooltip";
import { CheckIcon, ChevronRightIcon } from "../icons";
import { DirEntry } from '../../store'
import { Tooltip } from '../Tooltip'
import { CheckIcon, ChevronRightIcon } from '../icons'
export function FolderRow({
entry,
@@ -9,29 +9,29 @@ export function FolderRow({
onToggle,
onNavigate,
}: {
entry: DirEntry;
selected: boolean;
alreadyAdded: boolean;
onToggle: () => void;
onNavigate: () => void;
entry: DirEntry
selected: boolean
alreadyAdded: boolean
onToggle: () => void
onNavigate: () => void
}) {
return (
<div
className={`group flex h-11 items-center gap-3 rounded-md border px-3 transition-colors ${
alreadyAdded
? "border-transparent bg-white/[0.025] text-gray-600 light-theme:bg-gray-900 light-theme:text-gray-500"
? 'light-theme:bg-gray-900 light-theme:text-gray-500 border-transparent bg-white/[0.025] text-gray-600'
: selected
? "border-white/15 bg-white/[0.085] text-white shadow-[inset_0_0_0_1px_rgb(255_255_255_/_0.035)] light-theme:border-gray-700/45 light-theme:bg-gray-800/55 light-theme:text-white"
: "border-transparent text-gray-300 hover:bg-white/[0.055] hover:text-white light-theme:text-gray-500 light-theme:hover:bg-gray-900 light-theme:hover:text-white"
? 'light-theme:border-gray-700/45 light-theme:bg-gray-800/55 light-theme:text-white border-white/15 bg-white/[0.085] text-white shadow-[inset_0_0_0_1px_rgb(255_255_255_/_0.035)]'
: 'light-theme:text-gray-500 light-theme:hover:bg-gray-900 light-theme:hover:text-white border-transparent text-gray-300 hover:bg-white/[0.055] hover:text-white'
}`}
>
<button
type="button"
className={`grid h-5 w-5 shrink-0 place-items-center rounded border transition-colors ${
selected
? "border-white/30 bg-gray-200 text-gray-950 light-theme:border-gray-700/55 light-theme:bg-gray-700 light-theme:text-white"
: "border-white/15 bg-white/[0.035] text-transparent hover:border-white/30 light-theme:border-gray-700/50 light-theme:bg-gray-950"
} ${alreadyAdded ? "cursor-not-allowed opacity-40" : ""}`}
? 'light-theme:border-gray-700/55 light-theme:bg-gray-700 light-theme:text-white border-white/30 bg-gray-200 text-gray-950'
: 'light-theme:border-gray-700/50 light-theme:bg-gray-950 border-white/15 bg-white/[0.035] text-transparent hover:border-white/30'
} ${alreadyAdded ? 'cursor-not-allowed opacity-40' : ''}`}
onClick={onToggle}
disabled={alreadyAdded}
aria-label={selected ? `Remove ${entry.name} from folders to add` : `Choose ${entry.name}`}
@@ -45,7 +45,11 @@ export function FolderRow({
className="flex w-full min-w-0 items-center gap-2 text-left"
onClick={onNavigate}
>
<svg className="h-4 w-4 shrink-0 text-amber-300/90" fill="currentColor" viewBox="0 0 24 24">
<svg
className="h-4 w-4 shrink-0 text-amber-300/90"
fill="currentColor"
viewBox="0 0 24 24"
>
<path d="M3 6.5A2.5 2.5 0 015.5 4h4.1l2 2H18.5A2.5 2.5 0 0121 8.5v9A2.5 2.5 0 0118.5 20h-13A2.5 2.5 0 013 17.5v-11z" />
</svg>
<span className="truncate text-sm">{entry.name}</span>
@@ -53,20 +57,20 @@ export function FolderRow({
</Tooltip>
{alreadyAdded ? (
<span className="rounded-md border border-white/10 bg-white/[0.04] px-2 py-0.5 text-[11px] text-gray-500 light-theme:border-gray-700/40 light-theme:bg-gray-950">
<span className="light-theme:border-gray-700/40 light-theme:bg-gray-950 rounded-md border border-white/10 bg-white/[0.04] px-2 py-0.5 text-[11px] text-gray-500">
Added
</span>
) : null}
<Tooltip label={entry.has_children ? "Open folder" : "No subfolders"} anchorToCursor>
<Tooltip label={entry.has_children ? 'Open folder' : 'No subfolders'} anchorToCursor>
<button
type="button"
className="rounded-md p-1 text-gray-500 transition-colors hover:bg-white/[0.06] hover:text-white light-theme:hover:bg-gray-900 light-theme:hover:text-white"
className="light-theme:hover:bg-gray-900 light-theme:hover:text-white rounded-md p-1 text-gray-500 transition-colors hover:bg-white/[0.06] hover:text-white"
onClick={onNavigate}
>
<ChevronRightIcon className={`h-4 w-4 ${entry.has_children ? "" : "opacity-45"}`} />
<ChevronRightIcon className={`h-4 w-4 ${entry.has_children ? '' : 'opacity-45'}`} />
</button>
</Tooltip>
</div>
);
)
}
@@ -1,26 +1,26 @@
import { Tooltip } from "../Tooltip";
import { CloseIcon } from "../icons";
import { folderName } from "./pathUtils";
import { Tooltip } from '../Tooltip'
import { CloseIcon } from '../icons'
import { folderName } from './pathUtils'
export function StagedFoldersPanel({
stagedPaths,
onRemove,
onClear,
}: {
stagedPaths: string[];
onRemove: (path: string) => void;
onClear: () => void;
stagedPaths: string[]
onRemove: (path: string) => void
onClear: () => void
}) {
return (
<aside className="flex min-h-0 w-full flex-col border-t border-white/[0.07] bg-white/[0.018] light-theme:border-gray-300/70 light-theme:bg-gray-900/35 lg:w-80 lg:border-l lg:border-t-0">
<div className="flex items-center justify-between gap-3 border-b border-white/[0.07] px-5 py-4 light-theme:border-gray-300/70">
<p className="text-[11px] font-semibold uppercase tracking-[0.16em] text-gray-500">
<aside className="light-theme:border-gray-300/70 light-theme:bg-gray-900/35 flex min-h-0 w-full flex-col border-t border-white/[0.07] bg-white/[0.018] lg:w-80 lg:border-t-0 lg:border-l">
<div className="light-theme:border-gray-300/70 flex items-center justify-between gap-3 border-b border-white/[0.07] px-5 py-4">
<p className="text-[11px] font-semibold tracking-[0.16em] text-gray-500 uppercase">
Folders to add ({stagedPaths.length})
</p>
{stagedPaths.length > 0 ? (
<button
type="button"
className="rounded px-1.5 py-0.5 text-xs text-gray-400 transition-colors hover:bg-white/[0.06] hover:text-white light-theme:text-gray-500 light-theme:hover:bg-gray-800 light-theme:hover:text-white"
className="light-theme:text-gray-500 light-theme:hover:bg-gray-800 light-theme:hover:text-white rounded px-1.5 py-0.5 text-xs text-gray-400 transition-colors hover:bg-white/[0.06] hover:text-white"
onClick={onClear}
>
Clear all
@@ -30,9 +30,9 @@ export function StagedFoldersPanel({
<div className="min-h-0 flex-1 overflow-y-auto p-3">
{stagedPaths.length === 0 ? (
<div className="flex h-full min-h-40 flex-col items-center justify-center rounded-md border border-dashed border-white/[0.08] px-5 text-center light-theme:border-gray-700/35">
<div className="light-theme:border-gray-700/35 flex h-full min-h-40 flex-col items-center justify-center rounded-md border border-dashed border-white/[0.08] px-5 text-center">
<p className="text-sm text-gray-500">No folders selected.</p>
<p className="mt-1 max-w-52 text-xs leading-relaxed text-gray-600 light-theme:text-gray-500">
<p className="light-theme:text-gray-500 mt-1 max-w-52 text-xs leading-relaxed text-gray-600">
Choose folders on the left and they will collect here.
</p>
</div>
@@ -40,18 +40,24 @@ export function StagedFoldersPanel({
<div className="space-y-2">
{stagedPaths.map((path) => (
<Tooltip key={path} label={path} anchorToCursor block>
<div className="group flex min-h-12 items-center gap-2 rounded-md border border-white/[0.07] bg-white/[0.045] px-3 py-2 text-gray-200 transition-colors hover:border-white/15 hover:bg-white/[0.07] light-theme:border-gray-700/40 light-theme:bg-gray-900 light-theme:text-white light-theme:hover:bg-gray-800">
<svg className="h-4 w-4 shrink-0 text-amber-300/90" fill="currentColor" viewBox="0 0 24 24">
<div className="group light-theme:border-gray-700/40 light-theme:bg-gray-900 light-theme:text-white light-theme:hover:bg-gray-800 flex min-h-12 items-center gap-2 rounded-md border border-white/[0.07] bg-white/[0.045] px-3 py-2 text-gray-200 transition-colors hover:border-white/15 hover:bg-white/[0.07]">
<svg
className="h-4 w-4 shrink-0 text-amber-300/90"
fill="currentColor"
viewBox="0 0 24 24"
>
<path d="M3 6.5A2.5 2.5 0 015.5 4h4.1l2 2H18.5A2.5 2.5 0 0121 8.5v9A2.5 2.5 0 0118.5 20h-13A2.5 2.5 0 013 17.5v-11z" />
</svg>
<div className="min-w-0 flex-1">
<p className="truncate text-sm font-medium">{folderName(path)}</p>
<p className="mt-0.5 truncate text-[11px] text-gray-600 light-theme:text-gray-500">{path}</p>
<p className="light-theme:text-gray-500 mt-0.5 truncate text-[11px] text-gray-600">
{path}
</p>
</div>
<Tooltip label="Remove from folders to add" anchorToCursor>
<button
type="button"
className="rounded-md p-1 text-gray-500 opacity-80 transition-colors hover:bg-white/[0.08] hover:text-white group-hover:opacity-100 light-theme:hover:bg-gray-700"
className="light-theme:hover:bg-gray-700 rounded-md p-1 text-gray-500 opacity-80 transition-colors group-hover:opacity-100 hover:bg-white/[0.08] hover:text-white"
onClick={() => onRemove(path)}
aria-label={`Remove ${path} from folders to add`}
>
@@ -65,5 +71,5 @@ export function StagedFoldersPanel({
)}
</div>
</aside>
);
)
}
+7 -7
View File
@@ -1,13 +1,13 @@
import { FolderAddResult } from "../../store";
import { FolderAddResult } from '../../store'
export function StatusLine({ results }: { results: FolderAddResult[] | null }) {
if (!results) return null;
const added = results.filter((result) => result.status === "added").length;
const skipped = results.filter((result) => result.status === "skipped").length;
const failed = results.filter((result) => result.status === "error").length;
if (!results) return null
const added = results.filter((result) => result.status === 'added').length
const skipped = results.filter((result) => result.status === 'skipped').length
const failed = results.filter((result) => result.status === 'error').length
return (
<p className="text-xs text-gray-500 light-theme:text-gray-600">
<p className="light-theme:text-gray-600 text-xs text-gray-500">
Added {added}, skipped {skipped}, failed {failed}.
</p>
);
)
}
+35 -32
View File
@@ -1,66 +1,69 @@
export interface Breadcrumb {
label: string;
path: string | null;
label: string
path: string | null
}
export function normalizePath(path: string): string {
return path.replace(/\\/g, "/").replace(/\/+$/, "").toLowerCase();
return path.replace(/\\/g, '/').replace(/\/+$/, '').toLowerCase()
}
export function cleanAddressInput(path: string): string {
const trimmed = path.trim();
const trimmed = path.trim()
if (trimmed.length >= 2) {
const first = trimmed[0];
const last = trimmed[trimmed.length - 1];
if ((first === "\"" && last === "\"") || (first === "'" && last === "'")) {
return trimmed.slice(1, -1).trim();
const first = trimmed[0]
const last = trimmed[trimmed.length - 1]
if ((first === '"' && last === '"') || (first === "'" && last === "'")) {
return trimmed.slice(1, -1).trim()
}
}
return trimmed;
return trimmed
}
export function friendlyDirectoryError(error: unknown): string {
const message = error instanceof Error ? error.message : String(error);
const message = error instanceof Error ? error.message : String(error)
if (/cannot find the path|os error 3|not found|no such file/i.test(message)) {
return "Folder not found. Check the path and try again.";
return 'Folder not found. Check the path and try again.'
}
return message;
return message
}
export function folderName(path: string): string {
const trimmed = path.replace(/[\\/]+$/, "");
const parts = trimmed.split(/[\\/]+/).filter(Boolean);
return parts.length > 0 ? parts[parts.length - 1] : path;
const trimmed = path.replace(/[\\/]+$/, '')
const parts = trimmed.split(/[\\/]+/).filter(Boolean)
return parts.length > 0 ? parts[parts.length - 1] : path
}
export function buildBreadcrumbs(path: string | null): Breadcrumb[] {
if (!path) return [{ label: "This PC / Home", path: null }];
if (!path) return [{ label: 'This PC / Home', path: null }]
const separator = path.includes("\\") ? "\\" : "/";
const normalized = path.replace(/[\\/]+$/, "");
const windowsDrive = normalized.match(/^[A-Za-z]:/);
const separator = path.includes('\\') ? '\\' : '/'
const normalized = path.replace(/[\\/]+$/, '')
const windowsDrive = normalized.match(/^[A-Za-z]:/)
if (windowsDrive) {
const drive = windowsDrive[0];
const rest = normalized.slice(2).split(/[\\/]+/).filter(Boolean);
let current = drive;
const drive = windowsDrive[0]
const rest = normalized
.slice(2)
.split(/[\\/]+/)
.filter(Boolean)
let current = drive
return [
{ label: "This PC", path: null },
{ label: 'This PC', path: null },
{ label: drive, path: drive },
...rest.map((part) => {
current = current.endsWith("\\") ? `${current}${part}` : `${current}\\${part}`;
return { label: part, path: current };
current = current.endsWith('\\') ? `${current}${part}` : `${current}\\${part}`
return { label: part, path: current }
}),
];
]
}
const parts = normalized.split(/[\\/]+/).filter(Boolean);
let current = separator === "/" ? "" : "";
const parts = normalized.split(/[\\/]+/).filter(Boolean)
let current = separator === '/' ? '' : ''
return [
{ label: "/", path: null },
{ label: '/', path: null },
...parts.map((part) => {
current = `${current}/${part}`;
return { label: part, path: current };
current = `${current}/${part}`
return { label: part, path: current }
}),
];
]
}
+127 -120
View File
@@ -1,183 +1,190 @@
import { useEffect, useMemo, useRef, useState } from "react";
import { DirListing, FolderAddResult, useGalleryStore } from "../../store";
import { useEffect, useMemo, useRef, useState } from 'react'
import { DirListing, FolderAddResult, useGalleryStore } from '../../store'
import {
buildBreadcrumbs,
cleanAddressInput,
friendlyDirectoryError,
normalizePath,
} from "./pathUtils";
} from './pathUtils'
export function useFolderPicker() {
const folderPickerOpen = useGalleryStore((state) => state.folderPickerOpen);
const setFolderPickerOpen = useGalleryStore((state) => state.setFolderPickerOpen);
const folders = useGalleryStore((state) => state.folders);
const listDirectories = useGalleryStore((state) => state.listDirectories);
const addFolders = useGalleryStore((state) => state.addFolders);
const folderPickerOpen = useGalleryStore((state) => state.folderPickerOpen)
const setFolderPickerOpen = useGalleryStore((state) => state.setFolderPickerOpen)
const folders = useGalleryStore((state) => state.folders)
const listDirectories = useGalleryStore((state) => state.listDirectories)
const addFolders = useGalleryStore((state) => state.addFolders)
const [listing, setListing] = useState<DirListing | null>(null);
const [currentPath, setCurrentPath] = useState<string | null>(null);
const [addressDraft, setAddressDraft] = useState("");
const [addressEditing, setAddressEditing] = useState(false);
const [stagedPaths, setStagedPaths] = useState<string[]>([]);
const [loading, setLoading] = useState(false);
const [adding, setAdding] = useState(false);
const [error, setError] = useState<string | null>(null);
const [results, setResults] = useState<FolderAddResult[] | null>(null);
const scrollRef = useRef<HTMLDivElement>(null);
const addressInputRef = useRef<HTMLInputElement>(null);
const [listing, setListing] = useState<DirListing | null>(null)
const [currentPath, setCurrentPath] = useState<string | null>(null)
const [addressDraft, setAddressDraft] = useState('')
const [addressEditing, setAddressEditing] = useState(false)
const [stagedPaths, setStagedPaths] = useState<string[]>([])
const [loading, setLoading] = useState(false)
const [adding, setAdding] = useState(false)
const [error, setError] = useState<string | null>(null)
const [results, setResults] = useState<FolderAddResult[] | null>(null)
const scrollRef = useRef<HTMLDivElement>(null)
const addressInputRef = useRef<HTMLInputElement>(null)
const libraryPaths = useMemo(() => new Set(folders.map((folder) => normalizePath(folder.path))), [folders]);
const stagedSet = useMemo(() => new Set(stagedPaths.map(normalizePath)), [stagedPaths]);
const breadcrumbs = useMemo(() => buildBreadcrumbs(listing?.current ?? null), [listing?.current]);
const libraryPaths = useMemo(
() => new Set(folders.map((folder) => normalizePath(folder.path))),
[folders]
)
const stagedSet = useMemo(() => new Set(stagedPaths.map(normalizePath)), [stagedPaths])
const breadcrumbs = useMemo(() => buildBreadcrumbs(listing?.current ?? null), [listing?.current])
useEffect(() => {
if (!folderPickerOpen) return;
let cancelled = false;
setLoading(true);
setError(null);
if (!folderPickerOpen) return
let cancelled = false
setLoading(true)
setError(null)
void listDirectories(currentPath)
.then((nextListing) => {
if (cancelled) return;
setListing(nextListing);
setAddressDraft(nextListing.current ?? "");
setAddressEditing(false);
scrollRef.current?.scrollTo({ top: 0, left: 0 });
if (cancelled) return
setListing(nextListing)
setAddressDraft(nextListing.current ?? '')
setAddressEditing(false)
scrollRef.current?.scrollTo({ top: 0, left: 0 })
})
.catch((loadError) => {
if (cancelled) return;
setListing({ current: currentPath, parent: null, entries: [] });
setError(friendlyDirectoryError(loadError));
if (cancelled) return
setListing({ current: currentPath, parent: null, entries: [] })
setError(friendlyDirectoryError(loadError))
})
.finally(() => {
if (!cancelled) setLoading(false);
});
if (!cancelled) setLoading(false)
})
return () => {
cancelled = true;
};
}, [currentPath, folderPickerOpen, listDirectories]);
cancelled = true
}
}, [currentPath, folderPickerOpen, listDirectories])
useEffect(() => {
if (!folderPickerOpen) return;
if (!folderPickerOpen) return
const handleKeyDown = (event: KeyboardEvent) => {
if (event.key === "Escape") {
if (event.key === 'Escape') {
if (addressEditing) {
setAddressDraft(listing?.current ?? "");
setAddressEditing(false);
return;
setAddressDraft(listing?.current ?? '')
setAddressEditing(false)
return
}
setFolderPickerOpen(false);
setFolderPickerOpen(false)
}
};
window.addEventListener("keydown", handleKeyDown);
return () => window.removeEventListener("keydown", handleKeyDown);
}, [addressEditing, folderPickerOpen, listing?.current, setFolderPickerOpen]);
}
window.addEventListener('keydown', handleKeyDown)
return () => window.removeEventListener('keydown', handleKeyDown)
}, [addressEditing, folderPickerOpen, listing?.current, setFolderPickerOpen])
useEffect(() => {
if (!addressEditing) return;
if (!addressEditing) return
requestAnimationFrame(() => {
addressInputRef.current?.focus();
addressInputRef.current?.select();
});
}, [addressEditing]);
addressInputRef.current?.focus()
addressInputRef.current?.select()
})
}, [addressEditing])
useEffect(() => {
if (folderPickerOpen) return;
setCurrentPath(null);
setAddressDraft("");
setAddressEditing(false);
setListing(null);
setStagedPaths([]);
setError(null);
setResults(null);
setAdding(false);
}, [folderPickerOpen]);
if (folderPickerOpen) return
setCurrentPath(null)
setAddressDraft('')
setAddressEditing(false)
setListing(null)
setStagedPaths([])
setError(null)
setResults(null)
setAdding(false)
}, [folderPickerOpen])
const entries = listing?.entries ?? [];
const addressPath = cleanAddressInput(addressEditing ? addressDraft : (listing?.current ?? ""));
const normalizedAddressPath = addressPath ? normalizePath(addressPath) : "";
const addressAlreadyAdded = normalizedAddressPath ? libraryPaths.has(normalizedAddressPath) : false;
const addressAlreadyStaged = normalizedAddressPath ? stagedSet.has(normalizedAddressPath) : false;
const entries = listing?.entries ?? []
const addressPath = cleanAddressInput(addressEditing ? addressDraft : (listing?.current ?? ''))
const normalizedAddressPath = addressPath ? normalizePath(addressPath) : ''
const addressAlreadyAdded = normalizedAddressPath
? libraryPaths.has(normalizedAddressPath)
: false
const addressAlreadyStaged = normalizedAddressPath ? stagedSet.has(normalizedAddressPath) : false
const togglePath = (path: string) => {
const normalized = normalizePath(path);
if (libraryPaths.has(normalized)) return;
setResults(null);
const normalized = normalizePath(path)
if (libraryPaths.has(normalized)) return
setResults(null)
setStagedPaths((current) => {
const exists = current.some((staged) => normalizePath(staged) === normalized);
return exists ? current.filter((staged) => normalizePath(staged) !== normalized) : [...current, path];
});
};
const exists = current.some((staged) => normalizePath(staged) === normalized)
return exists
? current.filter((staged) => normalizePath(staged) !== normalized)
: [...current, path]
})
}
const stagePath = (path: string) => {
const cleaned = cleanAddressInput(path);
const cleaned = cleanAddressInput(path)
if (!cleaned) {
setError("Enter a folder path first.");
return;
setError('Enter a folder path first.')
return
}
const normalized = normalizePath(cleaned);
const normalized = normalizePath(cleaned)
if (libraryPaths.has(normalized)) {
setError("That folder is already in your library.");
return;
setError('That folder is already in your library.')
return
}
if (stagedSet.has(normalized)) {
setError("That folder is already selected.");
return;
setError('That folder is already selected.')
return
}
setError(null);
setResults(null);
setStagedPaths((current) => [...current, cleaned]);
};
setError(null)
setResults(null)
setStagedPaths((current) => [...current, cleaned])
}
const navigateToAddress = () => {
const cleaned = cleanAddressInput(addressDraft);
setResults(null);
setError(null);
setCurrentPath(cleaned || null);
};
const cleaned = cleanAddressInput(addressDraft)
setResults(null)
setError(null)
setCurrentPath(cleaned || null)
}
const updateAddressDraft = (nextDraft: string) => {
setAddressDraft(nextDraft);
setResults(null);
};
setAddressDraft(nextDraft)
setResults(null)
}
const beginAddressEdit = () => {
setAddressDraft(listing?.current ?? "");
setAddressEditing(true);
};
setAddressDraft(listing?.current ?? '')
setAddressEditing(true)
}
const removeStagedPath = (path: string) => {
const normalized = normalizePath(path);
setResults(null);
setStagedPaths((current) => current.filter((staged) => normalizePath(staged) !== normalized));
};
const normalized = normalizePath(path)
setResults(null)
setStagedPaths((current) => current.filter((staged) => normalizePath(staged) !== normalized))
}
const clearStagedPaths = () => {
setResults(null);
setStagedPaths([]);
};
setResults(null)
setStagedPaths([])
}
const confirmAdd = async () => {
if (stagedPaths.length === 0 || adding) return;
setAdding(true);
setError(null);
if (stagedPaths.length === 0 || adding) return
setAdding(true)
setError(null)
try {
const addResults = await addFolders(stagedPaths);
const failed = addResults.filter((result) => result.status === "error");
setResults(addResults);
const addResults = await addFolders(stagedPaths)
const failed = addResults.filter((result) => result.status === 'error')
setResults(addResults)
if (failed.length > 0) {
setStagedPaths(stagedPaths.filter((_, i) => addResults[i]?.status === "error"));
setError(failed.map((failure) => failure.data).join("; "));
return;
setStagedPaths(stagedPaths.filter((_, i) => addResults[i]?.status === 'error'))
setError(failed.map((failure) => failure.data).join('; '))
return
}
setFolderPickerOpen(false);
setFolderPickerOpen(false)
} catch (addError) {
setError(addError instanceof Error ? addError.message : String(addError));
setError(addError instanceof Error ? addError.message : String(addError))
} finally {
setAdding(false);
setAdding(false)
}
};
}
return {
adding,
@@ -208,5 +215,5 @@ export function useFolderPicker() {
stagedSet,
togglePath,
updateAddressDraft,
};
}
}