npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@humanlayer/fold-tui-theme

v0.0.5

Published

A playable OpenTUI app that renders a GitHub PR/issue browser as a cyberpunk HUD in **six swappable themes** — the two canonical candidates (RAPTURE, TACTICAL) plus four experiments (NEUROMANCER, RED ALERT, COVENANT, WINTERMUTE) — so you can decide which

Readme

fold-tui-theme

A playable OpenTUI app that renders a GitHub PR/issue browser as a cyberpunk HUD in six swappable themes — the two canonical candidates (RAPTURE, TACTICAL) plus four experiments (NEUROMANCER, RED ALERT, COVENANT, WINTERMUTE) — so you can decide which aesthetic to carry into the real Fold TUI. Every theme renders exactly the same data; press t to cycle them live and judge them side by side. This package exists to make that decision, not to be production code — see What this is not.

For the design system itself — the palette, the glow threshold that decides which colors emit light, the typography rules, and exactly how the glitch works — see STYLE.md. It is written to be self-contained: enough to rebuild this look from scratch without reading the source.

Run it

From this directory (the repo is a Bun workspace; run bun install at the root once if you haven't):

bun run demo

That is the zero-setup path: --demo forces the bundled fixtures, so it needs no network and no GitHub token. Use bun run start to pull live data from GitHub instead, or bun run <theme-id> (rapture, tactical, neuromancer, redalert, covenant, wintermute) to start on a specific theme. The app accepts --theme <theme-id>, --repo <owner/repo> (default humanlayer/fold), and --demo.

Live data is best-effort: the client discovers a token from GITHUB_TOKEN, GH_TOKEN, or gh auth token, and falls back to the fixtures on any failure — no token, no network, rate limit, private repo, 404. The network can never take the playground down. When it does fall back, the header's RATE readout reads OFFLINE and the SOURCE panel names the reason.

Everything on screen is real

Every panel is derived from records GitHub actually returned. The rail used to carry a spinning targeting reticle, sine-wave CPU/MEM/NET/IO gauges, fabricated LAT/LON coordinates, and a random-hex "data stream" — all of it is gone. The rule now: if a panel can't be backed by a function in src/github/stats.ts or a field on Feed, it doesn't belong on screen.

┌─ FOLD │ NAME + tagline │ REPO// owner/repo │ AUTH · RATE ─────────────────────────┐
├───────────────┬──────────────────────────────────────┬──────────────────────────┤
│ INDEX         │ RECORD                               │ STATE                    │
│  PULLS /      │  title · state chip · author ·       │  counts per state,       │
│  ISSUES       │  dates · comments · labels           │  bars in semantic slots  │
│  + rows,      │  ────────────────────                ├──────────────────────────┤
│  glyph per    │  // DESCRIPTION                      │ LABELS     top 5         │
│  row          │  scrollbox, markdown-ish body        ├──────────────────────────┤
│               │                                      │ AUTHORS    top 4         │
│               │                                      ├──────────────────────────┤
│               │                                      │ ACTIVITY   14-day spark  │
│               │                                      ├──────────────────────────┤
│               │                                      │ SOURCE     LIVE/FIXTURES │
├───────────────┴──────────────────────────────────────┴──────────────────────────┤
│ ↑↓/jk SELECT  TAB PULLS/ISSUES  T THEME  Q QUIT   FX// B GLOW:ON … R CRT-BAR:ON │
└───────────────────────────────────────────────────────────────────────────────────┘
  • INDEX — the PULLS / ISSUES list, with per-tab counts and a state glyph on each row. Tab switches lists; j/k moves the cursor.
  • RECORD — the selected item in full: title, state chip, author / opened / updated / comments (plus the branch-ref arrow on PRs), labels, and a scrolling, lightly-marked-up body.
  • STATE (stateTallies) — counts per open/draft/merged/closed, each bar drawn in the same semantic slot the list uses; zero-count rows render muted.
  • LABELS (labelTallies) — the top 5 labels by frequency.
  • AUTHORS (authorTallies) — the top 4 authors by record count.
  • ACTIVITY (updatesByDay) — a 14-day sparkline of how many records were last updated each day, with running total and peak.
  • SOURCE — whether the feed is LIVE or FIXTURES; when live, the rate-limit reset countdown; when it fell back, the reason (a 404, a rate limit). offlineReason and the reset countdown were the only Feed fields nothing else surfaced.

The layout is responsive: below ~118 columns the right-hand rail (STATE / LABELS / AUTHORS / ACTIVITY / SOURCE) drops; below ~84 the INDEX list narrows (to 40% of width, floored, min 24 cells). RECORD always survives.

Comparing the themes

t cycles the theme in place — same data, same layout, different palette and post-process chain — so the comparison is instantaneous. The cycle order is RAPTURE → TACTICAL → NEUROMANCER → RED ALERT → COVENANT → WINTERMUTE. The two canonical candidates are deliberately pulled apart along these axes:

| Axis | RAPTURE | TACTICAL | | --------------- | ------------------------------------------ | ---------------------------------------------------------------------- | | Canvas | absolute black | murky brown-black, like a dirty optic | | Neon | teal and laser purple and red | amber only; a rare cyan flash; red | | "Injected" slot | laser purple | bright yellow (same system, running hot) | | Frame | thin single border, cool teal | heavy border, burnt orange | | Heading prefix | // | [ | | Signature FX | glow + aberration + injected-colour bursts | vignette + heavier scanlines + chroma-dropout + injected-colour bursts | | Motion | fast scan sweep, occasionally unstable | slow tube roll, constant |

With the reticle gone, the fastest reads are the border color (cool teal vs burnt orange), the semantic state colors in STATE and INDEX (MERGED is laser purple in A, a rare cyan in B), the "injected" slot (purple vs yellow), and the FX signature: glow plus a glitch burst in A that separates the color layers (chromatic aberration) and snaps back, versus a vignette + denser scanlines in B whose bursts drop chroma — the whole frame washes toward monochrome. In both, the burst also injects corrupt color it cannot get by moving or draining the frame: solid color blocks stamped over the logo and borders, and tinted runs of foreground — warm/red/gray in tactical (never cool), the spliced neon palette in rapture. Dropout alone could only ever darken B; the injection is what turns things red, burnt-orange and amber. (glitch.chromaticAberration is pinned at 0 in tactical; chromaDropout at 0.4 plus injection carries its corruption — an unstable analog signal, not a splice.) Both themes roll a CRT bar, but A's is a fast thin scan sweep over a black void (only glyphs flare) and B's a slow fat tube roll that lifts the murky canvas with it.

The names carry the intent. RAPTURE — "drowned deco // neon splice" is a sunken art-deco city lit only from within: amber brass carries the structure, electric teal is the cold sea pressing on the glass (borders and structural data), laser purple is the neon signage marking anything "injected" (merged records, #123 cross-references, count badges), and piercing red is the rare emergency — all against absolute-black water that supplies none of its own light. TACTICAL — "optic feed // nominal" is the view through a cyborg's lens: amber and burnt orange own the whole screen, neon red is the only loud voice, cyan is a single rare flash, and the dominant artifact is the CRT itself.

The four experimental themes

Each is a complete Theme under src/theme/, built with the same discipline (role-named slots, a documented glow-threshold partition, a scarcity budget for the critical color, one whole-frame glitch idiom):

  • NEUROMANCER — "matrix feed // ice intact." Green phosphor owns the world (the matrix itself); cold cyan is ICE — the countermeasure lattice — on structural data; the one warm flash is an amber klaxon (black ICE burning). Analog chroma-dropout glitch, double frame. WINTERMUTE's twin.
  • RED ALERT — "master alarm // all systems red." An anime mech cockpit mid-alarm: blue-shadowed razor reds on near-pure black (no rust, no orange drift), a white strobe as the critical color (the only thing brighter than a red world), one cyan sync light, hazard yellow on DRAFT. Digital chromatic-aberration glitch, urgent CRT bar.
  • COVENANT — "plasma lattice // shields nominal." An alien warship's hardlight command surface: magenta-pink plasma structure, electric shield-cyan relief, a rare relic-gold, acid containment yellow for critical (red would mud into the pink). Aberration reads as shield harmonics; the rounded frame is the alien curvature tell.
  • WINTERMUTE — "ice lattice // self-diagnostic." A glacial orbital AI auditing itself. Restraint is the signature: the glow gate admits exactly one token (white-ice), the glitch fires roughly once a minute, no vignette, double frost frame, and the single warm color is a thermal-amber fault light. NEUROMANCER's twin.

The theming system

This is the part that actually informs the decision: is this the right token interface to carry forward?

  • Theme is one flat token interface (src/theme/types.ts): name / tagline, a color object, chrome, semantic, fx, and two block ramps — barRamp (▏▎▍▌▋▊▉█, left-to-right) for the horizontal count bars and sparkRamp (▁▂▃▄▅▆▇█, bottom-up) for the activity sparkline. rapture.ts and tactical.ts each define a private local palette of raw colors and map it onto those tokens.
  • No hex literal exists outside src/theme/*.ts. Every component calls useTheme() and references a slotcolor.core, color.inject, color.alert — never a color. Slots are named by role, not hue: a foundation (core/coreBright/coreDim), a cool "augmentation" pair (grid/gridDim), an "injected" slot (inject), a critical slot (alert), and a text hierarchy. Every slot has a reader — the dead ones were deleted, because a token interface carrying unused slots lies about what the aesthetic needs. Swapping themes is just swapping which raw colors sit in those roles; the components don't change. (Verified: rg '#[0-9a-fA-F]{6}' src matches only the two theme files.)
  • The post-process chain is assembled from fx tokens (src/hud/postfx.ts). installPostFx(renderer, theme, toggles) walks the PostFx tokens in a fixed order (glow → vignette → scanlines → CRT bar → glitch) and pushes one pass per token that is both present in the theme and enabled by the runtime toggle. It returns a disposer; App.tsx tears the chain down and rebuilds it whenever the theme or a toggle changes. Every pass is independently switchable, and a pass runs only when the theme declares it and the toggle permits it — so RAPTURE, which defines no vignette, shows V VIGNETTE:-- rather than pretending it is on. Everything ships on by default; the footer spells out each key and what it drives, because a toggle nobody can find is a toggle that looks broken. The scrolling CRT bar gets its own switch (r), never shared with the static vignette, since it is the only pass that animates continuously.
  • semantic maps GitHub states onto palette slots. open/closed/merged/draft each name a color slot; displayState() in src/github/types.ts collapses state/draft/ merged into one token, and useStateStyle() in src/components/atoms.tsx turns that into a { glyph, color, label } used by both the INDEX rows and the STATE panel. This is where the "no green, red is rare" rule lives: in both themes OPEN is not green, and red is spent only on CLOSED, target locks, and destructive arrows.

If you carry this forward, the load-bearing ideas are role-named color slots instead of hues, and an FX chain derived from tokens with per-effect runtime gates.

Gotchas a maintainer will trip on

These are real and mostly non-obvious. Do not "simplify" them away.

  • Post-process deltaTime is milliseconds — but the bundled effects disagree. CRTRollingBarEffect divides by 1000 internally (it wants ms), while DistortionEffect treats the value as seconds. Fed raw ms, DistortionEffect fires roughly every frame and expires the next, degenerating into constant static instead of occasional bursts. This is why postfx.ts hand-rolls a GlitchDirector (seconds internally) rather than reusing DistortionEffect, and passes raw ms straight to CRTRollingBarEffect. Keep it.
  • Do not use opentui's BloomEffect. Its emitter test is lum(fg) > threshold and it never looks at the cell's character. A blank cell still carries a foreground, and the default foreground is white (luminance 1.0) — so on a mostly-empty HUD over a black void every empty cell becomes a maximum-intensity emitter, threshold goes inert, and the palette drowns in a flat white wash (measured: 48% of glyphs rendered pure white). src/hud/GlowEffect.ts replaces it: only real glyphs emit (it reads buffers.char and skips space/unset), and the glow lands on neighbours' backgrounds, tinted toward the emitter's foreground, never on foregrounds — so glyph colours survive exactly, which is what "outer glow" actually means.
  • Terminal cells are ~2:1 (tall:wide). GlowEffect's kernel spans radius * CELL_ASPECT cells horizontally (CELL_ASPECT = 2), or every halo renders as a vertical smear.
  • The glow is O(w·h·r²). That is why it is toggleable with b and radius stays pinned at 2 — the first thing to turn off if a terminal chugs.

Verifying changes without a TTY

scripts/preview.tsx renders one frame into the test harness so you can iterate on layout and geometry without launching the full app. It always uses the fixtures, so no token or network is involved.

bun run scripts/preview.tsx --theme rapture --size 140x44
bun run scripts/preview.tsx --theme tactical  --size 140x44 --keys tab,j,j   # drive to 3rd issue
bun run scripts/preview.tsx --theme rapture --size 140x44 --spans          # palette histogram
bun run scripts/preview.tsx --theme rapture --size 90x30                   # narrow: rail drops

The flags:

  • --theme <rapture|tactical> and --size WxH (default 140x44).
  • --keys a,b,c drives the app through a comma-separated key sequence before the frame is captured, exactly as a user would type it. Friendly names (tab, up, pageup, …) map to their escape sequences; single letters (j, t, …) pass straight through — so --keys tab,j,j lands on the third issue and --keys t swaps to TACTICAL.
  • --spans prints a foreground-color histogram instead of the character grid: every distinct color, how many visible (non-space) cells it paints, and its share of the frame. --spans-top N widens how many colors are listed before the tail is folded into one line.

captureCharFrame() — what preview prints by default — is monochrome: it proves layout (that panels tile, that the sparkline fits its box, that text does not overflow) but says nothing about color, which is the whole point of this package. --spans is the only way to weigh the palette from the CLI — to answer "is red actually rare? does amber dominate?" — and the rendered frame in a real terminal is the final arbiter.

One harness subtlety worth knowing: settleFrame() yields two macrotasks before drawing, not one. The first lets React flush the commit queued by the key handler; but React 19 flushes passive effects (useEffect) on a later task, and App installs the post-process chain from one — so a single yield would draw the new tree through the old FX chain (a one-key --keys b --spans reported the glow still on).

Keybindings

Every row is sourced from App.tsx's useKeyboard:

| Key | Action | | ---------------------- | --------------------------------- | | / k, / j | Move selection | | PageUp / PageDown | Jump 8 rows | | Tab | Switch PULLS ↔ ISSUES | | t | Cycle theme (all six) | | b | Toggle glow (the fx.glow token) | | s | Toggle scanlines | | f | Toggle glitch | | v | Toggle vignette | | r | Toggle the scrolling CRT bar | | q / Esc / Ctrl-C | Quit |

All effects are on by default. A toggle reads -- when the active theme declares no such effect — RAPTURE has no vignette, so v is inert there. Above 120 columns the footer names each key (R CRT-BAR:ON); below it, initials (R:ON).

What this is not

This is a theme playground, not production architecture. Nowhere is that clearer than src/github/client.ts: it is plain async/fetch, with Bun.spawn for gh auth token and Promise.allSettled across the two list calls so one disabled endpoint doesn't sink the whole feed — deliberately not Effect, even though the rest of Fold is. The point here is the pixels, and Effect would only add ceremony between you and the frame. The real Fold TUI will wire its data through Effect; do not cargo-cult this client into it.