Skip to content

foolscap:// links open a document from other apps and web pages - #46

Merged
seamoss merged 2 commits into
seamoss:mainfrom
productengineered:feat/url-scheme
Sep 29, 2026
Merged

seamoss merged 2 commits into
seamoss:mainfrom
productengineered:feat/url-scheme

Conversation

@productengineered

Copy link
Copy Markdown
Contributor

Other apps and web pages can now open a document in Foolscap with a link:

foolscap://open?path=<URL-encoded absolute path>
foolscap://open?path=%2FUsers%2Fyou%2Fnotes%2Fbrief.md

What it does

  • Registers the scheme. protocols in electron-builder.yml puts CFBundleURLTypes in Info.plist, and the main process calls app.setAsDefaultProtocolClient('foolscap') (in dev, with process.execPath and the app path, for Windows/Linux).
  • Handles links on every route in. macOS delivers open-url, registered at the top level because a cold launch fires it before ready. Links queue until the app is ready. Windows/Linux get the link in argv, both on first launch and relayed through second-instance. pathsFromArgv now skips foolscap: arguments so a link is never mistaken for a file to create.
  • A good link is just another file from outside. It goes through openFileFromOutside, the same path as a Finder double-click: same batching, same choice of window, and a file that's already open gets its tab brought forward instead of a duplicate. On a cold launch it joins the launch arguments, so the session still restores.
  • Guarded. src/main/open-link.ts (pure, tested) accepts only host open, reads only the path parameter (anything else is ignored), and requires an absolute path to an existing .md, .markdown, or .txt file. Anything else opens nothing and shows a one-line toast, through a small new app:notice channel (WindowSession.notify).
  • README: a short "Opening files by link" section with the format and a terminal example.

A fix it turned up

On a cold launch that restores a session, a file arriving at launch (a link or a Finder open) could open as a background tab. restore() ran its tabs one await at a time and then activated its saved tab after the incoming file had already become active. Restore now queues on the same opening chain as opens, so the incoming file lands after the restored tabs and stays active. The untitled-tab fallback in did-finish-load skips windows with a queued restore, so it doesn't leave a stray Untitled tab.

Testing

  • pnpm typecheck and pnpm test pass. New tests: open-link.test.ts covers spaces and unicode, extra parameters, dot segments, missing files, wrong types, relative paths, other hosts and schemes, and garbage input; cli.test.ts checks that argv links are skipped.
  • Tested on a packaged arm64 build installed in /Applications, using open "foolscap://open?path=…":
    • App already running: a file opens and the app comes to the front; a path with spaces opens; a repeat link reuses the existing tab.
    • A missing file and a .png each show the toast and open nothing.
    • App closed: the link launches Foolscap, the session restores, and the linked file is the active tab.
    • Replayed a saved six-tab session plus a launch-argument file in an isolated profile: every tab is kept, the new file is appended and active, and no extra Untitled tab appears.

Notes: on macOS, setAsDefaultProtocolClient ignores its path and arguments, so pnpm dev registers Electron.app itself. Whichever Foolscap launched last owns the scheme. In the Mac App Store edition, the sandbox will only allow paths the app already has access to.

@seamoss
seamoss merged commit a77f2b4 into seamoss:main Sep 29, 2026
1 check passed
seamoss added a commit that referenced this pull request Sep 29, 2026
⌥⌘C puts the rendered document on the clipboard with its styles inlined
and the markdown as the plain-text flavor — a rewrite of the one
pipeline's output, never a second parser; each tab now remembers whether
it was in preview or the editor, with no source flash on the way in;
and foolscap://open?path=… lets other apps open a document, guarded to
absolute paths of existing markdown files, with a cold-launch fix so a
linked file stays the active tab after the session restores
(#44–#46, Brandon Dohman @productengineered).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants