All
Hardware
Maps
ML & AI
Music
Social
Tools
Writing
Apps & Games
Deciduous
Decision graph CLI for tracing how software decisions evolve
RustSQLiteHTML/JS
Deciduous Archaeology Demo
Demonstrations of decision archaeology on React and stacked git workflows
RustReactGit
Phish Explorer
Jam analytics dashboard for Phish 3.0 era
ElixirPhoenix LiveViewD3.js
Local LLM on MacBook
4-bit quantization, safetensors, and Bumblebee + EMLX for Apple Silicon
ElixirRustPython
A Bot that posts like me
Porting the posting bot to Elixir using local LLM work
ElixirBumblebeeEMLX
Receipt Printer Software Suite
A complete software suite for thermal receipt printers
ElixirPython
Role Call
TV writer overlap explorer
ElixirPhoenix LiveView
Fill Your Sky
Interactive map of 418+ Bluesky communities with 545K+ people
ElixirPhoenix LiveViewD3.js
Code Mirror
A live code mirror experiment
ElixirPhoenix LiveView
Pocket Pnin
A local LLM running on my iPhone, coming to the App Store for free
SwiftMLX
NYC Census Maps
Interactive census and PLUTO data visualization for New York City
ElixirPhoenix LiveViewLeaflet.js
MTA Bus Tracker
Real-time MTA bus and train tracking on an interactive map
ElixirPhoenix LiveViewLeaflet.js
Concert GIF Maker
Extract GIFs from concert videos with a retro Mac interface
ElixirPhoenix LiveViewFFmpeg
Send a VERY direct message, to my receipt printer
A social project where friends send photos that print on my receipt printer
ElixirPhoenix LiveView
Archive TV
A real over-the-air TV channel from magnetic media archives
ElixirFFmpeg
Losselot
Neural network loss function explorer
Python
Todoinksies
A personal todo app
ElixirPhoenix LiveView
Ormery
An ORM written in Temper
Temper
Collage Maker
Upload photos and arrange them into grid collages
ElixirPhoenix LiveView
GenStage Tutorial 2025
A modern GenStage tutorial for the Elixir ecosystem
ElixirGenStage
Temper Rust Bug
Found a bug in the Temper compiler and built a demo repo
TemperRust
300+ Years of Tree Law
A blog post that became its own LiveView application
ElixirPhoenix LiveView
HEEx in Other Languages
Experiments porting Phoenix HEEx templates to Rust, Lua, C#, Java, Python, and JS
TemperRustLua
Live Draft LSP
Live-stream blog drafts from Zed to Phoenix via a custom LSP
RustElixirZed
Bluesky Hoover Apps
Various apps that vacuum up and process the Bluesky firehose
ElixirPhoenix LiveView
Bobby Posts Bot
A bot that posts like me, in Python
Python
Photo Booth Receipt Printer
A portable photo booth that prints on receipt paper
PythonElixir
Nathan For Us
A Nathan For You social network with video search and GIF creation
ElixirPhoenix LiveViewFFmpeg
Browser History Roast MCP
An MCP server that roasts you based on your browser history
PythonMCP
GenStage Tutorial (Original)
The original GenStage tutorial
ElixirGenStage
Bluesky Firehose Toys
Real-time firehose visualizations: emoji streams, jetstream comparisons, and more
ElixirPhoenix LiveViewWebSocket
31 exhibits
Apps & Games
MY FAV LEICA SHOTS
Work Log — Recent Commits
+26-35notactuallytreyanastasio/blimp/main
May 01 04:29
ee19920
docs(exercises): replace commented-out answers with prose hints
The TODO comments in ch4/ch5/ch6 stubs had the answer code
commented out underneath, which is exactly the copy-paste shape
the prose rewrite was meant to remove. Each Exercise: hint is
now a prose description of what the handler should do, with no
literal lines the reader can uncomment.
Stubs still parse, solutions still pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
11006e2
Merge stubs/no-answer-comments: stubs no longer ship the answer
ch4/5/6 exercise stubs had Exercise hints with the literal
solution lines commented out underneath, which defeated the prose
rewrite from the previous merge. Hints are now prose-only.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
+492-1notactuallytreyanastasio/blimp/main
May 01 01:45
6b1ee2b
Merge tutorial/ch06-rider: Chapter 6 ("The Rider")
A BikeShare.Rider actor with a Bike-or-nil session slot. New ground
is nil as a state value, == nil / != nil guards, and the pre-flight
check pattern (ask the bike before committing the rider's own
state). Each rider lives at BikeShare.Rider so it inherits the
supervision boundary from chapter 5. Solution passes 7/7.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
3ce6a62
feat(tutorial): chapter 6 -- The Rider
Adds 06-rider.md and the ch06_rider exercise. Introduces a
BikeShare.Rider actor that holds one user's session: name and a
Bike slot that's nil when idle.
New language ground:
- nil as a meaningful state value
- == nil and != nil guards on multi-clause handlers
- the pre-flight check pattern (send to downstream, dispatch on
the reply, only commit your own state if downstream confirmed)
Three TODO handlers; the multi-clause :pickup and :return get
their fallback clauses pre-wired so the reader's work is the
guarded "do the thing" clauses. The rider also lives at
BikeShare.Rider so each instance is its own supervision boundary
under the same prefix as Vault and Inventory from chapter 5.
Solution passes 7/7 in CLI and WASM.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
+199-243notactuallytreyanastasio/blimp/main
May 01 01:11
7987591
docs(tutorial): rewrite ch3-5 step sections with parallel examples
The "Step N: fill in X" sections used to lead with the exact answer
in a code block, then walk through it line by line. That made the
exercises a copy-paste rather than a synthesis, and the "Putting it
together" recap blocks doubled the answer count.
New shape: each step shows the language pieces in small parallel
patterns (a Counter that takes a typed arg, a Stack that pops the
head, an integer-list reduce, a divide-by-zero bubbler) and asks
the reader to compose the actual handler from those parts. The
exercise file's solution stays the canonical assembled answer.
Also folded the standalone "Sending messages from inside a handler"
section in ch3 and "Sending into a closure" section in ch4 into the
steps that needed the concept, since they were duplicating ground
the steps now cover with their own parallel snippets.
No code changes; solutions still pass 8/7/5 in CLI and WASM.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
0373af8
Merge tutorial/parallel-examples: ch3-5 steps no longer copy-paste
Each "Step N: fill in X" section now shows the language pieces as
small parallel patterns and asks the reader to compose the actual
handler, rather than leading with the exact answer in a code block.
"Putting it together" recap blocks removed; standalone concept
sections that overlapped with the new step content folded into the
steps themselves.
No code changes; solutions still pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ea72016
feat(tutorial): chapter 5 -- The supervision tree
Adds 05-supervision.md and the ch05_supervision exercise.
Introduces three new pieces of language ground:
- bubble :reason raises a failure that propagates as a value
- target <- :msg orelse X catches a bubble at the call site
- bubbles(CascadeBubble) handler annotation: when this handler bubbles,
restart every dotted-name sibling under the
same supervisor prefix
The exercise builds three cooperating actors: a BikeShare.Vault that
holds cents, a BikeShare.Inventory that holds a bike count, and a
BikeShare parent that wires them together. Three TODOs:
- :withdraw bubbles :overdraw on insufficient funds
- :charge translates the bubble into {:error, :declined} via orelse
- :corruption is annotated bubbles(CascadeBubble) so a panic resets
every BikeShare.* sibling
Solution passes 5/5 in the CLI and in the tutorial WASM evaluator.
The chapter walks through bubble vs. tagged tuple as a design choice
(state preserved vs. erased), the dotted-name supervision boundary,
and the bulkhead pattern of unrelated supervisor prefixes never
cascading into each other.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
b138476
Merge tutorial/ch05-supervision: Chapter 5 ("The supervision tree")
Introduces bubble / orelse / bubbles(CascadeBubble) and the dotted-name
supervision tree. Exercise wires a BikeShare parent to a Vault and an
Inventory; the vault bubbles on overdraw, the parent translates the
bubble into a tagged tuple, and an :emergency handler cascades a reset
across every BikeShare.* sibling at once. Solution passes 5/5 in CLI
and WASM.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
19809f3
fix(ws): use the correct RFC 6455 magic GUID for Sec-WebSocket-Accept
The handshake constant in builtins.zig had the magic GUID's hex
digits scrambled:
258EAFA5-E914-47DA-95CA-5AB9DC80CB65 (wrong)
258EAFA5-E914-47DA-95CA-C5AB0DC85B11 (RFC 6455 §1.3)
curl doesn't validate Sec-WebSocket-Accept against the locally
computed expected value, so handshakes from curl saw the 101 and
the WS frames started flowing -- the chat *looked* like it worked.
Real browsers do validate, and rejected every connection with
"Incorrect 'Sec-WebSocket-Accept' header value", which is why
Enter on the chat input never actually delivered: the WS was never
open, the form fell through to its HTTP POST fallback, and the
page reloaded.
Fixes the GUID, fixes the test that was calibrated to match the
bug (it now expects the RFC 6455 example value
"s3pPLMBiTxaQ9kYGzzhZRbK+xOo=" for key "dGhlIHNhbXBsZSBub25jZQ=="),
and adds a deploy snippet to infra/README.md for rebuilding and
shipping the chat interpreter.
Verified end to end on the live box: handshake response now
matches the RFC value, and a headless-Chrome session through
https://blimp.bobbby.online/chat/ logs in, types a message, hits
Enter, and sees the message broadcast back via WebSocket without a
page reload.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1949b0c
Merge chat/fix-ws-handshake: WebSocket handshake now follows RFC 6455
The ws_accept_key builtin was using a corrupted magic GUID, which
curl's loose handshake check let slide but every real browser
correctly rejected. Fixes the constant, fixes the test that was
calibrated to the bug, and documents the rebuild/redeploy steps
for the chat interpreter in infra/README.md.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
9228098
chat: serve at blimp.bobbby.online/chat/ via Caddy reverse proxy
Three changes to chat.blimp so it works behind a path-stripping
proxy:
- Form actions become relative ('/login' -> 'login', '/send' ->
'send') so they resolve against whatever URL the page is at.
- Redirects use './' so the browser stays under the /chat/ prefix
instead of jumping to root.
- The WebSocket URL is now built from window.location, including
protocol (wss when the page is HTTPS) and the current pathname
prefix.
Also adds infra/Caddyfile + infra/README.md, which are the live
contents of /opt/blog/Caddyfile on the Hetzner box. The
blimp.bobbby.online block now redirects bare /chat -> /chat/ and
reverse-proxies /chat/* to 172.18.0.1:8080 (the blog_default
docker-bridge gateway, where the blimp-chat systemd service is
listening on the host's 0.0.0.0:8080).
Verified end-to-end: GET /chat/ serves the login HTML, POST
/chat/login redirects with a session cookie, GET /chat/ws does
the WebSocket upgrade and immediately receives the broadcast
frame.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
337d1f2
Merge chat/path-aware: chat lives at blimp.bobbby.online/chat/
chat.blimp uses relative URLs (form actions, WebSocket, 302
redirects) so it works behind a path-stripping reverse proxy.
Caddy on the Hetzner box now proxies /chat/* on blimp.bobbby.online
to 172.18.0.1:8080, where the blimp-chat systemd service is
listening. Adds infra/Caddyfile + infra/README.md so the proxy
config is tracked alongside the source.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4a74600
Merge tutorial/ch04-zoom-out: Chapter 4 ("Zoom out")
Anonymous functions, map/filter/reduce, and a `BikeShare` parent
actor that fans questions out to a list of `Station` children.
Seven tests; solution passes in CLI and WASM.
Splits the original ch4/ch5 roadmap: ch4 takes "zoom out", ch5
takes the supervision tree + dotted names + crash isolation
material, including a runtime hang to fix where actor refs passed
as typed args interact badly with closure iteration.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
01f1408
feat(tutorial): chapter 4 -- Zoom out
Adds 04-zoom-out.md and the ch04_zoom_out exercise. Introduces
anonymous functions (`fn(x: T) do ... end`) and the three list
builtins that take them as arguments: map, filter, reduce. The
chapter's exercise builds a `BikeShare` actor that holds a list of
`Station` references and fans questions (total bike count, names of
busy stations) out to all of them via map / filter / reduce.
Three TODO handlers (`:open`, `:total_bikes`, `:busy_stations`).
Solution passes 7/7 in CLI and in WASM.
The original ch4 roadmap said "supervision tree, dotted names, zoom
out". This chapter takes the "zoom out" half of that and ch5 will
take supervision + dotted names. Updated 00-why-actors.md's roadmap
to reflect the split, and updated the ch5 placeholder title in the
SPA chapter list.
Implementation note: at this zoom level Stations track bikes by an
integer counter rather than by holding [Bike] actor references. A
runtime hang shows up when actor refs are passed as typed message
args into actors that later get iterated by a parent's
closure-send; we work around it here, will revisit alongside ch5.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
b541da3
docs: add self-hosting narrative to README, deslop pass
Full story of the bootstrap: prerequisites, lexer, parser, evaluator,
closing the loop, bugs found, what came after. Deslopped: cut "the
language eating itself", "fell into place fast", dramatic fragment
openers.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
f0aee23
docs: rewrite README from scratch using decision graph
Self-hosted compiler is the lead story. Shows the actual compiler.blimp
pipeline. Fixes chat.blimp line count (706, not 23k). Adds concurrent
server, safe strings, property tests, LTO benchmarks. Drops the old
Zig-focused implementation table.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
e169732
docs: README reflects self-hosted compiler, Zig is bootstrap
Blimp compiles Blimp. The self-hosted compiler (lexer, parser,
evaluator, codegen, stdlib) lives in chunks/lang/lib/. The Zig code
is the bootstrap that gets it off the ground.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
5f4e961
docs: fix README accuracy -- real line counts, remove stale screenshots
Zig line counts were from months ago. Updated to actual wc -l output.
Clarified C is just the 846-line native runtime, everything else is Zig.
Removed outdated playground and REPL screenshots.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
5986e8a
docs: rewrite README with current project state
Update examples, line counts, WASM size, add tutorial and tree-sitter
links, restructure around key ideas rather than syntax reference.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
9ff6564
docs(tutorial): backtick actor / type names in ch1-3 prose
Sweeps the tutorial chapters so identifiers (Bike, DockingStation,
String, status, id) render monospaced wherever they appear in prose,
not just inside fenced code blocks. Headings get the same treatment
("The shape of a Bike" -> "The shape of a `Bike`"). Code blocks and
already-backticked spans untouched.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
39c1a3c
Merge tutorial/ch03-stations: Monaco editor + Chapter 3
Two coherent changes shipped together:
1. Replace the textarea editor in the tutorial SPA with Monaco
(v0.46.0), with a Blimp Monarch grammar and a dark theme that
mirrors blimp-highlight.js. Cmd/Ctrl+Enter runs tests via a
Monaco command. Cheat tab and rendered-markdown code blocks
keep using blimp-highlight.js.
2. Chapter 3 -- "Actors talking to actors". Introduces the
DockingStation actor that holds a list of Bike references and
orchestrates rentals. New language ground: list-typed state
([Bike]), cons / head / tail / length, typed message arguments
((b: Bike)), and sending messages from inside a handler.
Includes 03-actors-talking.md plus stub + 8/8-passing solution
under exercises/ch03_actors/.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ab2b6bb
feat(tutorial): chapter 3 -- Actors talking to actors
Adds 03-actors-talking.md and the ch03_actors exercise (stub +
solution). Introduces a DockingStation actor that holds a list of
Bike references and orchestrates rentals: list-typed state, the
cons / head / tail / length pattern, typed message arguments
((b: Bike)), and sending messages to other actors from inside a
handler. Exercise has 8 tests; the solution passes all 8 in the CLI.
Wires ch03 into the SPA's chapter list so the tutorial picks up the
new markdown, stub, and solution files automatically.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4a5caa8
feat(tutorial): replace textarea editor with Monaco
Drops Monaco (v0.46.0 from jsdelivr) into docs/tutorial/index.html in
place of the layered textarea + <pre> highlight overlay. Registers a
`blimp` Monarch grammar mirroring blimp-highlight.js (same keywords,
constants, builtins, atoms, #{} interpolation, PascalCase types) and a
`blimp-dark` theme matching the existing token colors so highlighting
stays visually consistent with the static code blocks rendered in the
middle pane.
Cmd/Ctrl+Enter is now a Monaco command bound to Run Tests. The cheat
tab and rendered markdown blocks keep using blimp-highlight.js (small,
sync, no behavioral change). Boot does Promise.all([blimp.init,
initMonaco]) before loading the first chapter.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
00efd1a
feat(lang): blimp_run_tests WASM export + structured test reports
Adds a `blimp_run_tests(source_ptr, source_len)` export to the WASM API
that parses source, runs every `test` block found in actors, and emits a
JSON report with per-test pass/fail plus the expected/actual detail
captured from any failing assertion. Assertion builtins now write to a
shared `last_assertion_detail` buffer on failure so the runner can
attach a readable error to each failing test.
Also fixes three pre-existing `usize`/`u64` mismatches in builtins.zig
that were blocking wasm32 builds under current Zig, and adds a
design doc at docs/lang_design/test-framework.md that lays out what a
first-class Blimp test framework should grow into (watch mode,
structural diffs, source lines on failure, property shrinking).
The browser-embeddable interpreter at docs/blog/repl/blimp.js gets a
new `runTests(source)` method that wraps the export and returns
`{ok, total, passed, failed, tests: [...]}`. Used by the tutorial SPA
to give readers red/green feedback inline.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ebf44cb
chore(deploy): point rsync at /opt/blimp and follow symlinks
The Caddy container (blog-caddy-1 on the Hetzner box) mounts
/opt/blimp:/srv/blimp:ro, which means the host path Caddy actually
reads from is /opt/blimp. The old deploy.sh rsynced to /srv/blimp on
the host, which the Caddy container never saw, so content looked like
it was transferring successfully but 404'd at blimp.bobbby.online.
Fixes the DEPLOY_DIR to /opt/blimp and adds -L to the rsync flags so
the docs/tutorial/exercises symlink gets followed and the real .blimp
files end up on the server.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
9d20b71
Merge lang/ws-bidirectional: WASM test runner, bike share tutorial, SPA, deploy fix
# Conflicts:
# deploy.sh
00f8e54
feat(tutorial): bike share tutorial chapters 0-2 and first exercises
Builds a TDD-driven Blimp tutorial that teaches the actor model by
constructing a bike share simulation one actor at a time. Twelve
chapters planned total; this commit lands the first three:
- Chapter 0: why actors, the dispatcher-with-clipboard mental model,
functional core / imperative shell as the organizing philosophy,
and the piecemeal progression the rest of the chapters follow.
- Chapter 1: your first actor. A Bike with id and status, four
handlers filled in one at a time. Walks through actor templates,
state defaults, the `::` assignment operator, atoms as interned
symbols, spawn-with-overrides, the `<-` send operator, and why
`become` exists (Hewitt's original actor primitive, not mutation).
- Chapter 2: state, become, and the free lock. Multi-clause handlers
with guards, tagged `{:ok, ...}` and `{:error, ...}` tuples, a third
`:broken` state that drops in without any type edits, and the
punchline of the chapter: why an actor's mailbox plus "handlers
run to completion" gives you mutual exclusion without any locks.
Each chapter has a matching exercise file in exercises/chNN_topic/
with a stub the reader fills in, failing tests that progressively
go green, and a reference solution in solutions/.
Voice: one-sentence-per-line markdown, no em-dashes, piecemeal
concept-then-exercise flow, design decisions traced back to the
deciduous graph and docs/lang_design/ where they were made.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
d939fdf
feat(tutorial): SPA with embedded REPL, test runner, and Blimp highlighter
Adds docs/tutorial/index.html, a single-page tutorial viewer. Three-pane
layout: chapter rail on the left, rendered markdown in the middle,
interactive REPL pane on the right. URL fragment (#ch00, #ch01, etc)
drives chapter navigation, fetches the chapter's markdown, loads the
matching exercise stub into the editor, and runs the reader's code
against the WASM-compiled Blimp runtime via the new `runTests()`
binding. Failures show up inline with structural detail captured from
the assertion builtin. "Cheat" tab fetches the solution .blimp file
from disk and highlights it; "Copy to editor" paves over the stub.
Also adds blimp-highlight.js, a zero-dependency syntax highlighter for
Blimp. Keyword list is derived from chunks/lang/tree-sitter-blimp/
grammar.js plus Zig-parser extras (test, property, given, if, else) so
it stays in sync with the real grammar. Handles string interpolation
(#{...}), atoms, numbers, operators, keywords, builtins, and type-like
capitalized identifiers. Used for code blocks in rendered chapter
markdown, the live editor textarea (layered <pre> overlay with scroll
sync), and the cheat-view solution display.
exercises/ lives at the repo root so CLI users can run `blimp test
exercises/...`. For the web tutorial, docs/tutorial/exercises is a
symlink to ../../exercises so the SPA fetches work from a single path
both locally and in production; deploy.sh follows the symlink via
rsync -L.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
90a90fd
docs: scrub 'Ruby's skin, Elixir's soul' phrase
Removes the pithy motto from the three places it appeared (the core
language feel design doc, the parser design doc, and the manifesto)
and replaces each with a direct description of what the phrase meant:
Blimp's surface syntax is Ruby-flavored (do/end, optional parens,
readable sentences) and the data model is Elixir-flavored (immutable
values, pattern matching, pipes, atoms). Same content, no marketing
motto.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
9a4e116
feat(chat): poll-based server loop, read WS frames end-to-end
Replace the blocking accept_loop with a poll-based loop that watches
[server_fd | all_ws_fds]. When a WS fd is ready, read one frame and
route {"type":"msg","from":..,"text":..} to the room, then broadcast.
HTTP POST /send path preserved as a fallback for WS-less clients.
Also fixes extract_quoted's off-by-one: Blimp evaluates args such that
char_at(s, idx) in a call alongside idx+1 sees the post-incremented
idx. Compute char_at into a local first. Same pattern may need the
same treatment wherever it appears (json_escape_loop, slice_idx);
not swept in this commit.
TODO: step 3 of the bidirectional-WS plan still has 'from' coming
from the client frame instead of a server-side session lookup.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
79aad67
chore: remove old blog tutorial.html
The old interactive browser tutorial at docs/blog/tutorial.html is
replaced by the new bike-share tutorial under docs/tutorial/. No
incoming links remain from blog/index.html or the manifesto.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
8bfe1a3
infra: deploy blimp docs to /opt/blimp (#21)
* V1 of the new terminal UI
* attempt to get a faster commit dialogue and make Follow mode work
* feat: C++/Notcurses diff viewer -- Phase 1 scaffold
Port blimp_diff from Rust/Ratatui to C++20/Notcurses for direct cell
manipulation and zero widget-tree allocation per frame.
Architecture (mirrors the Rust version):
- git/: Runner (popen), status/diff/log parsers, file watcher
- state/: Navigation, Interaction (mode + action mapping), CommitState,
LineSelection, FileState classifier
- ui/: Renderer with direct ncplane cell writes, file list, diff pane
with gutter + colored lines, status bar, commit overlay, 4 themes
(solarized dark/light, monokai dark/light)
Performance:
- Async refresh: watcher-triggered git calls run in background thread
via std::async, polled non-blocking each frame. Navigation is pure
index math against cached data -- zero git calls on j/k
- All untracked file diffs pre-cached on refresh
- Input: 16ms poll timeout, drain all pending events before render,
filter NCTYPE_RELEASE to prevent double-skip
Terminal hygiene:
- Clean shutdown: watcher thread joined before notcurses teardown,
stdin drained in raw mode to eat in-flight escape responses,
explicit reset sequences for mouse/kitty keyboard/bracketed paste
- US keyboard shift map for kitty keyboard protocol (Shift+= gives +)
Commit overlay supports multi-line bodies (Enter = newline,
Ctrl+Enter = submit). 54 unit tests covering all parsers, state
machines, navigation, selection, and file state classification.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: Phase 2 -- syntax highlighting, follow mode, log view
Syntax highlighting:
- Built-in regex tokenizer (no external deps) covering keywords,
strings, comments, numbers, operators, types (CamelCase), functions
- Language detection from extension (C/C++, Rust, Python, JS/TS, Go,
Ruby, Elixir, Zig, Shell, Lua, Java, C#, CSS, HTML, YAML, TOML, JSON)
- Per-language keyword sets with theme-derived colors
- DiffCache pre-renders highlighted spans once per file switch;
frame rendering just reads the cache. 50k line cap for huge files.
Follow mode:
- apply_refresh() detects changed files and calls follow_jump()
- Watcher-triggered async refresh auto-scrolls to changed file
Log view:
- Renders actual git log entries with colored hash + message
- Virtual scroll with selected-item highlight, j/k nav, q to return
Diff pane rewritten to consume DiffCache spans via batched
ncplane_putstr_yx (one call per span, not per character).
Renderer signature updated to pass DiffCache and log entries.
14 new highlight tests (68 total, all green).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: notcurses guide audit -- signal handling, resize, perf
Informed by reading Nick Black's "Hacking the Planet with Notcurses"
cover to cover and building a 90-node deciduous knowledge graph.
Signal handling:
- NCOPTION_NO_QUIT_SIGHANDLERS disables notcurses' own handlers
(they race with our cleanup)
- Own sigint_handler sets atomic flag, event loop exits cleanly
- Terminal state saved with tcgetattr BEFORE notcurses_init,
force-restored with tcsetattr(TCSAFLUSH) AFTER notcurses_stop
- Stdin drained in raw mode to eat in-flight escape sequences
Resize:
- NCKEY_RESIZE now handled: clears diff cache, re-queries dims
- Ctrl+L calls notcurses_refresh() for full screen redraw
Performance:
- hline() uses ncplane_hline (one call, not per-cell loop)
- vline() uses ncplane_vline (one call, not per-cell loop)
- fill_rect() uses ncplane_hline per row with shared nccell
- ncplane_erase() for full-screen clear
Init:
- setlocale(LC_ALL, "") in main() -- mandatory per notcurses docs
68 tests green.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor: commit overlay as its own ncplane
Per the notcurses guide (Ch5/Ch14 tetris architecture), modal overlays
should be separate planes composited by notcurses, not painted directly
onto the standard plane. This means the background content is preserved
and doesn't need full re-rendering when the overlay is dismissed.
- create_overlay_plane() creates a child plane of stdplane, centered,
sized to 60%x40% of terminal. Named "commit-overlay".
- show_overlay()/hide_overlay() manage the plane lifecycle
- Overlay created on cc chord and amend ('a'), destroyed on cancel
(Esc) and successful commit
- NCKEY_RESIZE destroys and recreates the overlay at new size
- Overlay coordinates are now relative to its own plane (0,0), not
the terminal -- cleaner rendering code
- Shutdown path destroys overlay before notcurses_stop()
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: visual select → Claude agent seam
Select diff lines, ask Claude a question, get a streaming response
in a full-screen agent tab.
Flow:
- v in diff view: enter visual selection mode (j/k to extend)
- Enter in selection: captures selected lines, opens agent prompt
overlay where you type your question
- Ctrl+Enter: spawns claude --no-input as a persistent subprocess
with pipes, sends initial prompt (selected code + question),
switches to full-screen AgentView tab
- AgentView: streams response with auto-scroll, j/k to scroll,
Tab to flip back to diff (agent keeps running), Esc to dismiss
- g from file list or diff view: switch to agent tab
Architecture:
- AgentState manages the subprocess lifecycle (fork/exec/pipes),
reader thread for non-blocking stdout consumption, mutex-guarded
response buffer. Supports send_followup() for ongoing conversation.
- AgentView is a full-screen mode (like log view), not an overlay
- Agent prompt reuses the overlay plane system
- extract_selected_lines() reconstructs +/- prefixed text from
the DiffCache spans
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: eliminate large-file freeze with lazy per-frame highlighting
The old DiffCache pre-highlighted every line of the entire diff on
file switch. For large files (1000+ lines), the tokenizer blocked
the event loop for hundreds of milliseconds, freezing the UI.
New approach: DiffCache stores lightweight CachedLines with raw text
only (no Span vectors). The diff pane renderer calls highlight_line()
on the fly for just the ~40-50 visible lines per frame. Switching
files is now O(n) string copies with no tokenization -- effectively
instant even for 50k-line diffs.
Scrolling is pure index math. Highlighting cost is bounded by
visible_height * tokenizer_cost_per_line, which is ~50 microseconds
total per frame on a typical terminal.
Side benefit: theme cycling no longer needs to invalidate the cache
since highlighting derives colors from the current theme each frame.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: replace std::regex with manual parser, async initial load
Root cause of large-file freeze: std::regex_search() was called on
EVERY line of diff output to check for hunk headers. libc++ regex
is notoriously slow -- for 5837 lines (not_curses.txt), thousands
of failed regex matches dominated parse time.
Fix: manual parse_hunk_header() using strtol(). Only called when
line starts with "@@" (fast 2-byte prefix check). Regex removed
entirely from the diff parser.
Also: initial data load is now async (refresh_async instead of
refresh_sync). UI appears immediately while git commands run in
the background. Previously the app would freeze for seconds on
startup in repos with large untracked files.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: double Ctrl+C force-exits, lazy untracked diffs, no pre-diffing
Three fixes for the freeze-and-can't-exit problem:
1. Signal handler now force-exits on SECOND Ctrl+C: first sets the
atomic flag (polite), second calls notcurses_stop + tcsetattr +
_exit directly (nuclear). This guarantees escape even when
notcurses_render() is blocked writing to a slow terminal.
2. Removed pre-diffing of ALL untracked files in build_refresh().
git diff --no-index on large untracked files (5837-line
not_curses.txt) was blocking the background thread for seconds.
Untracked diffs are now fetched lazily only when the user
navigates to that specific file.
3. Global notcurses pointer + saved termios for signal handler
emergency cleanup path.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: stop full-screen erase every frame, use notcurses damage tracking
ROOT CAUSE of scroll freeze: ncplane_erase() was called every frame,
marking ALL ~10,000 cells as damaged. notcurses_render() then emitted
escape sequences for every cell, even unchanged ones. Switching from
a 5-line diff to a 455-line diff meant ~8000 RGB color changes at
~14 bytes each = ~112KB of escape sequences in one frame.
Fix: only erase on mode transitions, resize, theme change, or data
refresh (needs_full_redraw_ flag). Normal j/k navigation in the file
list just overwrites the visible rows -- each pane fills its own
area via hline(). Notcurses damage tracking now properly skips
unchanged cells between frames.
Added switch_mode() helper that sets both the mode and the dirty
flag, replacing 17 direct set_mode() calls.
This is the optimization the notcurses guide (Ch4.3) describes:
"Subsequent to the first call, screen updates will write only to
changed (damaged) cells" -- but only if you don't erase them first.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: infinite loop in syntax highlighter on # character
ROOT CAUSE of the scroll freeze. The tokenizer had an infinite loop:
for Rust files, # (used in #[derive], #![...] attributes) was not
handled by any branch. The "everything else" fallback loop stopped
at # but didn't advance i. The outer while loop restarted with the
same i, checking the same character forever.
The file causing the freeze was app.rs (+455 lines) -- every line
with a # attribute spun the tokenizer infinitely.
Fix:
1. Added explicit # handler: for C/C++/C# it's preprocessor (rest
of line). For Rust it consumes #[...] or #![...] as keywords.
For Python/Ruby/etc it was already caught by is_comment_start.
2. The "everything else" fallback now ALWAYS advances i by at least
1 before entering its while loop. This is a safety net -- no
character can ever cause an infinite loop regardless of language.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: commit overlay now opaque -- set base cell bg before erase
The overlay plane was transparent because ncplane_erase() fills cells
with the base cell, not the active fg/bg. The base cell defaulted to
no explicit background, so the diff pane bled through underneath.
Fix: set the overlay plane's base cell to the theme bg_selected color
with NCALPHA_OPAQUE before erasing. Now the overlay fully covers the
content underneath, as the notcurses guide (Ch8) describes: "the base
cell is rendered wherever the plane's gcluster is 0."
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Commit rust stuff so its here for reference while we do C++ impl
* fix: segfault from infinite recursion in switch_mode, shell quoting
Two bugs:
1. SEGFAULT: switch_mode() called itself instead of
interaction_.set_mode(). The replace-all of set_mode→switch_mode
caught the call INSIDE switch_mode's own body, creating infinite
recursion → stack overflow → segfault on any mode transition.
2. SHELL INJECTION: runner's arg quoting only triggered on
spaces/quotes. Commit messages with newlines, $, backticks etc
could break or be interpreted by the shell. Now ALL args are
unconditionally single-quoted.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* add useless file
* test: comprehensive test suite -- 68 → 200 tests
132 new tests covering every module with edge cases and integration
tests using real git repositories.
Parser edge cases (26 new):
- Status: deleted, MM, spaces in paths, empty lines, renamed+modified
- Diff: no-newline marker, new/deleted files, multi-file, count=1
hunks, very long lines, --no-index format, function context headers
- Log: special chars, unicode, long messages, full-length hashes
Highlight edge cases (20 new):
- Rust #[derive] attributes, lifetimes ('a)
- Go/Elixir/Ruby keywords and comments
- Nested/escaped/empty strings, operators-only, whitespace-only
- 1200-char lines, C preprocessor, hex/binary/float numbers
- Function call and CamelCase type detection
- Language detection for all supported extensions
State machine edge cases (51 new):
- Navigation: empty lists, bounds clamping, scroll extremes, hunk/log
boundaries, follow jump with/without mode, divider extremes
- Interaction: all DiffView/Selecting/AgentView key mappings, cc chord
timing (within/after 400ms, c+different key), shift non-interference
- Commit: double submit, insert-after-cancel, error→retry, long
messages, newlines-only, idle no-ops
- Selection: single-point, range-after-clear, extend-without-start,
multiple extends, empty-contains
- FileState: None/None fallback, already-staged/unstaged nullopts,
command arg verification
Git integration tests (17 new):
- Real temp repos via mkdtemp + git init with RAII cleanup
- Runner: status/diff/commit/log/stage/unstage round-trips
- E2E: runner → parser → DiffCache → FileState classification
- Verifies actual git subprocess output parsing
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: black screen on startup -- switch back to sync initial load
The async initial load left repo_.files empty on the first frame,
rendering a blank dark screen until the background thread completed.
Switch back to refresh_sync() for the initial load so the UI has
data immediately. Subsequent watcher-triggered refreshes remain async.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: kqueue-based file watcher replaces polling
The old watcher only checked .git/index and .git/HEAD mtimes, missing
all working tree changes (file edits, new files). Then it was changed
to poll git status every 600ms which was wasteful.
New watcher uses kqueue (macOS) to get instant filesystem notifications:
- Watches .git/index, .git/HEAD, .git/refs for git operations
- Watches working tree directories (up to 4 levels deep) for file edits
- Skips .git, build, target, node_modules, .deciduous
- kqueue EVFILT_VNODE with NOTE_WRITE|DELETE|RENAME|ATTRIB
- Debounces rapid changes (save-all) with configurable delay
- Drains accumulated events after debounce to coalesce
- Zero CPU when idle (blocked in kevent syscall, not polling)
File edits now trigger a refresh within ~200ms instead of never.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: x to discard file changes, S to stash
x key (DiscardFile):
- Tracked files with unstaged changes: git checkout -- <path>
- Untracked files: deletes the file from disk
- No-op on clean files or staged-only files
S key (Stash):
- Runs git stash, refreshes immediately
- Status bar shows "Stashed" or "Stash failed"
Also added stash_pop() to runner for future use.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* simplify: u always runs git restore --staged
Replaced the branching unstage logic (rm --cached for new files,
restore --staged for others) with a single git restore --staged
call. Simpler and works for all staged files.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: crash on s/u/x -- reference to repo_.files after refresh
do_stage, do_unstage, and do_discard all referenced repo_.files[idx]
AFTER calling refresh_sync(), which replaces repo_.files entirely.
If the file list shrank (e.g., staging the last changed file), the
old index was out of bounds -> segfault.
Fix: copy the path (and any needed status fields) to local variables
before calling refresh_sync().
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: split staged/unstaged diff display for partially staged files
When a file has both staged and unstaged changes (MS status), the
diff pane now shows them as separate sections with headers:
── Staged ──
@@ ... @@
(what will be committed)
── Unstaged ──
@@ ... @@
(working tree changes not yet staged)
Data model: replaced FileDiff with CombinedDiff which holds separate
staged and unstaged FileDiff structs. merge_diffs no longer blindly
concatenates hunks -- it preserves the distinction.
Section headers only appear when BOTH staged and unstaged exist for
the same file. Single-state files render normally without headers.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: kitty keyboard protocol garbage on quit
The execute: 3u[107;;107u... garbage after quitting was kitty keyboard
protocol responses arriving after the shell had already started
reading stdin. The terminal processes our disable sequence and sends
an acknowledgment, but it arrives too late.
Fix: send kitty keyboard + mouse disable sequences BEFORE
notcurses_stop(), wait 50ms for terminal to process them, drain
responses via notcurses_get, THEN tear down. Final 200ms raw-mode
drain (up from 100ms) catches any stragglers.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: flash messages at top of diff pane, auto-clear after 2s
Status messages (Staged, Unstaged, Discarded, Stashed, etc.) now
appear as a highlighted bar at the top of the diff pane instead of
in the status bar where they covered keyboard shortcuts.
- set_flash() stores message + expiry timestamp (now + 2 seconds)
- active_flash() returns the message if not expired, empty otherwise
- Rendered as accent-colored bar spanning the diff pane width
- Auto-clears after 2 seconds with no manual dismissal needed
- Removed status_message_ field entirely
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: black lines between hunks -- fill entire row before gutter/content
Context lines between hunks appeared as black gaps because only the
content area was cleared with hline, but stale cells from previous
frames (before the no-full-erase optimization) persisted in the
gutter region. Now each diff line fills the entire row width with
line_bg first, then overwrites with gutter + content.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: selection highlight now clearly visible across all themes
Selection bg was nearly identical to the normal bg in all themes
(solarized dark: {7,54,66} vs bg {0,43,54} -- invisible difference).
New selection colors use a strong blue tint that's clearly distinct
from both the normal bg and the diff add/del backgrounds:
- Solarized Dark: {20, 70, 110} (dark blue)
- Solarized Light: {180, 210, 240} (light blue)
- Monokai Dark: {50, 70, 110} (dark blue)
- Monokai Light: {170, 200, 240} (light blue)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: follow mode bouncing, centered diff scroll, opaque agent overlay
Three fixes:
1. Follow mode no longer bounces between existing files. It now
compares old vs new file lists and only jumps when a genuinely
NEW file appears (wasn't in previous snapshot). Previously it
jumped to any non-selected file with a diff on every refresh.
2. Diff scroll now centers the cursor in the viewport. Added
diff_cursor_ separate from diff_scroll_ -- cursor moves with
j/k, scroll auto-adjusts to keep cursor at viewport midpoint.
Selection extends from cursor position, not scroll position.
3. Agent prompt overlay now opaque (same base cell fix as commit
overlay). Uses larger plane (80%x70% via create_agent_plane).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: claude subprocess uses -p flag, cursor line visible in diff pane
Claude invocation:
- Replaced broken fork/exec with --no-input (flag doesn't exist)
with simple popen of 'claude -p'. Prompt written to temp file
and piped via cat to avoid shell escaping issues with code content.
- Simplified AgentState: removed fork/pipe/pid fields, uses
std::thread + popen instead.
Diff pane cursor:
- Cursor line now highlighted with bg_selected when diff pane is
focused (not during active selection to avoid confusion).
- Gutter background also highlights on cursor line.
- Cursor visible immediately on entering diff view, before any
selection starts.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: diff scroll/cursor resets when switching files
The centered-cursor scroll change introduced diff_cursor_ but never
reset it on file switch. Navigating between files in the file list
left the cursor at the previous file's position, making the diff
appear stuck or empty.
Added reset_diff_scroll() which zeroes cursor, scroll, and h_scroll.
Called in rebuild_diff_cache when the selected file changes. Also
reset cursor in follow_jump.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: diff pane now actually focused when in DiffView mode
switch_mode was setting the interaction mode to DiffView but not
updating the active pane to Diff. The diff pane renderer checks
active_pane for the focused flag, which controls cursor visibility
and border color. Mode was DiffView (keys worked) but pane was
still FileList (cursor invisible, border dim).
Fix: switch_mode now auto-syncs active pane with mode:
- DiffView/Selecting → Pane::Diff
- FileList → Pane::FileList
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* finally scrolling right
* kill the diff viewing stuff with fire, we might as well just write a Zed
plugin for that!
* docs: add Blimp Chat code walkthrough
End-to-end walk through chunks/lang/web/chat.blimp: the four actors,
the accept loop, WS handshake, broadcast mechanism, JS client, and
the Zig TCP/WS primitives that back them.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* infra: deploy blimp docs to /opt/blimp to match static-site convention
The Caddy container in ~/code/blog mounts /opt/<name>:/srv/<name>:ro
for deciduous, phstats, donny, and now blimp. Switching the rsync
target from /srv/blimp to /opt/blimp aligns with that pattern and
makes the deploy flow self-consistent.
Fixes the outage on blimp.bobbby.online that went unnoticed from
2026-04-05 until today -- a prior server-local Caddy hotfix pointing
at /srv/blimp was overwritten by a blog deploy, taking the subdomain
offline. The matching Caddyfile + docker-compose changes land in
~/code/blog.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
4e0427c
Fix compiler warnings in PostLive.Index
- Add @impl true before mount/3, handle_info/2, handle_event/3, render/1
- Group all handle_event/3 clauses together (move modal/category handlers up)
- Unwrap {:ok, msg} tuple from Chat.save_message before accessing .id
- Remove dead {:error, _} branches from add_banned_word and check_for_banned_words
(both functions always return {:ok, _})
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
91c4262
fix: remove unreachable handle_format/7 clauses that matched on has_selection=true
The has_selection variable is always hardcoded to false in handle_event
("insert_format", ...), making all 12 clauses that pattern-match on true
in the 7th position dead code. Removed those clauses and simplified the
remaining ones to use _has_selection since only the no-selection path is
currently reachable.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
d2b49fd
infra: commit Caddy static-site blocks and container mounts
Sync repo with the deployed Caddyfile and docker-compose.yml. These
additions were made live (either on the server or locally without
committing) and rsynced out, but never tracked in git. That drift is
exactly what took blimp.bobbby.online down on 2026-04-05 -- the
prior blimp block existed only on the server, and the next deploy
overwrote it with the older committed version.
Caddyfile: add blocks for deciduous.dev, donny, blimp, phishjamexplorer.
docker-compose.yml: add /opt/deciduous-site, /opt/phstats, /opt/donny,
/opt/blimp volume mounts on the caddy service.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
f07a2e2
Fix compiler warnings across seven files
- terminal_live.ex: Group handle_info/2 and handle_event/3 clauses together,
move format_time/0 out of callback grouping
- blackjack_live.ex: Move process_game_info/5 out of handle_info/2 clause group,
remove unused default arg from render_player_hand/4
- python_demo_live.ex: Group handle_event/3 "reset" clause with other handle_event clauses
- keylogger_live.ex: Fix head_tags component by moving modal attrs (id, inner_block)
to before modal/1 in core_components.ex
- map_live.ex: Remove unreachable catch-all clauses for parse_spotify_link_to_embed_url/1
and parse_apple_music_link_to_embed_url/1
- aside_component.ex: Add missing @impl true before render/1
- seed_tags.ex: Fix incompatible types in Tags.tag_link/3 call
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
0d371f1
fix compiler warnings: remove unused functions, fix function headers
- Remove unused functions: message_visible?/2 (allowed_chats_live),
scale_position/3, require Logger (pong_god_live), generate_id/0
(bluesky_hose), render_mobile_view/1, render_mobile_modal/1,
render_desktop_view/1, post_expanded?/2, get_demo_categories/1,
filter_demos/2 (post_live/index), unused alias Types (play_live)
- Add function header for pick_weighted_choice/2 (story_demo)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
893c628
fix: remove dead route references and fix undefined module warnings
- Remove 11 dead route path references from PostLive.Index demos list
(/war, /bezier-triangles, /generative-art, /gay_chaos, /nathan_harpers,
/nathan_teen_vogue, /nathan_buzzfeed, /nathan_usenet, /nathan_content_farm,
/nathan_comparison, /nathan_ascii)
- Remove CairnController API routes from router (module does not exist)
- Replace EarmarkParser.as_html!/2 calls with MDEx.to_html!/1 in
MarkdownEditorLive (EarmarkParser.as_html! is undefined)
- Remove unused alias EarmarkParser from EnhancedMediaComponent
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
7c11f89
Remove dead receipt printer modules that reference undefined ReceiptPrinter dependencies
Delete three files that alias/call ReceiptPrinter.ReceiptBuilder,
ReceiptPrinter.NetworkClient, and ReceiptPrinter.ImageProcessor -- modules
that don't exist in this project. None of these files are referenced from
routes, supervision trees, or any other code:
- lib/blog/receipt_printer_service.ex (Blog.ReceiptPrinterService)
- lib/blog/receipt_printer/message_handler.ex (Blog.ReceiptPrinter.MessageHandler)
- lib/receipt_printer_emulator.ex (ReceiptPrinterEmulator)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
60bdb01
move receipt printer Python scripts to priv/scripts/
Relocated receipt_printer.py and receipt_printer_service.py from project
root to priv/scripts/ for proper organization. Updated System.cmd calls
in emoji_skeets_live.ex and jetstream_comparison_live.ex to use
Application.app_dir/2 for release-compatible path resolution.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
958fdcd
clean up Application.ex: remove duplicated supervisor tree
- Removed rescue block that duplicated the entire children list (was
missing HoseMonitor and LiveDraft compared to the main list)
- ETS creation already handles existing tables safely, so the rescue
for ArgumentError was unnecessary
- Removed war_players ETS table (war_live was deleted)
- Kept Pythonx env vars (used by PythonDemoLive)
- Removed verbose inline comments
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
41fa6ee
remove situation blocks and elixirc_options that break compilation
Removed situation_command and situation_verbose from mix.exs
elixirc_options - these Elixir 1.20 experimental features were causing
compilation failures (LLM timeout, missing compiler option errors).
Replaced three situation blocks in terminal_live.ex:
- Visitor IP logging intent -> TODO comment
- Online user marquee intent -> TODO comment
- Connected check -> simple if/do block
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
7d57770
remove 28 dead LiveView files with no routes or references
Deleted unrouted LiveViews verified via grep to have zero references
outside their own files:
- ai_dev_live, article_live, bezier_triangles_live, editor_live
- generative_art_live, mta_bus_live (superseded by mta_bus_map_live)
- muenster_live (already commented out), museum_live (admin exists separately)
- rainbow_live, vim_tweets_live, war_live
- jetstream_skeets_live (no route, receipt_printer.py caller)
- All nathan_* variant views (ascii, buzzfeed, comparison, content_farm,
harpers, teen_vogue, usenet) - none referenced from nathan_live.ex
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
5218c3c
add cairn stuff for annotations on the web
58c634c
fix compiler warnings: unused requires, aliases, variables, pin operators
- Remove unused `require Logger` from terminal_live, python_demo_live,
reddit_links_live, phish_component, image_processor, skeet_channel,
poke_around.migrate
- Remove unused `alias Scenario` from designer_logic
- Prefix unused `id` variables with underscore in poke_around.migrate
- Add pin operators to bitstring size() in simple_png.ex
- Remove unused `require Logger` from poke_around.migrate
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6c3199d
ci: align job names with required status check contexts
Ruleset requires contexts named fmt-and-clippy, test, and build, but
the workflow was reporting them as 'Format & Clippy', 'Test Suite (...)',
and 'Release Build'. Drop the display names so jobs report under their
keys, and add a 'test' aggregator that fans in from the matrix so a
single 'test' context reports.
236c8c5
release: v0.14.0 - observation title + description support
Observations now have first-class title + description semantics:
- CLI warns when creating observations without -d
- `deciduous nodes` shows description inline for observations
- Web viewer detail panel shows description prominently below title
- All templates, commands, and instructions updated
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
f24f307
Rewrite system prompt with detailed Expert context guidance
The system prompt now explains:
- Every context section Claude receives and how to use it
- That callers tell you the expected return shape
- That sibling modules show available APIs — use them, don't invent
- That functions must actually exist (no hallucinating APIs)
- To write inline logic when needed functionality doesn't exist
- Session continuity across multiple holes in one compile
- That generated code gets written to source for developer review
This directly addresses Claude hallucinating nonexistent functions
like Blog.Visitors.known_ip?/1 by explicitly instructing it to
only call functions visible in the Expert context.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2bc621b
Add automatic retry on compile errors with error feedback to Claude
When generated code fails to expand/compile, the error is captured
and sent back to Claude: "The previous code had this compile error:
[error]. The code that failed: [code]. Please fix it."
Uses the existing session continuity (--continue) so Claude has full
context from the original intent + its previous attempt. Retries up
to situation_max_retries times (default: 3) before giving up.
Output shows the attempt number and error at each retry:
│ Compile error (attempt 1/4):
│ undefined function foo/1
│ Asking Claude to fix...
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
af3498c
Strip markdown fences from Claude responses before parsing
Claude sometimes wraps code in ```elixir ... ``` fences despite
system prompt instructions. The parser now strips these before
attempting to parse the response as Elixir code.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6996fc6
Filter Kernel and SpecialForms from situation prompt imports
These are auto-imported in every module and just add noise to the
prompt. Claude already knows about Kernel functions.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
a63639d
Add situation_verbose for debug logging of prompts and responses
When situation_verbose: true is set, the compiler prints the full
system prompt, user prompt (with all Expert context), command details,
and raw Claude response for each hole. Off by default.
Set via mix.exs:
elixirc_options: [situation_command: "claude", situation_verbose: true]
Or at runtime:
Code.put_compiler_option(:situation_verbose, true)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
ce1f489
Enrich Expert LS context and add Claude session continuity
Expert context enrichment — when Expert LS engine is running, each hole
now receives:
- Callers of the current function (what callers expect)
- Function definition/spec (the stated contract)
- Module attributes (@moduledoc, @type, etc)
- Struct definitions in the module
- Sibling modules in the same namespace
- Related ExUnit tests
All queries use safe_expert_call which returns [] on failure, so
compilation never breaks if Expert isn't running.
Claude session continuity — the first hole in a compile run starts a
fresh Claude session. Subsequent holes use --continue to resume the
same session, so Claude naturally accumulates context from previously
filled holes. One Claude "brain" across the entire compilation.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
+15719-180notactuallytreyanastasio/blimp/main
Mar 30 18:59
e0396f2
feat: ExUnit-inspired test runner for Blimp
New syntax: test "name" do ... end inside actor bodies.
Suite actor state fields are re-evaluated per test for isolation
(spawn Counter gives each test a fresh instance).
Assertions: assert, assert_eq, assert_ne, refute with colored
left/right output on failure.
Two run modes:
./blimp file.blimp --test (single file)
./blimp test test/ (discover *_test.blimp files)
Green dots for pass, red F for fail, failure summary, timing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
54bff04
fix: slice(s, start, len) uses length not end index -- 10/10 lexer tests pass
The slice builtin treated arg 3 as end index but the lexer
(and the docs) use it as length. Fixed to start+len semantics.
This was the root cause of all 4 failing lexer tests -- keyword
matching, newlines, comments, and actor definitions all used
slice to extract substrings.
Also: nil-safe comparisons in evaluator, nil-comparable in
checker, char_code returns nil for nil input.
The self-hosted Blimp lexer (370 lines) now passes all 10 tests.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
bfeabd0
test: 623 tests -- comprehensive coverage of the entire language
Builtins: 231 tests -- every string/list/map/math/logic/HOF builtin
with edge cases (empty, nil, zero, out-of-bounds)
Operators: 142 tests -- all arithmetic, comparison, logical, pipe,
send, concat operators with type combinations
Language: 125 tests -- actors, become, guards, case, for, def, fn,
closures, cons lists, maps, interpolation, scoping,
dot notation, actor interaction, edge cases
Lexer: 36 tests -- all token types and edge cases
Parser: 34 tests -- all AST node types
Evaluator: 31 tests -- all eval paths
Completion: 8 tests
Bootstrap: 16 tests -- full self-hosted pipeline
4014 lines of tests. 1.5 seconds to run. All green.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2c7cbee
feat: canvas output pane on every lesson
Every lesson shows an Output canvas that displays the running
system's result: values for expressions, rendered views for view
code, errors in red, print output in blue. Canvas always visible
with "Hit Run to see output" placeholder. Consistent Run button
across all lesson types.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
b48d91d
fix: reuse REPL artifact for canvas -- same styles, same rendering
Output tab now mirrors the REPL exactly:
- REPL output lines (=> result, prints in blue, errors in red)
- View pane with blimp-* CSS classes from index.html
- State pane showing vars (green names) and actors (with fields)
- Message log (arrows with target + message)
No custom SVG. Same artifact as the compiled REPL.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
f9ac5f7
feat: autocomplete shows automatically as you type
Completions appear after 2+ characters, updating on every
keystroke. No need to hit Tab first. Exact matches are
suppressed. Menu stays open while typing and updates live.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
50afcf7
feat: multi-actor view composition with mount()
Actors own their state via become, render via :render handler.
mount() sends :render + wraps in data-actor boundary for routing.
JS DOM walk finds nearest actor boundary on button click.
New builtins: mount_root, actor_name, to_atom
Demo: Counter + Greeter composed in one page
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
a8f8dd9
deploy: Blimp Chat live at http://5.161.181.91:8080
Cross-compiled Blimp binary for Linux x86_64, deployed to Hetzner.
Running as systemd service (blimp-chat.service), 780KB memory.
Port 8080 opened on firewall.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
0d5030f
fix: and/or as expression operators, pipes with map/filter/reduce
Bug 1: 'and' and 'or' now work as expression operators alongside
&& and ||. Added kw_and/kw_or to token table and parser.
x > 0 and x < 10 # works now
Bug 2: Pipes with higher-order functions (map/filter/reduce/each)
now work. Pipe evaluator routes through evalFuncCall instead of
only checking the builtin registry.
[1,2,3] |> filter(_, fn(x: Int) do x > 1 end) # works now
[1,2,3] |> map(_, fn(x: Int) do x * 10 end) # works now
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
b6a40a0
feat: BOOTSTRAP -- Blimp compiles Blimp end-to-end, 56/56 tests
The self-hosted compiler compiles programs with: functions with
return type annotations, && and || boolean operators, case
expressions, nested function calls, list operations, string
builtins, comparisons, and multi-line programs.
Fixed: self-hosted lexer now tokenizes &&. Self-hosted parser
handles or/and/|| /&& in the precedence chain. Self-hosted
evaluator executes && and ||. Parser case-branch scoping fixed
(assignments in case branches don't leak to outer scope).
Self-hosted Blimp: 2186 lines across 5 components.
Zig bootstrap: 19101 lines. Compression ratio: 8.7x.
429 lib/lexer.blimp
752 lib/parser.blimp
479 lib/eval.blimp
69 lib/complete.blimp
457 lib/tests (lexer+parser+eval+bootstrap+complete)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6924581
fix: add debug output for WASM load failures
e1a14b2
feat: THE canvas -- generative hex actors, message rays, REPL blob
Added the real BlimpCanvas from web/canvas.js to the tutorial.
Actors render as generative-filled hexagons with SHA-hashed
patterns. Message sends shoot animated rays from the REPL blob
with trailing heads and impact rings. Variables show as squares.
State changes flash white. Auto-layout in hex grid.
This is the compile-time artifact canvas that's always been
part of the Blimp REPL -- now in the tutorial.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
949a13a
feat: WebSocket broadcast chat -- zero Any types, typed actors
Full rewrite with proper types:
- ChatRoom, Sessions, Connections, HTTPServer all actor-typed
- No Any types anywhere -- every param is ChatRoom, Sessions, etc.
- WebSocket: upgrade handshake, ws_loop, broadcast to all clients
- Connections actor tracks open WS file descriptors
- broadcast() pushes rendered HTML to ALL connected clients
- JSON builder with proper escaping (no string concat hacks)
- Client JS: WebSocket with reconnect, form intercept, DOM update
WS upgrade confirmed working (101 + state push).
HTTP login + cookie confirmed working (302 + Set-Cookie).
Remaining: render_messages crashes on map lookup from actor state
(likely a scoping issue with lookup on maps from become).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
26adbc0
feat: <-- operator for async message sends
actor <- :msg is sync (blocks until reply)
actor <-- :msg is async (enqueues in mailbox, returns :queued)
New token: async_send (<--), parsed as MessageSend with is_async.
Lexer checks 3-char <-- before 2-char <-. Evaluator enqueues in
mailbox instead of executing inline. schedule(N) drains mailboxes.
w <-- :compute(5) # queued, returns :queued
schedule(100) # processes all queued messages
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
bc6b4fb
fix: nil-safe comparisons, type checker allows nil in ==/</>
char_code returns nil for nil input instead of crashing.
Comparisons (< > <= >=) return false when either side is nil.
Type checker treats nil as comparable with any type.
Lexer nil guards in is_digit, is_alpha, is_alnum, scan functions.
Lexer tests: 6/10 pass (multiline tests need further debugging
in the type checker's interaction with case-match bodies).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
3841a17
feat: auto-scheduling -- no manual schedule() needed
<-- auto-drains mailbox immediately after enqueue. Reduction
counter also triggers drain every 4000 evals. No explicit
schedule() call required.
w <-- :compute(5) # enqueued AND processed
print(w <- :get) # 25 -- already done
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
fca8a7c
feat: completion engine in Blimp -- replaces complete.zig logic
69 lines of Blimp replaces the completion matching logic.
Keywords and builtin names derived from lists, prefix matching
via starts_with. 8/8 tests pass, 52/52 total.
Self-hosted Blimp: 1674 lines (lexer 429 + parser 699 +
evaluator 477 + completion 69). Tests: 432 lines, 52 passing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
fcd258c
fix: remove Google Fonts import that blocks page rendering
b85fa45
test: 75 property tests across the entire compiler -- 7,500 random cases
Properties cover:
- Arithmetic: commutativity, associativity, identity, zero, negation
- Comparison: complementarity, reflexivity, negation
- Boolean: identity, absorption, De Morgan's laws
- Lists: reverse/reverse, sort idempotent, append length, cons,
sum linearity, uniq idempotent, range length, elem/head
- Strings: concat identity/length, slice identity, char roundtrip,
to_string/to_int roundtrip, upcase/downcase
- Maps: put/lookup, overwrite, keys/values length, merge identity
- Higher-order: map length, filter length, filter complement,
map identity, reduce=sum, map compose, filter idempotent
- Self-hosted compiler: lexer tokenizes random ints, self-eval of
random arithmetic (add/sub/mul), comparisons, variables
- Generators: range bounds, length bounds, type correctness
- Actors: cumulative state, reset, spawn independence
Total suite: 698 tests (623 unit + 75 property) = 15,123 test cases
(623 + 75*100). All green in 5.6 seconds.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
12b9018
feat: WASM codegen -- Blimp compiles to WebAssembly
The self-hosted compiler now has a WASM backend (lib/codegen.blimp,
225 lines). Compiles Blimp integer expressions to valid .wasm
modules that run in any WASM runtime.
compile_to_wasm("2 + 3") -> [0,97,115,109,...] (40 bytes)
# Node.js: main() returns 5
# Browser: main() returns 5
Supported: integer literals, +, -, *, /, ==, !=, <, >, <=, >=,
&&, ||, unary negation, booleans, nested expressions.
Output: valid WASM module exporting "main" -> i32.
Bytecode: i32.const, i32.add/sub/mul/div_s, i32.eq/ne/lt_s/gt_s/
le_s/ge_s, i32.and/or.
New builtins: write_bytes(path, byte_list), read_file(path).
23 tests (18 unit + 5 property/500 random compilations). All green.
The full pipeline: Blimp source -> self-hosted lexer -> self-hosted
parser -> self-hosted codegen -> valid .wasm bytes -> runs in browser.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
e62b8d4
fix: strip quotes from WASM string results in validators
WASM eval returns strings as "hello" (with quotes) but validators
compared against hello (without). Added unquote() helper. Fixed
Greeter lesson validator.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
fdc1689
feat: --self-hosted flag -- Zig bootstraps, Blimp runs
./blimp file.blimp --self-hosted
The Zig runtime loads lib/*.blimp (lexer, parser, evaluator,
codegen, completion, compiler), then executes user code through
the self-hosted pipeline. The Zig lexer/parser/evaluator become
the bootstrap layer only.
./blimp expr.blimp --self-hosted # eval via Blimp compiler
./blimp expr.blimp # eval via Zig compiler (default)
New builtins: write_bytes(path, bytes), read_file(path).
compiler.blimp provides blimp_compile(source, :eval/:wasm/:tokens/:ast).
The Zig is now the kernel: builtins + WASM bridge + CLI.
Everything above it is Blimp.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
3b7a55b
feat: rewrite tutorial -- views by lesson 6, live preview pane
20 lessons restructured: values (3) -> variables/functions (2) ->
views (5, with live rendering!) -> actors (3) -> map/filter/pipes (3)
-> putting it all together (4). View lessons show rendered output
in a light preview pane. Tab inserts spaces, Cmd+Enter submits,
progress saved to localStorage.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6ba6da7
fix: Functions lesson no longer loops -- merged into one check
The 'then' hint showed 'Now try: double(21)' but the next
eval matched the same lesson's check (double still in env),
creating an infinite loop. Merged into one lesson that checks
for double(21) == 42.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
612f934
feat: WASM-powered interactive Blimp tutorial at /tutorial/
14 lessons running entirely client-side via blimp.wasm (130KB).
Predict output, fill blanks, write code -- all evaluated in-browser.
Curriculum: values, variables, functions, actors, builtins, pipes.
Dark theme, mobile-friendly, progress bar.
Deploy: rsync docs/ to server, lives at blimp.bobbby.online/tutorial/
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
3f4844c
fix: rebuild WASM with all new builtins, gate native-only code
Native-only functions (TCP, fork, WS, file IO) are stubbed on WASM
via comptime dispatch. Hole operator returns nil on WASM. Assert
builtins skip stderr on WASM. WASM binary: 178KB (was 131KB).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
65988cd
feat: tutorial IS the REPL -- guided lessons in the REPL stream
Complete redesign: the tutorial is the REPL itself with lesson
guidance. Lessons appear as styled text in the REPL output, you
type at the blimp> prompt, results show inline, state sidebar
updates live, views render in the view pane.
18 lessons: values -> strings -> atoms -> booleans -> variables ->
lists -> functions -> views -> actors -> messages -> map/filter ->
free play. /lessons restarts, /skip advances.
Same REPL as index.html: history, multi-line (shift+enter),
view rendering, state sidebar, mounted actor views.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
7bebcdc
fix: autocomplete derives from builtin registry, not hardcoded list
Completions now iterate eval.builtins.entries.items directly.
New builtins automatically appear in autocomplete without
maintaining a parallel list. Higher-order builtins (map/filter/
reduce/each) added separately since they're in evalFuncCall
not the registry.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
ba0e10f
feat: Blimp lexer written in Blimp -- first self-hosting step
Added char_at, char_code, from_char_code builtins for char-level
string scanning. The lexer (lib/lexer.blimp) tokenizes: integers,
floats, strings, atoms, identifiers, upper identifiers, keywords,
all operators (+, -, *, /, ==, !=, <-, |>, ::, .., ..., ->),
comments, and whitespace. 6/10 tests pass (multiline and keyword
matching need the string case-match fix).
370 lines of pure Blimp. The compiler is starting to eat itself.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
01ccc98
feat: bootstrap loop closed -- 44/44 tests, Blimp compiles Blimp
blimp_self_eval("source") runs the full self-hosted pipeline:
source string -> Blimp lexer -> Blimp parser -> Blimp evaluator -> result
12 bootstrap tests: integers, arithmetic with precedence, strings,
variables, function def/call, nested calls, builtins, comparisons,
case expressions, list ops, multi-line programs. All pass.
Full suite: lexer (10) + parser (11) + evaluator (11) + bootstrap (12) = 44
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
396f630
fix: enforce type annotations on ALL parameters, no exceptions
Lambda params now require types: fn(x: Int) not fn(x).
Checker traverses all function call arguments to catch lambdas
passed to map/filter/reduce/each. Complete audit:
on :msg(x: Int) -- enforced (existing)
def foo(x: Int) -- enforced (previous commit)
state x: Int :: 0 -- enforced (existing)
fn(x: Int) do end -- enforced (this commit)
The only untyped bindings are assignment (x = 42, inferred)
and for loops (for x in list, inferred from iterable).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
8b2911d
ship: rebuild WASM (182KB), update tutorial + manifesto
WASM rebuilt with all fixes: and/or operators, pipes with
filter/map/reduce, Any type checks, <-- async sends, auto-scheduler,
cons operator, self-hosted compiler.
Manifesto pipe example now uses filter |> map |> sort |> reverse.
Tutorial reference pane adds and/or, <-, <-- operators.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1f7478c
feat: the Blimp manifesto -- full language vision with live code
Long-form page covering every premise: values, functions, pipes,
actors, become, message passing, dot notation supervision, views
as message responses, the web framework in Blimp, the Hole operator,
the test runner, runtime internals (arenas, scheduling, LLVM),
WASM in the browser, the canvas, and the self-hosting dream.
Every code example has a Run button that evals in WASM. Actor
examples have the canvas showing hexagons and message rays.
Written in the voice: casual, direct, confident about taste.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
ef9b0c2
feat: self-hosted evaluator in Blimp -- 10/11 tests pass
Tree-walking evaluator (480 lines) that executes map-based AST:
- Literals: int, float, string, bool, nil, atom
- Arithmetic: +, -, *, / with correct precedence
- Comparisons: ==, !=, <, >, <=, >=
- Variables: assignment and lookup via Env actor
- Functions: def, call, params, closures over env
- Builtins: 25+ delegated to host (print, concat, length, etc.)
- Lists: literal construction and operations
- Case expressions: pattern matching with wildcard
- Actors: definition, spawn, message send (1 test remaining)
Combined self-hosted compiler: lexer (429) + parser (699) +
evaluator (480) = 1608 lines of pure Blimp.
Also fixed: checker allows Any-typed vars as message send targets.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
57205b0
fix: form falls back to HTTP POST when WebSocket not connected
e.preventDefault() only fires when WS is ready. Otherwise the
form submits normally via POST. Messages always send now.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
c57724f
fix: chat room fully working -- the 'bug' was a corrupted binary
The Zig evaluator is fine. The elem()/head()/tail() calls all work
correctly at any call depth. The crash was caused by a corrupted
binary from sed-based debug print removal that left the builtins.zig
in an inconsistent state.
Clean rebuild fixes everything. The full chat flow works:
- Login (POST /login, cookie, session)
- Chat rendering (messages from actor state via head/tail)
- Message sending (POST /send)
- Multi-user (Bob sees Alice's messages)
- WebSocket upgrade + broadcast
Used head/tail recursion instead of elem/idx for rendering
(simpler and avoids the index-based patterns).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
19c0f35
feat: reference cheat sheet pane next to state sidebar
Right column has State/Reference tabs. Reference pane covers
the full stdlib: math, strings, lists, higher-order (map/filter/
reduce), maps, views, actors, control flow, utility. Compact
format with green function names and gray descriptions.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
17e3527
feat: interactive Blimp tutorial with runtime eval/test
blimp_eval(code) and blimp_test(code) builtins for runtime
code evaluation. 12-lesson tutorial: values, functions, actors,
builtins. Three step types: predict output, fill blanks, write code.
Dark theme, mobile-friendly (viewport meta, 44px tap targets,
responsive breakpoint at 480px).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
09b06e5
feat: secure string composition -- SafeHtml, SafeSql, context automaton
Implements the accumulator pattern from the paper:
- SafeHtmlAccumulator: tracks HTML parse context through fixed parts,
applies context-sensitive escaping for each interpolation
- SafeSqlBuilder: parameterizes user input ($1, $2...), never interpolates
- HTML context automaton: pcdata/tag/attr_name/attr_value/url states
- Context-sensitive escapers: html_escape, html_attr_escape, url_escape
- Escaper selection based on current parse context (href -> url_escape)
- Reified safe types: %{__safe_html__: true, content: "..."}
22/34 tests pass. Security-critical tests all pass:
- XSS prevention: <script> tags escaped in pcdata
- URL injection: javascript: URLs escaped in href attributes
- SQL injection: user input parameterized, never interpolated
- Safe text passes through unescaped
Context automaton tests have a scoping issue (12 failures).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
ca21d7e
feat: dot notation for actor names in message sends and spawn
Shop.Checkout <- :add(10) now parses, type-checks, and evaluates.
Parser accepts upper_identifier after dot, eval/checker flatten
DotAccess chains into dotted name strings for registry lookup.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
fc29d3c
fix: pipes lesson and predict validator
Pipes with higher-order fns (filter/map) broken in pipe evaluator.
Changed lesson to sort/reverse/length which work. Predict lessons
no longer show Correct when the eval itself errors.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
d1a3c48
feat: Blimp stdlib -- 185 lines replacing Zig builtins
Pure Blimp implementations of:
- Math: abs, max, min
- Lists: sum, range, flat, uniq, list_contains, zip, set_at
- Strings: upcase, downcase (char-by-char transformation)
- Views: text, heading, bold, italic, code_inline, divider,
stack_view, row_view (as pure map constructors)
empty?, nil?, not stay as Zig primitives (can't be self-referential).
51 tests (44 unit + 7 property). All green.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
"> in from ""MFMF."" In the Phish debut version, the bulk of the jamming is conta..."
TAP TO SEE AND PLAY JAMS
Golden Age
.410
TAP TO FLIP BACK
34 jams
★
2009-11-27
9:33
Times Union Center · Albany, NY
> in from ""MFMF."" In the Phish debut version, the bulk of the jamming is contained within structure of the song, except for a brief cacophonous outro.
★
2011-07-02
13:03
Watkins Glen International · Watkins Glen, NY
Staccato funk jam before delays/effects creep in, foreshadowing the "Storage Jam" to come later in the night. > to "Prince Caspian."
★
2012-06-24
11:13
Blossom Music Center · Cuyahoga Falls, OH
A very good, psychedelic jam including an introspective ambient section in which Trey uses his delays/pedals to great effect. > to "Ghost."
★
2012-06-30
10:25
Alpine Valley Music Theatre · East Troy, WI
> in from "Wilson." Rhythmic funk held together by a really fun, loose beat from Fish, with a nice -> to "2001" to finish it.
★
2012-07-03
13:47
Nikon at Jones Beach Theater · Wantagh, NY
-> in from "Sand." There is fantastic interplay between Page and Trey throughout this jam, and Mike also does his part to punctuate the groove. Concludes with a beautiful ambient passage led by Page on the Wurlitzer. A top "GA."
★
2012-08-25
11:03
Aaron's Amphitheatre at Lakewood · Atlanta, GA
> in from "KDF." Dark, space funk with Mike at the forefront for much of the jam. Mike also plays around with an exotic motif towards the end of the jam. > to "Free."
★
2012-09-01
13:37
Dick's Sporting Goods Park · Commerce City, CO
The band wastes no time in hooking up on a mid-tempo groove with a slightly mysterious feel at the start of this jam. From there, it gradually gains intensity, and becomes a bit more rocking as Trey quickens his playing. Ends with a > to an exceptional "Prince Caspian." One of the many strong jams from the monumental 2012 Dick's run.
★
2013-07-03
13:55
Darling's Waterfront Pavilion · Bangor, ME
Laid-back and funky jamming abounds in this one (including a "Manteca" tease). > to "Twist."
★
2013-07-30
12:57
Lake Tahoe Outdoor Arena at Harveys · Stateline, NV
A pretty locked-in version including funk, rock, and upbeat melodic jamming. > to "46 Days."
★
2013-10-20
17:10
Hampton Coliseum · Hampton, VA
> in from "Tweezer" as the second half in a pair of phenomenal jams. The usual funk-rock elements are present, but the jam gets taken to another level when it bleeds into an intense, psychedelic ambience. Fishman on Marimba Lumina. > to "Piper."
★
2013-10-27
16:39
XL Center · Hartford, CT
Ferocious start-to-finish, with magnificent accompaniment by Page, Mike and Fish as Trey solos.
★
2014-07-18
16:37
FirstMerit Bank Pavilion at Northerly Island · Chicago, IL
Fish keeps this jam in high gear as it alternates between segments of funky and upbeat jamming until a wave of ambient effects are introduced around the 15 minute mark and concludes with a > to "The Mango Song."
★
2015-08-14
13:13
Walnut Creek Amphitheater · Raleigh, NC
Begins with some bouncy funk work and then builds into a rocking groove. Brief ambient interlude before a > to "Reba".
★
2015-09-04
15:58
Dick's Sporting Goods Park · Commerce City, CO
An intriguing version which drops into some contemplative space and eventually gains upbeat energy without ever totally taking off.
★
2016-07-18
12:41
Bill Graham Civic Auditorium · San Francisco, CA
After leaving the song's structure, a darkish groove quickly develops with great direction and intensity until a > to "Twist".
★
2016-10-28
25:38
MGM Grand Garden Arena · Las Vegas, NV
A multi-section, "Type II" behemoth which covers a lot of ground and peaks twice in the process. > to "Simple". Longest version to date.
★
2016-12-28
20:48
Madison Square Garden · New York, NY
Coming effortlessly out of the song proper and oozing with wah-funk, the band comes running out of the gates. A thoughtful, almost ambient jam emerges before descending into a Page-led section; Fish consistently building up the percussive attack and Trey doing much the same on guitar. The transitions are seamless throughout, and the end result is a cohesive revelry of celebration. Like 10/28/16, > to "Simple."
★
2017-07-14
5:43
Huntington Bank Pavilion at Northerly Island · Chicago, IL
Acts as the "meat" of a "Your Pet Cat"/"Golden Age" sandwich, with nifty segues into and out of the aforementioned "YPC".
★
2017-08-01
20:07
Madison Square Garden · New York, NY
A bit slow to get going, but really takes off when an uptempo groove kicks in around 11:30 with Trey and Page providing leads over the swiftly moving rhythm section. Continues to pick up steam as Trey takes charge with some exciting playing and peaks the jam before it ends with some brief space.
★
2018-08-05
18:39
Verizon Wireless Amphitheatre at Encore Park · Alpharetta, GA
Begins with a familiar swinging rhythm and Trey playing echoing, organ-like leads before switching to a heavy auto-wah tone. The next segment brings a shift to major and coalesces at 14 minutes when the jam begins its trajectory to a buoyant peak before briefly heading back to the song's riff with added "Reba" whistling/teasing.
★
2018-09-02
15:25
Dick's Sporting Goods Park · Commerce City, CO
Nice -> from "Tweezer". This jam is funky throughout and brightens into a bouncy, feel-good groove as it comes down the home stretch.
★
2018-10-19
23:51
Hampton Coliseum · Hampton, VA
Aggressively experimental "anti-bliss" jamming begins with angular grooves and then takes a rare deep dive into the abyss with plentiful space effects and "Shipwreck" quotes. Resurfaces > "Twist".
★
2018-10-26
17:31
Allstate Arena · Rosemont, IL
> from "Tweezer." The jam embarks on a dynamic ride, traveling from
cascading ear candy loops via Trey, to an effect-drenched minor key groove
that patiently builds to a rousing peak. The music then cools into chilly ambience
before appropriately moving into "Frost."
★
2020-02-23
17:47
Moon Palace · Quintana Roo, Cancun, Mexico
The second half of a deadly 1-2 punch with "Simple". Immediately moves into a bouncing calypso-y shuffle as Fish takes point and Page goes to work on the clavinet. The jam settles down and gets more funky as Fish taps away on his woodblock, Trey in particular laying down some thick nastiness. They then move into soaring anthemic territory, then as the jam dies away a darker and more unsettling mood takes over thanks to Page's synths and Trey's effects. Trey utilizes a guitar line very similar to Chest Fever to fine effect, much as he did in the 8/3/18 "Carini". Tremendous version.
★
2021-07-30
11:55
Oak Mountain Amphitheatre · Pelham, AL
Doesn't stray very far from the usual boundaries of "Golden Age", but does make feints towards "Mind Left Body" and features some very fancy rhythmic games from Fishman and lovely piano work from Page before very briefly blooming into major key and settling into "Mountains in the Mist".
★
2021-10-20
16:21
Matthew Knight Arena · Eugene, OR
> from "Lonely Trip". All four members of the band are locked in from the start; some really nifty and cool playing. At 12:00, the jam takes a serene, light turn which features some beautifully patient playing. Page and Trey enjoy some fun interplay.
★
2022-02-27
19:35
Moon Palace · Quintana Roo, Cancun, Mexico
A non-stop rollicking groove delivered with an assured confidence, as Mike and Fish set a good pace and Page and Trey fill in all the gaps with tasty detail, then takes a darker turn for the last few minutes before > "Lonely Trip".
★
2022-05-28
18:36
The Wharf Amphitheater · Orange Beach, AL, US
Takes a little bit of time to get going, but then resolves into a funky and bouncy jam not far away from "YPC", grows in power slowly and steadily, and nicely peaks before heading home. As with the 8/11/21 "BOAF" or 11/23/97 "Gin", finds its pocket and stays in it to very nice effect.
★
2022-08-12
14:55
Alpine Valley Music Theatre · East Troy, WI
A light and breezy, highly danceable groove is followed by some soul-piercing guitar when Trey gets out in front on the backend.
★
2023-04-14
17:32
Climate Pledge Arena · Seattle, WA
Tight, airy play hews closely to the song proper for quite some time before Trey calls for improvisational departure. Fish breaks the beat - listen, a bit after the 8:00 mark, for this wonderful moment - before the groove bleeds to become hushed, restrained play, evoking early (as in 60s) psychedelic experimentation. And then... memorable almost for what doesn't happen as opposed to what does, this is Phish at their seemingly effortless best, bathing the base with another dose of the finest in the nation.
★
2023-08-26
13:15
Broadview Stage at SPAC · Saratoga Springs, NY
A guest appearance and surprising cover selection lead to shimmering play that transcends expectations. Even in the composed section, what the estimable Derek Trucks brings to this special flood recovery benefit performance makes this version, and what follows, must-hear.
★
2023-10-08
16:25
Bridgestone Arena · Nashville, TN
Standard but quality play breaks for new directions after the 7 minute mark. Ambient synths, Trey's bevy of effects, and big drawn out waves of bass are all anchored by Fish's expert dynamics. The stew grows ever weirder,
as a beat switch leads to a bizarre, carnival-esque dissonant passage before collapsing > "The Well". Excellent.
★
2025-06-27
26:30
Moody Center · Austin, TX
Narrowly surpasses 10/28/16 for the longest version to date, and it's a doozy. Opening with minimalist funk which becomes increasingly reliant on synths and distortions of all shapes and manner, the jam seems to be crawling to a stop around 15:00, but is then resuscitated with a half-time, super-duty march of doom, and carries that same power as it transitions triumphantly to an ecstatic hose-peak finale.
★
2025-07-13
16:11
North Charleston Coliseum · North Charleston, SC
A spirited, high-energy take that is particularly dominated by Trey and Page's nimble play early on. The syncopated jazz-funk-isms that are familiar to "Golden Age" are particularly tight here and inspired as this feel eventually rides to an anthemic major-key rock out. This then builds to a lovely peak before the "Golden Age" beat barges in to wrap it up.
Disappointed that War does not lead to actual combined-arms conflict.
jeff
04:01 AM
that would be hard to conjure
jeff
04:02 AM
I am so excited that this works and is a successful combination of windows and old apple lol
Uechi Nerd
04:02 AM
Probably for the best, actually. That shit is very very messy.
Uechi Nerd
04:02 AM
I am intrigued and happy it works!
Uechi Nerd
04:03 AM
I respect the wizardry.
Visitor7804
04:05 AM
this is delightful.
jeff
04:06 AM
hell yeah visitor 7804, this is livin' brother
guy4get
04:07 AM
i've never felt so alive
EarlofVincent
04:09 AM
Commencing experiment in 3....2....
jeff
04:14 AM
1
leah
04:16 AM
hi!
leah
04:16 AM
this is lovely
jeff
04:21 AM
hi! lol I was just like what if I combined Mac and windows and added a flower tree of life and called it my homepage and then smoked some weed and made it happen in an empty mall in Connecticut
B. Droptables
10:51 AM
Always cool to play with your toys.
Visitor1128
08:47 AM
yo!
Visitor1128
08:48 AM
i can barely work my phone. what am i doing here?
jeff
09:04 AM
the phone is not optimized yet but it "kind of works" I am sorry lol
jeff
09:04 AM
you have to pick a username, then it goes to the chat, then if you hit the bottom tabs it'll let you go to the app sections.
Bobdawg
04:43 AM
Hi everybody this is my blog I hope you enjoy it I did some more changes and anyone can write a post here now for me.
dinkleberg
01:45 AM
ALL HAIL TREE OF LIFE
jeff
08:55 PM
hi Hacker News
jeff
04:28 PM
hey there I am not really Jeff
Mal Function
05:34 PM
Hey! Please reveal... how exactly do I actually use losselot on my Mac? I've run the git clone commend in Terminal.app and seem successfully to have installed into a new <losselot> sub-folder in my home folder but now???