{"id":50417700,"url":"https://github.com/cablehead/stacks2099","last_synced_at":"2026-05-31T07:01:05.533Z","repository":{"id":360272687,"uuid":"1249357229","full_name":"cablehead/stacks2099","owner":"cablehead","description":null,"archived":false,"fork":false,"pushed_at":"2026-05-25T18:07:11.000Z","size":4077,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-25T19:28:39.226Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cablehead.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-25T16:03:33.000Z","updated_at":"2026-05-25T18:07:15.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/cablehead/stacks2099","commit_stats":null,"previous_names":["cablehead/stacks2099"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/cablehead/stacks2099","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cablehead%2Fstacks2099","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cablehead%2Fstacks2099/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cablehead%2Fstacks2099/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cablehead%2Fstacks2099/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cablehead","download_url":"https://codeload.github.com/cablehead/stacks2099/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cablehead%2Fstacks2099/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33722156,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-31T02:00:06.040Z","response_time":95,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-05-31T07:01:04.777Z","updated_at":"2026-05-31T07:01:05.526Z","avatar_url":"https://github.com/cablehead.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# stacks2099\n\nStacks is a tool for thought: a collection of **stacks** of **clips** for\nmanaging your personal context. A **clip** is any byte sequence with a mime type\n-- a note, an image, a JSON blob, a screenshot, a README. Clips gather into\n**stacks**, one per task or train of thought.\n\nstacks2099 is that thesis, expanded. The original\n[stacks](https://github.com/cablehead/stacks) ran on a crude event stream and a\nthrowaway first-draft UI. Its event stream was spun out and matured into\n[cross.stream](https://github.com/cablehead/xs)\n([stacks#46](https://github.com/cablehead/stacks/issues/46)); the UI it was\nmissing arrived as [Datastar](https://data-star.dev)\n([stacks#58](https://github.com/cablehead/stacks/issues/58)). stacks2099 is\nstacks rebuilt on both.\n\nIt also widens what a clip can be. Besides notes and images, a clip can be a\nrunning terminal or an embedded URL, so a stack holds the working context itself\n-- the shells you're in, the site you're building. The page is a pure projection\nof the event log: selection, layout, and the visible HTML are computed on the\nserver and patched over Datastar SSE. Terminals included, rendered from\n[wezterm-term](https://github.com/wezterm/wezterm) as\n[an HTML grid (no WASM, no client-side VT emulator)](journey.md).\n\n\u003cimg alt=\"A terminal clip rendered as an HTML cell grid\" width=\"760\" src=\"docs/assets/terminal.png\"\u003e\n\n## Install\n\nEach release ships prebuilt binaries for macOS (Apple Silicon), Linux (x86_64),\nand Windows (x86_64).\n\n### [eget](https://github.com/zyedidia/eget)\n\nGrabs the right binary for your platform from the latest release:\n\n```bash\neget cablehead/stacks2099\n```\n\n### Homebrew (macOS)\n\n```bash\nbrew install cablehead/tap/stacks2099\n```\n\n### Direct download\n\nPick an archive from the\n[releases](https://github.com/cablehead/stacks2099/releases) page; the binary\nsits at the root.\n\n### cargo\n\nNot published to crates.io. Install from git, or build a checkout:\n\n```bash\ncargo install --git https://github.com/cablehead/stacks2099\n# or, in a clone:\ncargo build --release      # -\u003e target/release/stacks2099\n```\n\nThe binary is self-contained -- the app (Nushell handler, assets, fonts) and the\nNushell engine, store, and Datastar bundle are all baked in. There is no\nexternal `nu` to install and nothing fetched from a CDN at runtime.\n\n## Run\n\nYou choose where it listens (`ADDR`) and where its state lives (`--store`); both\nare required.\n\n```bash\nstacks2099 127.0.0.1:5099 --store ./store      # runs the app baked into the binary\n```\n\nFor development, `--dev` runs the app (`app/serve.nu` + `app/www`) straight from\nthe source tree with hot-reload -- edit the request closure, the `sessions.html`\ntemplate, CSS, or JS and refresh; no Rust rebuild:\n\n```bash\ncargo run -- --dev 127.0.0.1:5099 --store ./dev-store\n```\n\nOnly changing the Rust (the pty projection, new builtins) needs `cargo build`.\n\n## Model\n\nEverything -- clips, stacks, selection, the window title -- is frames in an\nappend-only [cross.stream](https://cross.stream) log. The page is a pure\nprojection of that log, so every client sees the same state and a restart\nreplays it. The UI is three columns: **stacks** | **clips** | **content** (the\nselected stack's clips, stacked top to bottom).\n\n- A **clip** is any byte sequence with a mime type, rendered by what it is: an\n  editable **note** (`text/*`), an inline **image** (`image/*`), a live\n  **terminal**, a live **embed** (a URL in an `\u003ciframe\u003e`), or -- for anything\n  else -- a read-only / downloadable preview. An unknown mime type still holds\n  and previews; rendering it nicely is a later add, not a prerequisite.\n- Some clips offer **alternate views**, toggled from a button on the pane: a\n  markdown note flips **edit ⇄ rendered**; a note whose body is a URL flips\n  **edit ⇄ embedded** (the live iframe). A `text/uri-list` clip starts embedded.\n- A **stack** groups clips into a context. The top-left **breadcrumb** names the\n  current stack and opens a switcher to jump between stacks or create one\n  (`Alt+\\`; `Alt+[` / `Alt+]` cycle). It stays reachable when the scrollable\n  layout hides the rail.\n- Clips order **`auto`** (by activity -- newest edits float up) or **`manual`**\n  (curated). The clips-header badge toggles the mode;\n  `Alt+Shift+J`/`Alt+Shift+K` move the selected clip down/up (the first move\n  freezes the current order into `manual`).\n- Each stack picks a **layout**: `flow` (a vertical column of panes) or `niri`\n  (a horizontal scrollable strip). The top-bar Layout button or `Alt+L` toggles\n  it.\n- **Terminal clips** bind to an embedded-Nushell pty. The binary re-execs itself\n  to run the shell, so there is no external `nu` to find, and placement survives\n  a restart -- the pty respawns where it was, zellij-style.\n\nThe top bar carries the cross-clip handles -- the stack breadcrumb, Sort,\nLayout, Theme, Actions, and New. The **Theme** button swaps the terminal palette\n(client-side: Default, Nord, Solarized, Railscasts, and friends).\n\n\u003cimg alt=\"Theme picker open beside a focused, themed terminal\" width=\"640\" src=\"docs/assets/theme.png\"\u003e\n\n## Add assets\n\nPaste an image into the page (`Cmd`/`Ctrl+V`) to drop it into the current stack.\nFrom the command line, POST any asset to the running server:\n\n```bash\n# mime from the Content-Type header; lands in the current stack; prints the clip id\ncurl --data-binary @diagram.png -H 'content-type: image/png' localhost:5099/clip/add\ncat notes.md | curl --data-binary @- -H 'content-type: text/markdown' localhost:5099/clip/add\ncurl --data-binary @logo.svg -H 'content-type: image/svg+xml' 'localhost:5099/clip/add?stack=design'\n```\n\nEmbed a live URL (e.g. a dev server you're watching) as an iframe clip:\n\n```bash\necho http://localhost:3000 | curl --data-binary @- -H 'content-type: text/uri-list' localhost:5099/clip/add\n```\n\nRe-post to an existing clip to replace its bytes -- its pane refreshes in place,\nso a regenerated asset updates live:\n\n```bash\ncurl --data-binary @diagram.png 'localhost:5099/clip/update?clip=\u003cid\u003e'\n```\n\n`?stack=` takes a stack id or name; omit it for the current stack. `/api/state`\nlists the stacks (ids, names, clip counts) for scripting.\n\n## API and events\n\nThe HTTP routes are thin wrappers: each appends a frame to the log (selection\naside -- that rides the in-process bus). The store serves an `xs` API on its\nsocket, so you can write the same frames yourself; the API is just sugar over\nthe event log.\n\n```bash\n# add a markdown clip via the HTTP API\ncurl --data-binary @notes.md -H 'content-type: text/markdown' localhost:5099/clip/add\n\n# the identical effect, appended straight to the log with the xs client\n# (STACK_ID from `curl localhost:5099/api/state`):\ncat notes.md | xs append ./store clip.add --ttl forever \\\n  --meta '{\"stack_id\":\"STACK_ID\",\"kind\":\"content\",\"mime_type\":\"text/markdown\"}'\n```\n\nIn a store-connected Nushell (`xs` gives you one) the builtin form is\n`\u003cbody\u003e | .append \u003ctopic\u003e --meta {...}` -- exactly what each route runs.\n\n| Action          | HTTP                          | Frame appended                                   |\n| --------------- | ----------------------------- | ------------------------------------------------ |\n| New stack       | `POST /stack/new`             | `stack.add {sort}`                               |\n| Rename stack    | `POST /stack/rename`          | `stack.update {id, name}`                        |\n| Delete stack    | `POST /stack/close?stack=`    | `stack.delete {id}`                              |\n| Add clip        | `POST /clip/add`              | `clip.add {stack_id, kind, mime_type}` + body    |\n| Update clip     | `POST /clip/update?clip=`     | `clip.update {id}` + body                        |\n| Rename clip     | `POST /pty/label`             | `clip.patch {id, label}`                         |\n| Set view        | `POST /clip/view?clip=\u0026view=` | `clip.patch {id, view}`                          |\n| Close clip      | `POST /clip/close?clip=`      | `clip.delete {id}`                               |\n| Move clip       | `POST /clip/move?dir=\u0026clip=`  | `clip.patch {id, position}` (renumber on freeze) |\n| Toggle sort     | `POST /stack/sort?stack=`     | `stack.update {id, sort}`                        |\n| Toggle layout   | `POST /stack/layout?stack=`   | `stack.update {id, layout}`                      |\n| Select / switch | `POST /nav`, `/stack/select`  | bus `clip.select` / `stack.select`               |\n\nThe topics and fields _are_ the protocol -- defined in `app/projection.nu`.\nTerminal clips are the exception: their pty is spawned by a `POST` to\n`/clip/new?type=terminal`, so create those through the API.\n\n## Keys\n\nTwo modes. **Navigate** browses (read-only, dimmed); **focus** drives the\nselected pane (a terminal gets your keystrokes, a note opens its editor).\n`mod+Enter` toggles between them. App chords are `Alt`-prefixed and fire in\nevery mode; plain `Enter`/`Esc` go to the focused pty. See\n[docs/adr/0004-keyspace.md](docs/adr/0004-keyspace.md).\n\n| Chord                         | Action                                 |\n| ----------------------------- | -------------------------------------- |\n| `mod+Enter`                   | Toggle focus (Cmd on macOS, Ctrl else) |\n| `mod+K`                       | Clip actions (rename / close / move)   |\n| `Alt+T`                       | New clip (note / terminal picker)      |\n| `Alt+D`                       | Close current clip                     |\n| `Alt+J` / `Alt+K`             | Next / previous clip                   |\n| `Alt+Shift+J` / `Alt+Shift+K` | Move clip down / up                    |\n| `Alt+\\`                       | Switch stack (open the switcher)       |\n| `Alt+[` / `Alt+]`             | Previous / next stack                  |\n| `Alt+S`                       | Toggle stack sort (auto / manual)      |\n| `Alt+L`                       | Toggle stack layout (flow / niri)      |\n| `Alt+R`                       | Rename current clip                    |\n| `Alt+Shift+R`                 | Rename window title                    |\n| `Alt+O`                       | Cycle current terminal pane height     |\n\nThe top bar carries the same actions as clickable handles (breadcrumb, Sort,\nLayout, Theme, Actions, New); the status bar lists the active mode's chords.\n\n## Built on\n\nA fork of [http-nu](https://github.com/cablehead/http-nu) -- one self-contained\nbinary embedding the [Nushell](https://www.nushell.sh) engine, the\n[cross.stream](https://github.com/cablehead/xs) (`xs`) event log, and the\n[Datastar](https://data-star.dev) bundle. Clips and request handlers are\nNushell; the app leans on a handful of builtins:\n\n- `pty open` / `pty view` -- terminals, modelled by\n  [wezterm-term](https://github.com/wezterm/wezterm) and projected to an HTML\n  cell grid.\n- `.cat` / `.append` / `.cas` -- the `xs` event log this fork is built around.\n  `.bus` (pub/sub) is http-nu's own in-process event bus, not xs.\n- `.mj` ([minijinja](https://github.com/mitsuhiko/minijinja)) for templates,\n  `.highlight` ([syntect](https://github.com/trishume/syntect)) for syntax\n  highlighting, `.md`\n  ([pulldown-cmark](https://github.com/pulldown-cmark/pulldown-cmark)) for\n  markdown.\n\n## Status\n\nExperimental, moving fast. Interfaces and the event protocol are not stable.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcablehead%2Fstacks2099","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcablehead%2Fstacks2099","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcablehead%2Fstacks2099/lists"}