Skip to content

Add the diffr browser interface and Cloudflare deployment - #1018

Open
milanb17 wants to merge 22 commits into
mainfrom
mb/diffr-web-interface-9a6716
Open

milanb17 wants to merge 22 commits into
mainfrom
mb/diffr-web-interface-9a6716

Conversation

@milanb17

@milanb17 milanb17 commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Add a browser interface that opens GitHub pull requests and comparisons, computes structural diffs locally, and renders the Whiteboard editor with folding, syntax highlighting, viewed state, keyboard navigation, and statistics.

GitHub sources → shared WASM module → worker pool → structural diff editor

This is the bottom of the remaining stack: #1018 → #1022. Earlier engine optimization dependencies, including #1016, are already on main. This branch is rebased onto current main rather than carrying their pre-squash history.

The browser engine includes a native plugin host and WASM compatibility layer. Generated grammar lexers execute one step at a time to avoid excessive compiler memory. Cloudflare serves the compressed engine and static app; pull requests receive previews and main deploys the production domain.

Evidence

  • Before: this PR conflicted with main; its browser build was terminated and desktop formatting failed. After: the stack is cleanly rebased, and the lexer memory fix and formatting fix are included in this base layer.
  • Browser typecheck and production build pass with current main's required file-collapse observable.
  • The Whiteboard/diffr integration suite passes all 15 tests. Two older unsupported-file expectations now match main's fix(whiteboard): fix lens coverage totals while structural diffs load #1062 requirement to retain complete Git coverage totals; runtime coverage behavior is unchanged.
  • Fresh GitHub checks run on the updated branch; see the checks tab for their current status.

Merge Danger

Door: two-way; code and deployments can be reverted.

Blast Radius: new browser app, Cloudflare workflow, and shared Rust build/plugin infrastructure. The engine download is approximately 11 MB compressed. GitHub credentials stay in the browser and are sent to GitHub. Merge this PR before #1022; #1022 adds search, summaries, agent tools, caching, and the mobile experience.

@milanb17
milanb17 added this pull request to stack #1015 October 8, 2026 06:09
Base automatically changed from sm/diffr-lazy-queries to main October 8, 2026 16:17
milanb17 and others added 19 commits October 8, 2026 23:03
`cargo xtask build-core-wasm` compiles the engine and every grammar for the
browser. The grammars compile against tree-sitter-language's wasm libc
headers; `crates/diffr-core/wasm` supplies the few functions newer scanners
use beyond them. On wasm32, diffr-grammars links each grammar's tables
unpacked and drops zstd, since a page is served compressed anyway.

Agent-Session: 12453e0c-e113-407e-915b-4c9ef7f94bac
Agent-Session: 8f2a69da-568a-4c81-bb98-4b75bfff948f
Agent-Session: 01a1197a-7e1b-7742-a9bd-04ed4353bed1
Agent-Session: 01a11e9f-d478-7a50-b799-c97cc8a58020
diffr-web classifies files too, and needs the generated tag and the tag
name check without the native diffr.

Agent-Session: 12453e0c-e113-407e-915b-4c9ef7f94bac
Agent-Session: 8f2a69da-568a-4c81-bb98-4b75bfff948f
Agent-Session: 01a1197a-7e1b-7742-a9bd-04ed4353bed1
Agent-Session: 01a11e9f-d478-7a50-b799-c97cc8a58020
A browser runs no component runtime. With `native`, diffr-plugin-sdk
provides the plugin contract as plain Rust under the paths wit-bindgen
generates, so a plugin's unchanged source compiles into the host: the host
implements CursorHost and GitHost, and the export macros expand to nothing.
The bundled plugins also build as rlibs for this.

Agent-Session: 12453e0c-e113-407e-915b-4c9ef7f94bac
Agent-Session: 8f2a69da-568a-4c81-bb98-4b75bfff948f
Agent-Session: 01a1197a-7e1b-7742-a9bd-04ed4353bed1
Agent-Session: 01a11e9f-d478-7a50-b799-c97cc8a58020
`cargo xtask build-web` builds a wasm-bindgen package whose Differ
classifies files before their sources are fetched (`preview`) and turns
one file's two sides into its `file` record (`diff`), shaped by the
bundled shape plugins linked in through the SDK's native contract. A
configuration naming plugins the browser cannot run gets notices; one
reading plugins from disk is an error.

