feat(desktop): open a local file with a chosen application - #32
Merged
Conversation
Right-clicking a local file now offers "Open with…". It opens a dialog with the system default preselected and the applications registered for that file type listed under it, so the ordinary case is Enter and picking a different one is there when you want it. The handlers come from `gio`, which is what the desktop itself consults: `gio info` for the content type, `gio mime TYPE` for the default and the registered set, and the entry's own Exec line via `gio launch` rather than us reconstructing its argument syntax. With no handler chosen it is `shell.openPath`, the plain double-click. Two details in the parsing that are wrong in the obvious implementation, both covered by tests against real /usr/share/applications entries: - A .desktop file carries localised keys. `Name[zh_TW]` comes after `Name`, so matching `Name` anywhere in the file names btop "系統監視器". - It also carries action groups (`[Desktop Action new-window]`) with their own `Name=`. Only the `[Desktop Entry]` group is read. `NoDisplay` and `Hidden` entries are skipped, because those are plumbing the desktop keeps out of menus and this is a menu. A `Terminal=true` application says "Runs in a terminal", since launching one from a file manager usually flashes and exits, and a choice that appears to do nothing is worse than one that is labelled. Scope, and why: the item is disabled for a pane pointed at a server (the path names a file on that machine, and there is nothing here to hand to a local application) and for directories. `handlerId` is validated as a bare `*.desktop` filename in the main process and resolved only against the XDG application directories, because a renderer that could pass a path there would be choosing which program runs. The list is a bounded flex column with the ScrollArea as `min-h-0 flex-1`, not `max-h` on the ScrollArea root. The root form does not clip: the viewport is `size-full`, so with no definite height it grows to fit and the last rows are cut off by the dialog with no way to scroll to them. That is the same trap fixed in the preview dialog earlier today, found the same way, by looking at it. Verified in headless Chromium under the app's real CSP, both themes: the menu item appears on a local file and is disabled on a directory, the dialog lists the handlers with the default marked, Open with nothing chosen sends a null handler, and choosing Visual Studio Code sends code.desktop. Handler enumeration itself could not be exercised end to end here: this box has gio but no registered desktop associations, so `gio mime` returns nothing for every type. The parsers are tested against real gio output and real desktop files; the live lookup is not. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VScug5VRbcTuhiAoieeQ52
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Right-clicking a local file now offers Open with…. The dialog opens with the system default preselected and the applications registered for that file type listed under it, so the ordinary case is just Enter and picking a different one is there when you want it.
How it finds them
From
gio, which is what the desktop itself consults:gio infofor the content type,gio mime TYPEfor the default and the registered set, and the entry's own Exec line viagio launchrather than reconstructing its argument syntax. With no handler chosen it isshell.openPath— the plain double-click.Two parsing details that are wrong in the obvious implementation
Both covered by tests against real
/usr/share/applicationsentries:Name[zh_TW]comes afterNamein the file, so matchingNameanywhere names btop "系統監視器".[Desktop Action new-window]with its ownName=. Only the[Desktop Entry]group is read.NoDisplayandHiddenentries are skipped — that is plumbing the desktop keeps out of menus, and this is a menu. ATerminal=trueapplication says "Runs in a terminal", because launching one from a file manager usually flashes and exits, and a choice that appears to do nothing is worse than one that is labelled.Scope and safety
The item is disabled for a pane pointed at a server (that path names a file on that machine, so there is nothing here to hand to a local application) and for directories.
handlerIdis validated as a bare*.desktopfilename in the main process and resolved only against the XDG application directories — a renderer that could pass a path there would be choosing which program runs.One more instance of a trap from earlier today
The list is a bounded flex column with the ScrollArea as
min-h-0 flex-1, notmax-hon the ScrollArea root. The root form does not clip: the viewport issize-full, so with no definite height it grows to fit and the last rows are cut off by the dialog with no way to scroll to them. Same trap as the mirror preview's delete list, found the same way — by looking at it.Verification
Headless Chromium under the app's real CSP, both themes: the menu item appears on a local file and is disabled on a directory, the dialog lists the handlers with the default marked, Open with nothing chosen sends a null handler, and choosing Visual Studio Code sends
code.desktop.pnpm test546 passing,pnpm -r typecheckclean,pnpm smoke:desktopall three guards ok.What is not verified: handler enumeration end to end. This box has
giobut no registered desktop associations, sogio mimereturns nothing for every type. The parsers are tested against real gio output and real desktop files; the live lookup on a machine with actual applications installed is not.🤖 Generated with Claude Code
https://claude.ai/code/session_01VScug5VRbcTuhiAoieeQ52