You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
What this is. A lookup-form catalog of Electron Inter-Process
Communication (IPC) channels between the main process and the renderer
process. The architectural rationale is in
architecture/03-ipc-protocol.md. The
concrete renderer-facing API is window.vv, exposed by
resources/preload.js, which is the
authoritative source for this catalog (89 distinct vv:* channels).
All renderer code talks to main through window.vv. The renderer never imports
ipcRenderer, fs, or child_process directly. A separate isolated preload,
resources/web-preload.js, runs inside the
in-app HTTP page (see §2.1).
Legend: ⮐ marks an ipcRenderer.invoke request/response channel (all others
are fire-and-forget send/on). Payloads are plain JSON-shaped data or EDN
text (see §5).
1. Renderer → Main
1.1 Files, content, paging & streaming
Channel
Preload method
Payload
Main receiver
Purpose
vv:open
open(path)
path string
service/open!
Read a local (or ssh:///sftp://) file, send content + git tree, start/reuse its watcher.
vv:close
close(path)
path string
service/close!
Compatibility close path; retained-file sync is the normal ownership path.
vv:retained-files
syncRetainedFiles(paths)
string array
service/sync-retained!
Replace main's retained local-file set and release unretained watchers/assets.
vv:watch-assets
watchAssets(docPath, paths)
{docPath, paths}
service/watch-assets!
Watch local media assets referenced by a retained Markdown/Org/LaTeX document.
vv:content-page ⮐
contentPage(request)
{path, kind, stamp, page, meta?} → page payload with hasPrev/hasNext
content_service.contentPage
Fetch one bounded page for a large log or delimited-table preview.
Show/load the in-app HTTP WebContentsView. tabId is the owning tab, so the navigation relay updates that tab even after the user switches away mid-load.
vv:http-hide
httpHide()
none
web/hide!
Hide the in-app HTTP view.
vv:http-bounds
httpBounds(bounds)
{bounds}
web/set-bounds!
Reposition the in-app HTTP view.
vv:http-snapshot ⮐
httpSnapshot()
none → JPEG data-URL
web (cached capturePage→toJPEG)
Cold-cache fallback raster: freeze the page under an overlay while the always-on-top native view hides.
vv:http-toc-goto
httpTocGoto(id)
heading id string
web → web preload vv:web-scroll-to
Scroll the HTTP page to a heading.
vv:http-scroll
httpScroll(kind)
kind string (page-down/page-up/home/end)
web → web preload vv:web-scroll
Forward page/edge keys to the web page when app chrome holds focus (native view visible).
vv:http-zoom / vv:http-zoom-set
httpZoom(dir) / httpZoomSet(f)
direction / factor
web (setZoomFactor)
Zoom the web PAGE (the native view's own webContents), not the app chrome; reports on vv:zoom-changed.
1.4 SSH / SFTP & connections
Channel
Preload method
Payload
Main receiver
Purpose
vv:ssh-prompt-reply
sshPromptReply(promptId, secret)
{promptId, secret}
ssh (reply!)
The only secret-bearing renderer→main channel: return the user-typed password / key passphrase / MFA answer for a pending prompt. One-shot, resolved into a main-memory promise, never persisted or placed in app-db.
vv:ssh-close-connection
sshCloseConnection(connKey)
connKey string
ssh → transport.closeConnection
Close a pooled SSH connection by its user@host:port key.
Persisted (non-secret) connection metadata rides vv:connections-request /
vv:connections-save in §1.2.
1.5 Native password-manager bridge
Channel
Preload method
Payload
Main receiver
Purpose
vv:password-state-request
passwordState()
none
passwords
Refresh provider status for the native password-manager bridge.
vv:password-search
passwordSearch(url)
URL string
passwords
Search ready providers for logins matching the current web origin. Results are sanitized metadata only.
vv:password-fill
passwordFill(item)
{provider, id, vault-id?, …} metadata
passwords
Reveal the selected item in main, then send credentials directly to the web-view preload. Passwords do not cross into renderer app-db.
vv:password-save
passwordSave(payload)
{token, provider}
passwords
Save a short-lived main-memory login candidate into the chosen provider (the token references the secret; the renderer never carries the password).
vv:password-dismiss-save
passwordDismissSave(token)
token string
passwords
Drop an unsaved login candidate from main memory.
1.6 Extensions & ad-blocking
Channel
Preload method
Payload
Main receiver
Purpose
vv:ext-state-request
extState()
none
extensions
Request the installed-extensions state push.
vv:ext-install / vv:ext-remove
extInstall(idOrUrl) / extRemove(id)
string
extensions
Install (Web-Store id/URL) / uninstall an extension.
Open the multi-file native dialog, seeded to the active/most-recent folder — main's seeds->dir opens in the first candidate that resolves (seed-dir: file → its parent, dir → itself; else the OS home dir).
vv:clipboard-write
copyText(text)
string
shell
Copy text to the OS clipboard.
vv:open-path
openPath(path)
string
shell
Ask the OS to reveal/open a local path.
vv:open-external
openExternal(url)
string
shell
Ask the OS to open an external URL.
vv:app-info-request
requestAppInfo()
none
shell
Request app metadata for the About UI.
vv:quit
quit()
none
shell
Quit the Electron app.
vv:devtools
toggleDevtools()
none
shell
Toggle renderer devtools.
vv:zoom / vv:zoom-set
zoom(dir) / zoomSet(f)
direction / factor
shell
Adjust the app-window zoom (DOM views); reports the new factor on vv:zoom-changed.
1.8 Retired native-PDF seam
Channel
Preload method
Payload
Main receiver
Purpose
vv:pdf-show / vv:pdf-hide / vv:pdf-bounds
pdfShow / pdfHide / pdfBounds
{path, bounds} / — / {bounds}
RETIRED
Native PDF view seam — retired for in-renderer pdf.js (ADR-0013). The preload methods still exist as harmless no-ops; main.pdf/init! is commented out in core.cljs, so these channels have no live listener.
bounds payloads are plain maps with numeric x, y, width, and height
fields derived from getBoundingClientRect().
2. Main → Renderer
Each window.vv.on*(cb) subscription returns an unsubscribe function (see
§4).
Deliver initial and live-refreshed document content. PDFs carry :bytes; directories/archives carry :entries; large logs/tables carry the first :page; a doc collocated with an exported PDF carries :pdfSibling (and a PDF its :sourceSibling).
vv:error
onError(cb)
{path?, message, stamp?}
[:content/error …]
Deliver read/render errors.
vv:tree
onTree(cb)
{root, files}
[:tree/received …]
Deliver git file-tree data (one project per git root).
vv:keymap
onKeymap(cb)
EDN text (registry)
[:keymap/config-received …]
Deliver persisted keybinding config.
vv:grammars
onGrammars(cb)
EDN text{:grammars [...] :filetypes {...}}
syntax/register-user!
Deliver user tree-sitter grammar entries and filetype mappings.
vv:settings
onSettings(cb)
EDN text
[:settings/received …]
Deliver persisted settings.
vv:recent
onRecent(cb)
EDN text
[:recent/received …]
Deliver persisted recent-navigation state ({:trail … :recent-files [...] :web-history [...]}).
Record in-app HTTP navigation onto the owner tab's history (tab may not be the active tab — a slow page can finish after the user switches away).
vv:http-snapshot-ready
onHttpSnapshotReady(cb)
JPEG data-URL
web-host (pre-snap)
Push a fresh page raster (captured after load + on scroll-settle) so opening any overlay freezes the page instantly.
vv:web-toc
onWebToc(cb)
heading vector
[:web/toc …]
Deliver the heading outline from the HTTP web view (relayed from its preload).
vv:web-active-heading
onWebActiveHeading(cb)
heading id or nil
[:web/active-heading …]
Deliver the active HTTP heading (scroll-spy).
vv:web-key
onWebKey(cb)
{key, ctrl, shift, alt, meta}
replayed as a synthetic window keydown → keymap resolver
Forward an app-global Ctrl/Cmd chord from the (separate-context) web view so the keymap runs the same command. Page editing/clipboard chords stay with the page.
vv:history-nav
onHistoryNav(cb)
"back" or "forward"
history dispatch
Forward browser-like navigation from native/web surfaces.
2.3 Open, app-info & zoom
Channel
Preload subscription
Payload
Renderer event/effect
Purpose
vv:open-files
onOpenFiles(cb)
{paths, focus-first?}
[:files/opened …]
Deliver file/URI selections from the native Open dialog or the command-line arguments at launch (each opened in its own tab). focus-first (command-line launch) re-focuses the first path's tab.
vv:app-info
onAppInfo(cb)
app metadata map
[:app-info/received …]
Deliver app metadata for the About dialog.
vv:zoom-changed
onZoomChanged(cb)
{:context "window"|"web" :factor n}
[:view/zoom-changed …]
Report the resolved app-window / web-view zoom factor so the zoom bar shows the live %.
2.4 Extensions & ad-blocking
Channel
Preload subscription
Payload
Renderer event/effect
Purpose
vv:ext-state
onExtState(cb)
EDN text
[:ext/state-received …]
Deliver the installed-extensions list + master toggle.
A non-secret auth-prompt request (password / passphrase / keyboard-interactive). The reply's secret returns on vv:ssh-prompt-reply; the prompt modal holds the secret locally.
vv:ssh-status
onSshStatus(cb)
{connKey, host, state}
[:ssh/status …]
Connection status (connecting/ready/closed).
vv:ssh-error
onSshError(cb)
{connKey, host, port, kind, message}
[:ssh/error …]
A connection/transport error (host-key rejected, SFTP error, …).
2.1 Web-View Preload Channels
These channels involve resources/web-preload.js, which runs in the isolated
in-app HTTP page. It exposes no API to the remote page — it only observes the
DOM and relays to main, which forwards to the app renderer where relevant.
Channel
Direction
Payload
Peer
Purpose
vv:web-toc
web preload → main → renderer
heading vector
web.cljs relay → [:web/toc …]
Report the page heading outline (re-sent to the app renderer on the same channel name).
vv:web-active-heading
web preload → main → renderer
heading id or nil
web.cljs relay → [:web/active-heading …]
Report the scroll-spy active heading.
vv:web-scroll-to
main → web preload
heading id string
web-preload.js
App TOC click → scroll the page to a heading (sent by web.cljs on vv:http-toc-goto).
vv:web-scroll
main → web preload
kind (page-down/page-up/home/end)
web-preload.js
App-forwarded page/edge keys when app chrome holds focus (sent by web.cljs on vv:http-scroll).
vv:password-forms
web preload → main
{url, origin, count, hasPassword}
passwords
Report login-form presence so the app can show the key-icon badge.
vv:password-save-candidate
web preload → main
{url, origin, username, password}
passwords
Store a submitted login candidate in main memory behind a short-lived token.
vv:password-fill
main → web preload
{username, password, url?}
web-preload.js
Fill the selected login directly into DOM fields and fire input/change. (Same channel name as the renderer→main vv:password-fill in §1.5, but a distinct sender/receiver context.)
3. Content Kinds
vv:content uses the file classifier in vinary.main.file-kind/kind-of, except
for directories, which vinary.main.service/send-content! detects first (via
directory?) and lists rather than reading as text:
Extension(s)
kind
Payload detail
any path that is a directory
"directory"
not read as text; payload carries :entries [...], each {:name :path :dir? :size :mtime :symlink}. Stored as :doc/entries, rendered in-pane by the directory browser.
.md, .markdown, .mdx
"markdown"
text is read and rendered through the common IR in the renderer.
.org
"org"
text is read and rendered through the common IR via uniorg (HTML + heading TOC + assets), like Markdown.
.tex, .latex, .ltx
"latex"
text is read and rendered through the common IR via unified-latex (batch render). .sty/.cls/.bib are not here — they render as highlighted source.
.diff, .patch
"diff"
text is read and rendered by the diff IR front-end: a colored unified view plus an on-demand side-by-side (split) view enriched from on-disk sources.
image extensions such as .png, .jpg, .svg, .webp, .avif
"image"
binary is not read as text; renderer uses a file:// URL (a remote image arrives as :dataUrl).
.pdf
"pdf"
bytes streamed to the renderer (:bytes) and rendered in-DOM by pdf.js (ADR-0013); never read as text.
.html, .htm, .xhtml
"html"
not read as text; rendered as a live page in the in-app web view via its file:// URL, with the ad-blocker + extensions applied.
.docx, .odt, .odp, .odf
"office"
parsed in main into sanitized HTML/text preview payloads, then rendered through the common IR.
delimited files render as bounded rows (large files page through vv:content-page); workbooks render capped sheet matrices.
.log, .out, .err, .trace, compressed .log.gz / rotated .log.N.gz, standard log basenames, and sniffed log-like text
"log"
rendered as lines with tolerant severity highlighting; large logs page through vv:content-page or stream through vv:stream-*.
.zip, .jar, .war, .ear, .epub, .tar, .tgz, .tar.gz and vv-archive://…
"archive"
listed in-pane as directory-browser-compatible entries; nested archive entries are addressed by virtual archive URI chains and are not extracted to disk.
.mmd, .mermaid
"mermaid"
text is read and rendered to SVG by the renderer-side Mermaid strategy.
text is read into the read-only CodeMirror source view.
everything else
"text"
plain text routes through the content service first so extensionless logs/delimited files can be sniffed; otherwise text is escaped into plain HTML.
ssh:// and sftp:// URIs classify off the same basename extension (served by
the async SFTP reader instead of the local filesystem).
4. The on* → Unsubscribe Contract
Every window.vv.on*(cb) subscription returns an unsubscribe function that calls
ipcRenderer.removeListener. The Electron event object is dropped; only the
payload reaches renderer code.
ClojureScript data is converted with clj->js, or serialized as EDN text where keyword fidelity matters (vv:keymap, vv:grammars, vv:settings, vv:recent, vv:ext-config, vv:connections).
renderer receive
Renderer converts JavaScript payloads with js->clj :keywordize-keys true, or parses EDN text with cljs.reader.
Plain structured data is mandatory because Electron IPC and contextBridge
cross V8 context boundaries; ClojureScript persistent structures do not cross
that boundary directly.
6. Not Exposed
The renderer does not receive direct access to fs, arbitrary file reads,
ipcRenderer, child_process, or Node require. SSH/password secrets stay
main-side by construction (vv:ssh-prompt-reply is the sole secret-bearing
renderer→main channel, and it is one-shot and never persisted). See
architecture/03-ipc-protocol.md §8
and security/threat-model.md.