For wasm32-unknown-unknown, tree-sitter-language's libc sources (a
scanner heap for tree-sitter's own wasm runtime) are replaced: malloc and
friends come from Rust's allocator, and the stdio tree-sitter only uses
for debug output writes nothing. The shim headers are hashed into CFLAGS
so editing one rebuilds the grammars.

Agent-Session: 12453e0c-e113-407e-915b-4c9ef7f94bac
Agent-Session: 8f2a69da-568a-4c81-bb98-4b75bfff948f
Agent-Session: 01a1197a-7e1b-7742-a9bd-04ed4353bed1
Agent-Session: 01a11e9f-d478-7a50-b799-c97cc8a58020
`/owner/repo/pull/N` and `/owner/repo/compare/base...head` list the
change from GitHub, fetch each file's two sides and diff them in a pool
of diffr workers, nearest the reader first, with files diffr hides
(generated, vendored, lock files) last. Files show in Review Desktop's
multi-diff view, forked under src/review: structural folds, scope and
file viewed marks kept per change in this browser and dropped when a
file changes, the unified/split switch, and diffr's highlights painted
in the Whiteboard themes. F2 and F3 show diff stats and engine timing.

The editor is built from Review Desktop's code-oss sources; the
standalone services the fork removed are copied from upstream VS Code
under src/standalone. Both copies are left out of lint, like code-oss.
The engine ships gzipped, under static hosts' 25 MiB file cap.

Agent-Session: 8f2a69da-568a-4c81-bb98-4b75bfff948f
Agent-Session: 01a1197a-7e1b-7742-a9bd-04ed4353bed1
Agent-Session: 01a11e9f-d478-7a50-b799-c97cc8a58020
Pushes to main that touch diffr, the page or the Review Desktop sources
it is built from deploy diffsthatgetyoustiff.com as Workers static
assets; pull requests from this repository upload a preview version
aliased pr-N. Needs the CLOUDFLARE_API_TOKEN and CLOUDFLARE_ACCOUNT_ID
secrets.

Agent-Session: 8f2a69da-568a-4c81-bb98-4b75bfff948f
Agent-Session: 01a1197a-7e1b-7742-a9bd-04ed4353bed1
Agent-Session: 01a11e9f-d478-7a50-b799-c97cc8a58020
Geist Mono has no "→", one of Monaco's monospace probes, so Monaco
measured carets from the DOM and drew one at a line's end past the fold
hint's margin, over its text. Review Desktop measures before Geist Mono
loads and gets Menlo's uniform widths; the page keeps Geist Mono's
widths and overrules only the monospace verdict.

Agent-Session: 8f2a69da-568a-4c81-bb98-4b75bfff948f
Agent-Session: 01a1197a-7e1b-7742-a9bd-04ed4353bed1
Agent-Session: 01a11e9f-d478-7a50-b799-c97cc8a58020
diffr makes each statement its own one-line fold, with a chevron and no
rail. Hovering one targeted the statement, so the body around it never
lit up. Like diffr's TUI, a scope without a rail is now a target only
from its chevron; elsewhere the innermost body around the line lights.

Agent-Session: 8f2a69da-568a-4c81-bb98-4b75bfff948f
Agent-Session: 01a1197a-7e1b-7742-a9bd-04ed4353bed1
Agent-Session: 01a11e9f-d478-7a50-b799-c97cc8a58020
In the unified view deleted rows are view zones after the modified line
above them, which the fold controls ignored. A deleted row now reads the
innermost open body around its gap, as a body line does. Other view
zones, such as unchanged-line bands, still light nothing.

