Files
Stirling-PDF/frontend/editor/src/desktop/services/directoryPicker.ts
T
Reece Browne 1b2a3118a6 Disk-mounted folders on desktop and improved folder management (#7502)
Description of Changes

Adds folder kinds so the file manager can work with real directories on
disk.

Desktop
- New folder is now a menu with two options: "Add local folder" and "New
folder on the server".
- Add local folder opens the native picker and mounts a directory. Files
are listed straight from disk, nothing is copied in.
- Subfolders show inside a mount and open like any folder. New folder
inside a mount creates a real directory on disk.
- Moving, dropping or uploading files into a mount writes them to the
directory. The app copy is only removed after the write succeeds. Name
clashes get a " (2)" suffix.
- Mounted files get thumbnails.
- Adding the same directory twice just returns the existing mount.
- Removing a mount never touches the disk.
- The server option is disabled in local mode with a sign in message.

Web + desktop
- Uploading or dropping files while inside a folder puts them in that
folder instead of Local.
- Files can be dragged onto folders in the grid and the tree to move
them.
- Folders show an origin badge (cloud or local).
- The Local view now means files that are not in any folder.

Follow ups for a future pr
- Mount listing cap: large directories currently show the 500 most
recent files with no notice. Will be removed as part of the
virtualisation/performance PR.
- Folders within folders need to be supported
- Symlinks in mounts: currently not listed. Behaviour to be decided
alongside the wider folder work.
2026-09-02 14:18:57 +00:00

28 lines
1.0 KiB
TypeScript

/**
* Desktop directory picking: the Tauri file dialog hands back a real path,
* which is the whole reason local folders are a desktop capability — a
* browser can only produce handles, never locations.
*/
import { isTauri } from "@tauri-apps/api/core";
import { open } from "@tauri-apps/plugin-dialog";
import type { PickedDirectory } from "@core/services/directoryPicker";
export type { PickedDirectory };
// The desktop bundle also runs as a plain web page in dev; only the actual
// Tauri webview can open the native dialog.
export const canPickDirectory = isTauri();
export async function pickDirectory(): Promise<PickedDirectory | null> {
if (!canPickDirectory) return null;
const picked = await open({ directory: true, multiple: false });
if (typeof picked !== "string" || picked.length === 0) return null;
// The path's last segment, tolerant of either separator and a trailing one.
const name =
picked
.replace(/[\\/]+$/, "")
.split(/[\\/]/)
.pop() || picked;
return { path: picked, name };
}