Open local HTML files instead of erroring - #1
Open
gamellis wants to merge 3 commits into
Open
Conversation
Finicky is the default handler for public.html, so `open page.html` hands the *file* — not a URL — to the default browser, which is ChromeTabFocus. The applet claimed only http/https URL schemes and had no `on open` handler, so LaunchServices refused: "ChromeTabFocus cannot open files in the 'HTML text' format." Every local page was affected; web links were unaffected, which is why this went unnoticed. The applet now accepts files, converts each to a percent-encoded file:// URL (matching Chrome's own encoding so tab reuse works for local pages too), and routes it through the same focus-or-open path. The installer claims public.html/public.xhtml at rank Alternate — enough to accept the handoff, not enough to become the app Finder shows for .html files. verify.sh flags an applet built before this, since the symptom is specific and easy to misread as a Finicky problem. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
gamellis
marked this pull request as ready for review
August 8, 2026 20:52
The encoder escaped every character outside the unreserved set. Chrome
does not: it keeps : @ ! , = + $ & ' ( ) * [ ] literal and escapes only
space, non-ASCII, and # ; ? % { }. So a file already open in a tab that
Chrome itself created -- from Finder, a link, a restored session -- never
matched, and opening it made a duplicate instead of focusing it. Verified
byte-exact against Chrome's own canonicalisation on 11 paths covering
every class of punctuation, spaces, CJK and an emoji.
It also spawned a shell per escaped character. A path of 100 non-ASCII
characters cost ~490ms, a realistic one ~50ms, and it made the README's
"nothing is passed to a shell" false. Foundation's percent-encoding is
one call: the same path now costs a fraction of a millisecond, and the
framework load is paid once at launch because the applet stays open.
NSURL's fileURLWithPath would have been the obvious choice and is wrong:
absoluteString uses the NFD filesystem representation, so "café" comes
back as cafe%CC%81 where Chrome reports caf%C3%A9.
Comparing file: URLs decoded is the belt to that braces -- the encoder
tracks a Chromium rule that could drift, and a decoded comparison still
matches if it does, including against tabs the previous version opened.
verify.sh grepped the whole plist as text for public.html, which passes
on an applet that merely mentions it in a type name or an inherited UTI
declaration. It now reads the keys LaunchServices actually reads, and
checks the compiled script has an open-documents handler at all -- a
plist claim with no handler leaves the event to time out, which looks
exactly like the click doing nothing. Tested against a good build, a
plist-claims-but-no-handler build, a pre-fix build and that UTI fooler.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The NFD/NFC match worked, but by accident. `POSIX path of` yields the decomposed filesystem form while a tab Chrome opened from Finder carries the composed form from disk, and what reconciled them was the NSString -> AppleScript text coercion quietly composing on the way out: the NSString is length 20, the coerced text 19. Nothing in the code said so, and the match had no business depending on a side effect of a type coercion. precomposedStringWithCanonicalMapping makes it the stated behaviour. No change in result today -- verified the two spellings still compare equal with `considering diacriticals and case`, which disables AppleScript's implicit normalization, so the composition is now ours rather than the runtime's. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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.
The bug
open page.htmlin a terminal (or double-clicking an HTML file in Finder) put up:Why
Finicky is the default handler for
public.html, so it receives the file open and hands the file — not a URL — to the default browser, ChromeTabFocus. The applet claimed only thehttp/httpsURL schemes and had noon openhandler, so LaunchServices refused the handoff. Web links kept working, which is why this went unnoticed.The fix
chrome-tab-focus.applescript:on open theFileshandler, plus afileURLForpercent-encoder. Chrome reports tab URLs percent-encoded, so the encoding has to match or every open would make a duplicate tab instead of focusing the existing one. Encoding goes throughodrather than AppleScript'sid of c, since one AppleScript character can be several UTF-8 bytes.install-chrome-tab-focus.sh: adds thefilescheme and aCFBundleDocumentTypesclaim forpublic.html/public.xhtmlat rank Alternate — enough to accept Finicky's handoff, not enough to become the app Finder shows for.htmlfiles.verify.sh: flags an applet built before this change. The symptom is specific and easy to misread as a Finicky problem.Verified locally
Rebuilt the applet and tested against real Chrome (test tabs cleaned up after):
open "test page.html"→file:///private/tmp/ftf/test%20page.html, no dialogopen a.html "café ü.html"→ both opened; unicode encoded ascafe%CC%81%20u%CC%88.html, matching what Chrome reports./verify.shpassesReviewer notes
on openhandler,osacompileproduces a droplet rather than an applet — the app's icon changes. Behaviour is otherwise the same.file:URL scheme is broader than strictly needed for the observed failure; it covers the case where a future Finicky hands off afile://URL instead of a file. Happy to drop it if you'd rather keep the claim minimal../install-chrome-tab-focus.shre-run;verify.shnow says so.🤖 Generated with Claude Code