Skip to content

Providers

lukataylor-pixel edited this page May 3, 2026 · 2 revisions

Providers

Each tab in Soffit is rendered by a provider registered for a URI scheme. The tab's source field is a string like file:///path/to/file.md or folder:///Users/me/notes. Soffit looks up the provider for that scheme and asks it for a SwiftUI view.

This page is a tour of every shipping provider.

Folder — folder://

Opens a directory. Two modes per folder:

Grid — sortable card grid. Cards show live excerpts (first 600 chars of markdown, first 400 of mermaid, first 300 of plain text). Sort by Name, Recent (modification time), or Kind.

Canvas — see Canvas mode.

Cards: single-click opens the file, double-click opens in Split, right-click for Open / Add to Canvas / Reveal in Finder.

A breadcrumb at the top gets you back up the directory tree. Clicking a folder card navigates in place (the panel's source is rewritten — the panel ID stays the same, so canvas state, recent files, etc. don't get reset).

File — file://

Markdown editor. Four modes — Preview, Source, Split, Math. See Markdown editing.

Recognised extensions for markdown rendering: .md, .markdown, .mdx. Other text files (.txt, .swift, .py, .json, .yml, etc.) open in the fallback monospace Text view, no editor.

Mermaid — mermaid://

Renders a workspace-relative .mmd file as an SVG diagram. The path is the URL path:

mermaid:///diagrams/user-flow.mmd

Soffit reads the .mmd from disk, then loads a local HTML shim (Resources/mermaid-shim.html) into a WKWebView. On the shim's didFinish, it postMessages the diagram source into the page. The shim runs vendored mermaid.min.js (no network required) and renders the SVG.

If the file is missing or unreadable, the shim shows a small placeholder so the panel never goes blank.

Web — https:// and http://

Anything WKWebView can load: dashboards, GitHub, your local dev server, embedded Figma frames.

Figma URLs auto-rewrite to embed form. http://localhost:* passes through unmodified.

Terminal — terminal://

Embedded shell via SwiftTerm's LocalProcessTerminalView. Starts with cd <workspace> then execs your login shell ($SHELL, defaulting to /bin/zsh).

Each terminal pane is its own process. They don't survive a relaunch — re-opening the app spawns fresh shells. State you care about belongs in scrollback or files, not in the terminal panel itself.

Sketch — sketch://

Freehand drawing surface. Six-color palette, three brush sizes, undo + clear. Strokes persist as JSON in the panel state and round-trip through layout.json like any other panel.

Strokes are vector, not bitmap, so they stay crisp at any zoom. The current implementation supports plain ink only — no shapes, arrows, or text-on-canvas — for that, drop a markdown file onto a folder canvas instead.

Chat — chat:// (legacy)

chat://claude panels stream against https://api.anthropic.com/v1/messages using the API key in your Keychain.

The chat creation UI was removed in v0.3 — there's no menu item, sidebar entry, or [+] option to make a new chat panel. The provider remains registered so any chat panels persisted before v0.3 still load and work.

Adding a new provider

Conform to PanelProvider:

protocol PanelProvider {
    static var scheme: String { get }
    static var displayName: String { get }
    func makeView(for source: PanelSource, context: PanelContext) -> AnyView
}

Register in AppServices.init:

registry.register(MyProvider())

Your provider gets a PanelContext with the workspace root, the keychain, and a savePanelState callback for persisting to the panel's state: Data? blob (which round-trips through layout.json for free).

Clone this wiki locally