import { ConfigService, FileProvider, NotificationsService } from 'tabby-core'; /** * What a downloaded copy is handed to: the configured editor, or the OS's own * "open with" dialog when there is none. */ export type Opener = { kind: 'editor'; path: string; } /** * `learn` is set only when this dialog stood in for a missing editor, never * when the user asked for it explicitly from the context menu: that entry * is a one-off escape hatch and must not redefine what double-click does. */ | { kind: 'openWith'; learn?: boolean; }; /** * Where a remote file opens once it has been copied locally. * * The whole point of this service is that a double-click never hands a file to * the OS by association: an executable, or a script whose extension is bound to * an interpreter, would *run* instead of being edited. Everything that opens a * downloaded copy goes through `launchEditor()`, and the only way to reach the * OS association at all is the explicit "Ouvrir avec..." entry of the context * menu, which is a deliberate escape hatch and never a default. */ export declare class SidebarPlusEditorService { private config; private notifications; private fileProviders; constructor(config: ConfigService, notifications: NotificationsService, fileProviders: FileProvider[] | null); get editorPath(): string; setEditorPath(path: string): Promise; /** * How a double-clicked file should be opened. * * With an editor configured, that editor — no question asked. Without one, * Windows' own "Ouvrir avec" dialog, which is what the user asked for: it * lists installed applications instead of making them find an .exe in * Program Files. * * That dialog cannot be remembered, and this is not an oversight: * `OpenAs_RunDLL` opens the file with whatever was picked and returns * nothing — no Windows API hands the chosen application back. So it comes * up on every double-click until an editor is set in the settings tab, * which is the one place that can record one. * * Elsewhere (no "Ouvrir avec" outside Windows) the file picker stands in, * and what it returns *is* recorded. Returns null when there is nothing to * open with — callers treat that as "open nothing", never as a licence to * fall back to the OS association. */ resolveOpener(): Promise; /** * The OS's own "choose an application" file dialog. * * Opened through `@electron/remote`, not through Tabby's own helpers, for * one reason: neither `FileProvider.selectAndStoreFile()` nor * `PlatformService.startUpload()` lets a caller set `defaultPath` or * `filters`, so both land wherever the OS last left the dialog — in * practice the Downloads folder, which is the last place an editor lives. * Here it opens on the platform's applications directory and filters to * runnable things. * * Returns null on cancel and on a dialog that could not be opened at all. */ pickEditorPath(): Promise; /** * Where the dialog opens. Not the last-used directory, which is the whole * point — an editor is never in Downloads. * * Windows deliberately starts in Program Files rather than the Start Menu: * the Start Menu holds `.lnk` shortcuts, and while `resolveShortcut()` * below handles one, a real executable is what actually gets spawned. */ private defaultEditorDirectory; private editorFilters; /** * A Windows `.lnk` is resolved to its target at pick time, not at launch * time: `spawn()` does not follow shortcuts (no shell is involved, which is * deliberate), and storing the real executable also means the settings tab * shows what will actually run. */ private resolveShortcut; /** * Used only when `@electron/remote` is unreachable: Tabby's `Filesystem` * FileProvider, which opens the same kind of native dialog but with no * control over where it starts. * * Not `FileProvidersService.selectAndStoreFile()`, which first asks *which* * storage to pick from as soon as more than one provider exists — Tabby * ships a second one, `VaultFileProvider`, whose result is a `vault://` * key, not a path this could spawn. The filesystem provider returns a * `file://`-prefixed path and throws on cancel. */ private pickEditorPathFallback; /** * Runs the configured editor on a local path. * * `detached` + `stdio: 'ignore'` + `unref()`: without them the editor stays * a child of Tabby, its pipes keep the parent's event loop referencing it, * and closing Tabby takes the editor down with it. Failures are reported * and stop there — never a fallback to the OS association, which is the * behaviour this whole path exists to avoid. */ launchEditor(editorPath: string, localPath: string): void; /** Windows only — the entry that offers this is gated on the platform. */ get canOpenWith(): boolean; /** * Windows' own "Open with..." dialog on a local path. * * `execFile`, not `exec`: no shell is involved, so a path containing a * space, an ampersand or a quote is passed as one argument instead of being * re-parsed by cmd. * * The callback fires when `rundll32` exits, which is when the dialog closes * and the chosen application has been started — the moment `learnFrom()` * needs to start looking. */ openWith(localPath: string, learn?: boolean): void; /** * Works out which application the "Open with" dialog started, and keeps it. * * Windows offers no way to ask: `OpenAs_RunDLL` returns nothing, and the * registry trail it leaves (`FileExts\<.ext>\OpenWithList`) is per * extension — useless for the files this actually opens, which are remote * config files usually named without one. What *is* reliable is that the * application was handed our temp path on its command line, so the process * list identifies it whatever its extension. * * Deliberately a single PowerShell process that does its own polling rather * than one spawn per attempt: an editor can take a few seconds to appear, * and repeatedly paying PowerShell's startup would cost more than the whole * lookup. Silent when nothing is found — an unmemorised editor is a dialog * next time, not an error. */ private learnFrom; /** * The program out of a Win32 command line. * * The quoted first token is preferred over `ExecutablePath` on purpose: for * a Store application the latter points inside `WindowsApps`, whose ACLs * usually refuse a direct spawn, while the command line carries the * launcher that actually works (measured on Notepad, which reports * `WindowsApps\...\Notepad.exe` but is launched as * `C:\WINDOWS\system32\notepad.exe`). */ private executableFromCommandLine; }