Result<T>, and the renderer checks result.ok rather than
wrapping calls in try/catch.
The Result type
EACCES, ENOENT, EEXIST, and friends — become friendly
toasts in the UI, never uncaught exceptions. A renderer call reads like this:
The flow of an operation
Every operation travels the same four hops:Preload method
Added to the
FsApi interface (src/shared/types.ts + preload/index.d.ts)
and exposed through the contextBridge in src/preload/index.ts.Handler
Registered in
src/main/fs/handlers.ts, wrapped in wrap() which converts
thrown errors via toAppError(). Path arguments are validated with
assertValidPath.Adding a new FS operation
The recipe is mechanical:- Add the channel to
src/shared/channels.ts. - Add the method to
FsApiinsrc/shared/types.ts. - Implement it in
service.ts— throw on failure. - Register a handler in
handlers.ts(validate path args withassertValidPath). - Expose it in
preload/index.ts.
Events, not just calls
Some flows push from main to renderer instead of returning a value. Those go over event channels (Events.*), for example:
Events.dirChanged— the watched directory changed.Events.indexProgress— indexing progress.Events.aiModelProgress/Events.llmModelProgress— model download progress.Events.chatStream— streamed assistant tokens and tool activity.Events.graphProgress— knowledge-graph build progress.
Delete = OS Trash only
Delete moves items to the real OS Trash / Recycle Bin viashell.trashItem
(Channels.trash). FilDOS keeps no trash of its own — no in-app Trash view,
no restore, no empty-trash, no undo-of-delete. Recover from Finder or Explorer
if needed.