---
title: Changelog
url: 'https://wiki.teletypegames.org/engines/rubbs/changelog'
markdown: 'https://wiki.teletypegames.org/engines/rubbs/changelog.md'
lang: en
date: '2026-10-29'
description: 'The version history of rubbs, newest first, from the git history of engines/rubbs and the version bumps in bbs.gemspec. Each entry links to the manual page that documents the feature. Boards pin rubbs by tag or git revision in their Gemfile.lock; the version below is the s.version of the gemspec a…'
taxonomy:
  tag:
    - reference
---

# Changelog

> The version history of [rubbs](https://wiki.teletypegames.org/engines/rubbs), newest first, from the git history of `engines/rubbs` and the version bumps in `bbs.gemspec`. Each entry links to the manual page that documents the feature. Boards pin rubbs by tag or git revision in their `Gemfile.lock`; the version below is the `s.version` of the gemspec at that commit.

| Version | Date | In one line |
|---|---|---|
| 1.1.0 | 2026-11-02 | `Editor`/`Screen#edit` `autosave:` seconds and a block (off by default, keeps the idle limit itself); `Selector` `item_exits:` and `Screen#pick` `item_commands:` act on the lit item |
| 1.0.0 | 2026-11-02 | The cleanup release: TUI, `BBS::Color` and unused widgets and Flow steps gone; one palette, one Selector, `say(…, color:)`, `edit`, `BBS.log`, `BBS.background`, `BBS::Wikipedia`, the gateway calls public addresses only |
| 0.34.0 | 2026-11-02 | `Gateway.new(translate: true)`: a C64 can call an ASCII/ANSI board |
| 0.33.0 | 2026-11-01 | Hardening (per-address limit, control-free text, capped telnet, bounded AI calls), `BBS::SQLite` and `BBS::Store` on it, `BBS::HTTP`, `Cache`, `RateLimit`, `Text`, `Address` |
| 0.32.0 | 2026-11-01 | `BBS::Selector` `fill:` keeps the menu's height when the items take fewer rows |
| 0.31.0 | 2026-11-01 | `BBS::Gateway::Report` for each call, `connect_timeout:` |
| 0.30.0 | 2026-11-01 | [Art and graphics screens](https://wiki.teletypegames.org/engines/rubbs/art): `Petscii::Canvas`, `Art`, `Petscii::CanvasMenu`, `Widgets::Scene`, `Season`, `art-preview` |
| 0.20.0 | 2026-10-29 | `Terminal.flow(petscii:)` in place of `c64:`, the face is `:petscii` |
| 0.19.0 | 2026-10-28 | `config.theme`, `on_idle`, per-session `idle_seconds` and `Panel` layout work |
| 0.18.2 | 2026-10-28 | Full RDoc, releases by tag, the API reference on the wiki |
| 0.18.1 | 2026-10-28 | (tag only; its RDoc upload failed, see 0.18.2) |
| 0.18.0 | 2026-10-28 | `BBS_MAX_SESSIONS`, a log line for a turned-away caller |
| 0.17.1 | 2026-10-27 | Fixes found while writing the manual |
| 0.17.0 | 2026-10-27 | `BBS::Gateway`: a telnet gateway door to another board |
| 0.16.1 | 2026-10-27 | `Terminal.flow` switches the mouse on only with `config.mouse` |
| 0.16.0 | 2026-10-27 | `BBS::Terminal`: the modern-or-C64 question |
| 0.15.0 | 2026-10-25 | PETSCII: a Commodore 64 on the line |
| 0.14.0 | 2026-10-22 | Switchable windows |
| 0.13.0 | 2026-10-22 | `on_tick`, `on_door`, hidden status hints |
| 0.12.0 | 2026-10-22 | Moving and sizing windows |
| 0.11.0 | 2026-10-22 | `AnsiArt.view` |
| 0.10.0 | 2026-10-22 | Maximizable windows |
| 0.9.0 | 2026-10-22 | ZMODEM downloads, `with_line`; an audit |
| 0.8.0 | 2026-10-21 | AI facts, fallback, deflect |
| 0.7.0 | 2026-10-21 | Flow doors, a flow's own screens, SAUCE |
| 0.6.0 | 2026-10-20 | `BBS::Door` |
| 0.5.0 | 2026-10-20 | `BBS::AI` |
| 0.4.0 | 2026-05-12 | The Application layer (and everything up to 2026-10-20) |
| 0.3.0 | 2026-05-11 | TUI page navigation |
| 0.2.0 | 2026-05-11 | Page-based TUI DSL |
| 0.1.0 | 2026-04-28 | Flow, Telnet, Store, extracted from the Impostor BBS |

# 1.0.0 — 2026-11-02

The cleanup release: one way to do each thing. Most boards change only three things (the first three below); `UPGRADING.md` in the repository lists every removed or renamed API with its replacement and a grep that finds it.

- **`Petscii::Screen#say` and `#rule` take the colour as a keyword**: `say(text, color: GN)`, `rule(color: DG)`.
- **`Screen#editor` is gone**: `edit(title:, max:)`, which returns `nil` when the caller quits. **`Petscii::Screen::MENU` is gone**: `BBS::Selector::COLORS` is the default.
- **The gateway calls only public addresses**: a board that dials its neighbours (`host.docker.internal`, a container) says `public_only: false`; a refused call ends with `BBS::Gateway::Refused` ([Doors](https://wiki.teletypegames.org/engines/rubbs/doors)).
- **Removed**: `BBS::TUI` and the Flow's `tui` step, `BBS::Color`, the widgets `ProgressBar`, `Spinner`, `HBox`, `VBox`, `Panel` and `AnsiArt` (use `Art` + `Widgets::Scene`), the Flow steps `set`, `line`, `section`, `rows`, `table`, `wait_enter`, `fetch`, `pick`, `body`, `output`, `os` (use `call`), the colour-named Flow styles, `Menubar#add_menu`, the public `Terminal.choose` (use `Terminal.flow`), `Petscii::Screen.plain` (`BBS::Petscii.plain`), `Dialogs.wrap` (`FrameBuffer.wrap_ansi`).
- **One of each**: one palette (`BBS::Ansi`; `Petscii::Colors` are its C64 names), one cursor menu (a Flow's `menu` is a `BBS::Selector` now), one word-wrap, one PETSCII glyph table, one telnet parser (`BBS::Telnet::Parser`). Flow steps are objects; a `call` block's runner answers `context`, `ask`, `select` and `line` ([Flow](https://wiki.teletypegames.org/engines/rubbs/flow)).
- **Themes as data**: `Theme::PRESETS`, `Theme.fetch(name)`, frozen themes, an unknown role raises ([Drawing](https://wiki.teletypegames.org/engines/rubbs/drawing)).
- **New**: `BBS.log` and `config.log_tag` (every log line of the library starts with the board's name), `BBS.background` (work in a thread with retry), `BBS::Wikipedia`, `RateLimit.allow_all?`, `room?`/`record` and counters in SQLite, `Text.line`, `HTTP.get(to: io)` and `HTTP::TooLarge`, `Cache#refresh`, broken CSV rows set aside on import, `Store#open!`, and the error rule: repositories raise, services decide ([Data and network](https://wiki.teletypegames.org/engines/rubbs/data-and-network)).
- `Text.clean(keep_newlines: false)` turns a line end into a space instead of gluing the words; `Telnet#settle` returns the session or `nil`.

# 0.34.0 — 2026-11-02

- **`BBS::Gateway.new(translate: true)`**: a Commodore 64 can call a board made for ASCII and ANSI terminals; the board's text reaches it as PETSCII, its keys reach the board as an ANSI terminal's ([Doors](https://wiki.teletypegames.org/engines/rubbs/doors)).

# 0.33.0 — 2026-11-01

Hardening, and the blocks every content board used to write for itself ([Data and network](https://wiki.teletypegames.org/engines/rubbs/data-and-network)).

- **Limits**: `max_sessions_per_ip` (6) per public address; NAWS at most 255×100; input and the frame buffer keep no control characters; a text field holds at most 64 KiB; gateway subnegotiations and ZMODEM restarts capped; SIGTERM stops the server like Ctrl-C.
- **AI**: a deadline (120 s) and a reply cap per call, at most `ai_concurrency` (2) requests at once, `BBS::AI::Busy` and the persona's fallback when the model is taken ([AI characters](https://wiki.teletypegames.org/engines/rubbs/ai)).
- **`BBS::SQLite`** with migrations, and **`BBS::Store` on it**: a `.csv` path means the `.sqlite3` beside it, the old CSV is imported once ([Telnet and the store](https://wiki.teletypegames.org/engines/rubbs/telnet-and-store)).
- **`BBS::HTTP`**, **`BBS::Cache`**, **`BBS::RateLimit`**, **`BBS::Text`**, **`BBS::Address`**. Ruby 3.2 or newer; `sqlite3` is a dependency.

# 0.32.0 — 2026-11-01

- **`BBS::Selector`** with `fill:` keeps its height when the items take fewer rows, so a status row stays at the bottom of the screen.

# 0.31.0 — 2026-11-01

- **`BBS::Gateway`** hands its block a `Report` of the call (connected, error, address, connect time, bytes received, who hung up) and takes `connect_timeout:` ([Doors](https://wiki.teletypegames.org/engines/rubbs/doors)).

# 0.30.0 — 2026-11-01

The shared building blocks of the TTG Clubhouse look: pictures drawn as text, for both faces. Everything is new; nothing existing changes behaviour. (The versions between 0.20.0 and 0.30.0 are in the git history of `engines/rubbs`.)

- **`BBS::Petscii::Canvas`**: the C64 screen as cells in the uppercase/graphics set, raw PETSCII out (whole frame, spans, patches, `update`), moved here from TTG Gate BBS with the same bytes; `feed` plays PETSCII back for tests.
- **`BBS::Art`**: drawings as YAML (chars, colours, reverse, cp437 backgrounds, hotspots, layers), out to ANSI, a FrameBuffer or a Canvas, with errors that name the cell.
- **`BBS::Petscii::CanvasMenu`**: `BBS::Selector` drawn on a Canvas, patches of the changed cells, an idle hook for animation.
- **`BBS::Widgets::Scene`**, **`BBS::Season`** and the **`art-preview`** tool. See [Art and graphics screens](https://wiki.teletypegames.org/engines/rubbs/art).

# 0.20.0 — 2026-10-29

The PETSCII face is named after the line it speaks, not after one machine that speaks it. This breaks the call of every two-faced board; ours all changed with it.

- **`BBS::Terminal.flow(title:, modern:, petscii:)`** takes the PETSCII face's Flow under `petscii:` (was `c64:`), and **`Terminal.guess` / `Terminal.choose`** answer `:modern` or `:petscii` (was `:c64`); `Terminal::FACES` has the same keys ([Commodore](https://wiki.teletypegames.org/engines/rubbs/commodore)).
- The boards follow the same name: their faces are `UI::Modern` and `UI::Petscii` (`lib/ui/petscii`), the Impostor flow is `Impostor::Modern` / `Impostor::Petscii`.

# 0.19.0 — 2026-10-28

Four things that were documented but did nothing now work. No board of ours used them, so none changes behaviour.

- **`config.theme`** is the theme of every Application without a `theme` call of its own, and the base of a `theme do … end` block given none ([Getting started](https://wiki.teletypegames.org/engines/rubbs/getting-started)).
- **`on_idle { … }`** runs when the caller reaches the idle limit; `:keep` keeps them on, anything else drops the line ([Application](https://wiki.teletypegames.org/engines/rubbs/application)).
- **`Session#idle_seconds`** is the limit the Application loop and the TUI read, and setting it changes the socket timeout too: one caller can get more time ([Telnet, session and store](https://wiki.teletypegames.org/engines/rubbs/telnet-and-store)).
- **`Widgets::Panel#layout`** gives its children the area inside the border ([Widgets](https://wiki.teletypegames.org/engines/rubbs/widgets)).

# 0.18.2 — 2026-10-28

- **The whole API is documented** in the source (`rdoc -C lib`: 100%), and the RDoc is on the wiki: [API reference](https://wiki.teletypegames.org/rdoc/bbs/latest/), all versions on [RDoc](https://wiki.teletypegames.org/development/rdoc). CI fails below 100%, and the repo's `CLAUDE.md` makes it the rule.
- **Releases are tags** from now on: `vX.Y.Z`, matching the gemspec; earlier versions were tagged retroactively (`v0.1.0` … `v0.18.0`).
- Comments that disagreed with the code now say what it does: the `AnsiArt` options (`fill_parent`, not `width`) and SAUCE stripping (everything from the first `\x1A`), the five theme presets, and three settings that nothing reads yet: `Config#theme`, `Application.new(on_idle:)` and `Session#idle_seconds`.
- 0.18.1 has the same code; its tag pipeline could not install RDoc on the slim image, so 0.18.2 is the first published reference.

# 0.18.0 — 2026-10-28

- **`max_sessions`** can be set from the environment: `BBS_MAX_SESSIONS` (default 100), so a deployment sizes it to the container's memory without touching the board's code ([Telnet, session and store](https://wiki.teletypegames.org/engines/rubbs/telnet-and-store)).
- A caller turned away at the limit is logged: `BBS busy: N callers on, turned one away`.

# 0.17.1 — 2026-10-27

Fixes found while writing this manual.

- **`open_door` works from any handler**: it stops the event loop itself, so a window button (`Info`, `Form`, `MasterDetail` actions) or a `MasterDetail` `on_activate` leaves through the door without a following `stop!`. **`Application#line_lost?`** tells a run that ended because the line did (a hang-up, the idle limit); `Builder#run` opens no door after it, where a door picked just before a hang-up used to open. See [Application](https://wiki.teletypegames.org/engines/rubbs/application) and [Doors](https://wiki.teletypegames.org/engines/rubbs/doors).
- The Flow **`app` step** runs the application through `Builder#run`, so its doors work there too ([Flow](https://wiki.teletypegames.org/engines/rubbs/flow)).
- **`theme :name`** without a block takes that preset (it used to store the Symbol and crash the first redraw), and `theme` without arguments returns the theme set so far instead of resetting it ([Drawing](https://wiki.teletypegames.org/engines/rubbs/drawing)).
- **`ask ... validate:`** takes a Proc or Method as well; a falsy answer fails like `:non_empty` ([Flow](https://wiki.teletypegames.org/engines/rubbs/flow)).
- A menu item's **`enabled:`** may be a lambda, asked on the application each time the menu is drawn or used; `Menubar::MenuItem#enabled?` is the answer ([Application](https://wiki.teletypegames.org/engines/rubbs/application)).
- **`Store#upsert`** ignores fields that are not headers on update too; it used to append them as an extra column ([Telnet, session and store](https://wiki.teletypegames.org/engines/rubbs/telnet-and-store)).
- **`Session#resize!(cols, rows)`**: the gateway hands the caller's resize to this board's session as well, so the board has the new size after the crossing ([Doors](https://wiki.teletypegames.org/engines/rubbs/doors), [Telnet, session and store](https://wiki.teletypegames.org/engines/rubbs/telnet-and-store)).
- The README's Gemfile line points at git.teletypegames.org.

# 0.17.0 — 2026-10-27

- **`BBS::Gateway.new(host:, port:, name:)`**: a door to another telnet board, the telnet gateway of the old BBSes. The caller's line goes on to that board as it is and comes back when it hangs up, so a board can offer another one without running its code. See [Doors](https://wiki.teletypegames.org/engines/rubbs/doors).
- Towards the other board the gateway is the telnet client: it answers the negotiation, reports the caller's window size and passes resizes on. A PETSCII line reports none, so a two-faced board on the other side takes the caller for a C64 ([Commodore 64](https://wiki.teletypegames.org/engines/rubbs/commodore)).
- **`session.line`** exposes the caller's socket, for a door that hands the line on ([Telnet, session and store](https://wiki.teletypegames.org/engines/rubbs/telnet-and-store)).
- The caller's side wakes the other side with `shutdown`, never `close`: a thread killed halfway through `IO#close` leaves the descriptor open.

The Teletype BBS reaches Alexandria BBS and Feedia through it.

# 0.16 — 2026-10-27

## 0.16.1

- `Terminal.flow` calls `enable_mouse` after a modern pick only when the board has `config.mouse` on. Before, a two-faced board without a mouse (Impostor BBS) still had reporting switched on for a modern caller. See [Commodore 64](https://wiki.teletypegames.org/engines/rubbs/commodore).

## 0.16.0

- **`BBS::Terminal`**: the first question of a board with a modern and a C64 face, moved into the gem from the three boards that each had their own copy. See [Commodore 64](https://wiki.teletypegames.org/engines/rubbs/commodore). 
    - `Terminal.guess(session)` guesses from the telnet answers: wider than 40 columns is modern, 40 or no window size at all is a C64.
    - `Terminal.choose(session, title:)` asks in plain ASCII capitals and names the protocols: `1 - MODERN (ANSI/XTERM, 256 COLORS)`, `2 - COMMODORE 64 (PETSCII, 40 COLUMNS)`. A key picks, RETURN takes it (the input of WikiProxy), and 60 seconds of silence takes the pick or the guess.
    - `Terminal.flow(title:, modern:, c64:)` asks, switches the line (`petscii!` or mouse reporting) and runs the chosen face's Flow.

Used by the Teletype BBS, Impostor BBS, TTG WIKI BBS, Alexandria BBS and Feedia.

# 0.15 — 2026-10-25

- **PETSCII**: `session.petscii!` turns a session into a C64's: 40×25, the lowercase character set, no mouse. See [Commodore 64](https://wiki.teletypegames.org/engines/rubbs/commodore).
- Everything written then goes through `BBS::Petscii::Encoder`, so a board keeps writing UTF-8 with ANSI colours and cursor moves: letters swap case the PETSCII way, box drawing and blocks become the C64's glyphs, ANSI colours its colours, CR LF one RETURN, and a line of exactly 40 characters gets no empty line after it.
- `readkey` and `readline` read the C64's keys; RUN/STOP and `←` are `:escape`.
- **`session.settle(timeout:)`** waits for the telnet answers and drops the typeahead, so a board can guess the terminal before it asks ([Telnet, session and store](https://wiki.teletypegames.org/engines/rubbs/telnet-and-store)).
- **`config.mouse_on_connect`** (default `true`) lets the flow switch mouse reporting on later, after a question a C64 must not see an escape sequence before.

# 0.14 — 2026-10-22

- **Switchable windows**, Turbo Vision style: several windows open at once cover each other in the order they were last used. A click on a window behind the active one brings it to the front, F6 / Shift-F6 step through them, Alt-1 … Alt-9 picks one by the number on its frame. The active window has a double frame. See [Windows](https://wiki.teletypegames.org/engines/rubbs/windows).
- Modal windows (the `Dialogs`) keep the front until they close; the `BBS::Windows` types are no longer modal.
- F6 moves a window only while it is the only one; Ctrl-F5 always does.
- `app.raise_window`, `cycle_windows`, `switchable_windows` do it from code ([Application](https://wiki.teletypegames.org/engines/rubbs/application)).

# 0.13 — 2026-10-22

- **`on_tick { … }`** in the Application DSL runs once a second while the caller is idle (a statusbar clock). **`on_door { |door, context| … }`** runs just before the line goes to a door. See [Application](https://wiki.teletypegames.org/engines/rubbs/application).
- **`status_hint(key, label, hidden: true)`** binds a key without taking room on the statusbar.
- Fixes: Enter in `Dialogs.input`'s field presses OK; a door that raises brings the caller back to the board instead of ending the call; an indented line under a Markdown list item continues the item.

# 0.12 — 2026-10-22

- **Moving and sizing windows**: a window that is not maximized can be moved, a resizable one sized. The mouse drags the title bar or the bottom right corner (`┘`); from the keyboard, Ctrl-F5 or F6 starts it, the arrows move, Shift+arrows size, Tab switches, Enter or Esc ends. `movable: false` turns it off; `move_to(x, y, w, h)` from code. See [Windows](https://wiki.teletypegames.org/engines/rubbs/windows).
- Mouse reporting is xterm 1002 now (motion while a button is held), so drags arrive.
- **The telnet reader returns xterm's modifiers**: `:shift_up`, `:ctrl_f5`, `:alt_home`, `:ctrl_shift_right` ([Telnet, session and store](https://wiki.teletypegames.org/engines/rubbs/telnet-and-store)). A window hands a modified arrow nothing takes to its widget as the plain key.

# 0.11 — 2026-10-22

- **`AnsiArt.view(session, lines, title:)`**: art on the whole screen, centred, scrolled with the arrows, PgUp/PgDn and Home/End when taller than the screen; a status line with the title; any other key returns. It runs inside `Application#with_line`. See [Widgets](https://wiki.teletypegames.org/engines/rubbs/widgets).

# 0.10 — 2026-10-22

- **Maximizable windows**: `maximizable: true` (`w.maximizable true` in the window DSL) gives a window a zoom gadget: `[↑]` fills the desktop between menubar and statusbar, `[↕]` restores it; F5, Alt-Enter and a double click on the title bar do the same. Off by default. See [Windows](https://wiki.teletypegames.org/engines/rubbs/windows).
- Windows that can lay out at any size say `resizable?` and implement `normal_rect` and `place(x, y, w, h)`: `MasterDetail`, `Stream`, `Form` and a scrolling `Info` do. `Stream`, `Form` and `Info` now follow a terminal resize.
- `Application#desktop_rect`; `ESC CR` reads as `:alt_enter`.

# 0.9 — 2026-10-22

- **ZMODEM downloads**: `BBS::Zmodem.send_files` sends files to the caller's terminal (SyncTERM, NetRunner, Qodem, `rz` behind telnet, zmodem.js in a web terminal). Control characters are ZDLE-escaped and 0xFF goes as a doubled telnet IAC. See [Doors](https://wiki.teletypegames.org/engines/rubbs/doors).
- **`Application#with_line { |session| … }`** hands the raw line to a block and repaints afterwards; `Telnet#read_raw_byte` reads for it.
- `MasterDetail` takes `on_activate` (Enter on an item) and `replace_items`, so a window can browse folders in place ([Windows](https://wiki.teletypegames.org/engines/rubbs/windows)).
- An audit of the whole gem, among others: 
    - NAWS is clamped (one 65535×65535 report made every render allocate gigabytes), subnegotiations and lines are capped;
    - sockets get the idle limit as read/write timeout, connections are capped (`max_sessions`), `EMFILE` no longer spins the accept loop;
    - `readline` returns UTF-8, Backspace removes a whole character, `readkey` reads UTF-8 characters;
    - CR LF is one Enter, a lone Esc no longer waits for the next key, SS3 arrows and X10 mouse reports no longer leak as keys;
    - Esc and function keys get past a `TextInput`; button hotkeys work; `on_close` runs once;
    - narrow terminals, a wheel event over a window's padding, a handler's exception and a FakeOS command's exception no longer end the call.

See [Telnet, session and store](https://wiki.teletypegames.org/engines/rubbs/telnet-and-store) for the limits.

# 0.8 — 2026-10-21

- **`Chat#reply(text, ctx, facts:, fallback:)`**: facts go into the system prompt for one request, and `fallback` replaces the persona's canned reply when the model is missing or fails. A persona's **`deflect`** rules are answered by the code before the model is asked. See [AI](https://wiki.teletypegames.org/engines/rubbs/ai).
- `TextInput#value=` puts the cursor at the end of the new value: the second line typed into a `Stream` window used to raise `IndexError` and hang the caller up.

# 0.7 — 2026-10-21

- **Flow doors**: a door may be a `BBS::Flow`. It runs on a copy of the caller's context and asks for ENTER before handing the line back. See [Doors](https://wiki.teletypegames.org/engines/rubbs/doors).
- **`Flow.define(screens_dir:)`** keeps a flow's ERB screens next to it, so a flow shipped in a gem renders inside any board; `FlowRunner` takes `context:` to seed the flow's context ([Flow](https://wiki.teletypegames.org/engines/rubbs/flow)).
- **FakeOS `base` colour**: every reset returns to it and `cls` fills the screen with it, so a coloured background (a C64's blue) survives prompts and new lines.
- **`AnsiArt.sauce(path)`** reads the SAUCE record of an art file ([Widgets](https://wiki.teletypegames.org/engines/rubbs/widgets)).

# 0.6 — 2026-10-20

- **`BBS::Door`**: hand the line to a program and take it back. Declared with `door :name, program, label:`, listed in a menu with `doors`, opened from any handler with `open_door`. `Builder#run` runs the door the application was left through and builds the application again, with the same context. The program is a FakeOS definition or a `callable(session, context)`. See [Doors](https://wiki.teletypegames.org/engines/rubbs/doors).

# 0.5 — 2026-10-20

- **`BBS::AI`**: characters a caller can talk to. A persona from a YAML file (system prompt, example turns, request options, canned replies), a streaming client for any OpenAI-compatible chat endpoint, a chat that keeps a short history in the caller's context and falls back to the canned replies, and a typewriter that word-wraps the streamed reply onto a FakeOS console. See [AI](https://wiki.teletypegames.org/engines/rubbs/ai).

# 0.4 — 2026-05-12

**0.4.0** brought the third layer, the widget framework (the commit "new framework"):

- **`BBS::Application`** with an event loop, `Event`, `FocusManager`, a menubar, a statusbar, `Dialogs`, `Window`, `Container` and the widgets (`Label`, `Button`, `TextInput`, `TextArea`, `Checkbox`, `ListBox`, `ScrollView`, `ProgressBar`, `Spinner`). See [Application](https://wiki.teletypegames.org/engines/rubbs/application) and [Widgets](https://wiki.teletypegames.org/engines/rubbs/widgets).
- **`BBS::Theme`** presets; xterm mouse reporting (1000 + SGR 1006) and the key decoder in the telnet reader.
- `BBS::Store` got its in-memory cache and Mutex: new rows are appended, updates rewrite the file ([Telnet, session and store](https://wiki.teletypegames.org/engines/rubbs/telnet-and-store)).

The version stayed 0.4.0 until 2026-10-20, while these landed:

- 2026-05-12 – 05-13: `Widgets::AnsiArt` (CP437 art), the theme DSL and more presets, the Markdown renderer, the pre-built window types (`Info`, `Form`, `MasterDetail`, `Stream`, `ButtonBar`) and the window DSL (`window :name, type:`). See [Windows](https://wiki.teletypegames.org/engines/rubbs/windows) and [Drawing](https://wiki.teletypegames.org/engines/rubbs/drawing).
- 2026-06-05: GFM tables in Markdown; mouse-wheel scrolling in `ScrollView`. **`BBS::FakeOS`**, a character-mode OS simulator as configuration ([Doors](https://wiki.teletypegames.org/engines/rubbs/doors)).
- 2026-06-11: open windows re-lay out on a terminal resize.
- 2026-08-17: a CI check; 2026-09-09: the MIT license.
- 2026-10-16: `Store` survives invalid UTF-8 in the CSV and cleans values before writing them.
- 2026-10-19: a client hang-up is logged as a dropped session, not an error.
- 2026-10-20: `readline(mask:)` for passwords, and `naws_reported?`.

# 0.3 — 2026-05-11

- A `nav` DSL in `TUI::Page`; `helpers` accumulates across calls. (Removed in 1.0.0.)

# 0.2 — 2026-05-11

- **The page-based TUI DSL** (`BBS::TUI.define`, pages, a router), on the TUI runner added the same day, with NAWS terminal-size negotiation and sizes that follow it. (Removed in 1.0.0.)
- Followed on 2026-05-12 by the double-buffered `FrameBuffer` with delta rendering ([Drawing](https://wiki.teletypegames.org/engines/rubbs/drawing)).

# 0.1 — 2026-04-28

- **The first version**, extracted from the Impostor BBS's `gems/bbs`: `BBS::Flow` and `FlowRunner` (screen, banner, ask, say, menu, persist, …), ERB screens (`Renderer`), `Banner`, `BBS::Store`, the threaded `Server`, `Session` and the telnet layer. See [Getting started](https://wiki.teletypegames.org/engines/rubbs/getting-started) and [Flow](https://wiki.teletypegames.org/engines/rubbs/flow).
- 2026-04-28 – 04-30, still 0.1.0: the `call` step and `config.on_session_end`; the Flow primitives `line`, `section`, `text`, `rows`, `table`, `wait_enter`, `fetch`, `pick`, `body`, `output`; `BBS::Color` with the `c()` helper.

# See also

- [rubbs](https://wiki.teletypegames.org/engines/rubbs) — the overview
- [Getting started](https://wiki.teletypegames.org/engines/rubbs/getting-started)
- [Commodore 64](https://wiki.teletypegames.org/engines/rubbs/commodore)
- [Telnet, session and store](https://wiki.teletypegames.org/engines/rubbs/telnet-and-store)
- [Data and network](https://wiki.teletypegames.org/engines/rubbs/data-and-network)

'; var prevLevel = 2; for (var i = 0; i < headings.length; i++) { var h = headings\[i\]; var text = h.textContent.trim(); var slug = text.toLowerCase() .replace(/\[áàâä\]/g,'a').replace(/\[éèêë\]/g,'e').replace(/\[íìîï\]/g,'i') .replace(/\[óòôöő\]/g,'o').replace(/\[úùûüű\]/g,'u') .replace(/\[^a-z0-9\]+/g, '-').replace(/^-|-$/g, ''); if (!slug) slug = 'heading-' + i; if (used\[slug\]) { slug += '-' + i; } used\[slug\] = true; h.id = slug; var level = parseInt(h.tagName.charAt(1)); var cls = level > 2 ? ' class="toc-child"' : ''; html += '- [' + text + '](#'%20+%20slug%20+%20')
'; } html += '
'; toc.innerHTML = html; // Scroll-spy: highlight current heading var links = toc.querySelectorAll('a'); var ids = \[\]; for (var j = 0; j < links.length; j++) ids.push(links\[j\].getAttribute('href').slice(1)); function updateActive() { var current = ''; for (var k = 0; k < ids.length; k++) { var el = document.getElementById(ids\[k\]); if (el && el.getBoundingClientRect().top <= 80) current = ids\[k\]; } for (var m = 0; m < links.length; m++) { links\[m\].parentNode.classList.toggle('active', links\[m\].getAttribute('href') === '#' + current); } } window.addEventListener('scroll', updateActive, { passive: true }); updateActive(); })();

---

## Navigation

- Parent: [RuBBS](https://wiki.teletypegames.org/engines/rubbs.md)
- Previous: [Art and graphics screens](https://wiki.teletypegames.org/engines/rubbs/art.md)
- Next: [Commodore 64](https://wiki.teletypegames.org/engines/rubbs/commodore.md)