Agent-Session: 8f2a69da-568a-4c81-bb98-4b75bfff948f
Agent-Session: 01a1197a-7e1b-7742-a9bd-04ed4353bed1
Agent-Session: 01a11e9f-d478-7a50-b799-c97cc8a58020
Fold commands (za zo zc and capitals, zM zR, zj zk) at the caret, Alt+click
to fold recursively, ] and [ between changes, c for context gaps, s, w, i,
\ and Cmd+B for the tree, and j k d u f b space g G to scroll. The diff
stats panel counts files diffr diffed line by line.

Agent-Session: 8f2a69da-568a-4c81-bb98-4b75bfff948f
Agent-Session: 01a1197a-7e1b-7742-a9bd-04ed4353bed1
Agent-Session: 01a11e9f-d478-7a50-b799-c97cc8a58020
Monaco positions overlay widgets on its next render, which a hover may not
cause for a second, so each new control first sat in the editor's top left
corner and then jumped to its scope's header.

Agent-Session: 8f2a69da-568a-4c81-bb98-4b75bfff948f
Agent-Session: 01a1197a-7e1b-7742-a9bd-04ed4353bed1
Agent-Session: 01a11e9f-d478-7a50-b799-c97cc8a58020
Agent-Session: 8f2a69da-568a-4c81-bb98-4b75bfff948f
Agent-Session: 01a1197a-7e1b-7742-a9bd-04ed4353bed1
Agent-Session: 01a11e9f-d478-7a50-b799-c97cc8a58020
Agent-Session: 8f2a69da-568a-4c81-bb98-4b75bfff948f
Agent-Session: 01a1197a-7e1b-7742-a9bd-04ed4353bed1
Agent-Session: 01a11e9f-d478-7a50-b799-c97cc8a58020
Preserve the deployed browser UI, development fixture hook, and production asset filtering. AI-assisted with Codex.

Agent-Session: 01a1197a-7e1b-7742-a9bd-04ed4353bed1
Agent-Session: 01a11e9f-d478-7a50-b799-c97cc8a58020
Consume the cursor with into_sides after shaping, matching the native host in PR #1011. AI-assisted with Codex.

Agent-Session: 01a1197a-7e1b-7742-a9bd-04ed4353bed1
Agent-Session: 01a11e9f-d478-7a50-b799-c97cc8a58020
Avoid applying the previous file’s ranges during a virtualized editor model switch. AI-assisted with Codex.

Agent-Session: 01a1197a-7e1b-7742-a9bd-04ed4353bed1
Agent-Session: 01a11e9f-d478-7a50-b799-c97cc8a58020
Handle the DiffError variants introduced by PR #1016, matching the native host. AI-assisted with Codex.

Agent-Session: 01a1197a-7e1b-7742-a9bd-04ed4353bed1
Agent-Session: 01a11e9f-d478-7a50-b799-c97cc8a58020
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ns4ZYciirL61M66WFtLXRp
Agent-Session: 01a11e9f-d478-7a50-b799-c97cc8a58020
LLVM's WebAssembly backend checks each loop for irreducible control flow
by working out which blocks reach which, in memory square in the
function's blocks. A generated lexer is one loop around a branch per
state, so tree-sitter-fortran's took over 11 GB at -O0 and the Diffr Web
job's runner was killed. The lexer now takes one step and returns where
to go next; a driver advances and calls it again. The engine build peaks
under 3 GB, and parse trees match the original lexers'.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ns4ZYciirL61M66WFtLXRp
Agent-Session: 01a11e9f-d478-7a50-b799-c97cc8a58020
@milanb17
milanb17 force-pushed the mb/diffr-web-interface-9a6716 branch from c92390a to d1aae54 Compare October 9, 2026 06:07
Agent-Session: 01a11e9f-d478-7a50-b799-c97cc8a58020
Agent-Session: 01a11e9f-d478-7a50-b799-c97cc8a58020
Agent-Session: 01a121bc-677b-7a70-a558-53bb1365164c
Agent-Session: 10dc1e44-eba6-438e-b46d-1228abd8fd81
Agent-Session: e137d86a-da30-480f-9f2f-152b1946fef2
Agent-Session: 01a11e9f-d478-7a50-b799-c97cc8a58020
Agent-Session: 01a121bc-677b-7a70-a558-53bb1365164c
Agent-Session: 10dc1e44-eba6-438e-b46d-1228abd8fd81
Agent-Session: e137d86a-da30-480f-9f2f-152b1946fef2

This branch has not been deployed

No deployments
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.

3 participants