<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
	<channel>
		<title>Tui on Phosphor BBS</title>
		<link>https://phosphorbbs.net/tags/tui/</link>
		<description>Recent content in Tui on Phosphor BBS</description>
		<generator>Hugo</generator>
		<language>en-us</language>
		
		
		
		
			<lastBuildDate>Tue, 20 Oct 2026 00:00:00 +0000</lastBuildDate>
		
			<atom:link href="https://phosphorbbs.net/tags/tui/index.xml" rel="self" type="application/rss+xml" />
			<item>
				<title>The Case of the Vanishing Blink</title>
				<link>https://phosphorbbs.net/blog/the-case-of-the-vanishing-blink/</link>
				<pubDate>Tue, 20 Oct 2026 00:00:00 +0000</pubDate>
				<guid>https://phosphorbbs.net/blog/the-case-of-the-vanishing-blink/</guid>
				<description>&lt;p&gt;For years, picking an item on the Phosphor main menu did a small, satisfying thing: the row flashed — inverted video for about 150 milliseconds — and then the next screen took over. It&amp;rsquo;s a 65-byte courtesy. Two tiny frames: highlight the row, unhighlight the row, go.&lt;/p&gt;&#xA;&lt;p&gt;Then one day the flash was just gone in Terminal.app&amp;rsquo;s telnet client. Not intermittently. Gone. MuffinTerm still showed it. The web client showed it, but only one flash instead of two. Three different clients, three different answers, one unchanged server. By the time we sat down to hunt it, every standard explanation had already been eliminated: the code was untouched since the feature shipped, the server was provably running the current build, the user&amp;rsquo;s preferences and theme tested innocent, and the same binary rendered the blink perfectly against a test instance. Same client, same server, same account. Different port. Blink on one, gone on the other.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Synchronized Updates and Frame Coalescing: How Phosphor Paints Atomic Screens</title>
				<link>https://phosphorbbs.net/blog/synchronized-updates-and-frame-coalescing/</link>
				<pubDate>Mon, 19 Oct 2026 00:00:00 +0000</pubDate>
				<guid>https://phosphorbbs.net/blog/synchronized-updates-and-frame-coalescing/</guid>
				<description>&lt;p&gt;A BBS screen is a grid of characters, and for as long as character grids have flown over dial-up lines and TCP sockets alike, they have had one chronic way of embarrassing themselves: tearing. A full-screen repaint travels as a stream of cursor moves and color changes, and a client on a slow link — or a busy one — can catch the middle of it. You&amp;rsquo;ve seen the result: half the old screen with half the new one written over it, a status bar flashing the wrong colors for a tenth of a second, a table redrawn one row at a time like a document slowly printing.&lt;/p&gt;</description>
			</item>
			<item>
				<title>The Case of the Invisible Popups</title>
				<link>https://phosphorbbs.net/blog/the-case-of-the-invisible-popups/</link>
				<pubDate>Mon, 05 Oct 2026 00:00:00 +0000</pubDate>
				<guid>https://phosphorbbs.net/blog/the-case-of-the-invisible-popups/</guid>
				<description>&lt;p&gt;The session log screen has two popups: [H] on a row pops up that connection&amp;rsquo;s full history, and Enter pops up the session&amp;rsquo;s details. The SysOp account-review screen pops up a confirmation with the temp password when you approve a request. File lists pop up a preview panel. All four had been built the same way — a &lt;code&gt;Window&lt;/code&gt;, a hint list, a &lt;code&gt;Label&lt;/code&gt; shoved into the contents panel — and all four now shared the same bug report: &lt;em&gt;the popup doesn&amp;rsquo;t appear.&lt;/em&gt;&lt;/p&gt;</description>
			</item>
			<item>
				<title>Markdown in a Terminal: Building a MarkdownWidget for jterm</title>
				<link>https://phosphorbbs.net/blog/markdown-in-a-terminal-building-the-markdownwidget/</link>
				<pubDate>Wed, 23 Sep 2026 00:00:00 +0000</pubDate>
				<guid>https://phosphorbbs.net/blog/markdown-in-a-terminal-building-the-markdownwidget/</guid>
				<description>&lt;p&gt;Every BBS has the same problem: text that wants to be formatted. Help screens with headings and bullet lists. News articles with paragraphs. Mail that would really use a code block for that error dump. The traditional answers are all bad in the same way — hard-coded ANSI sequences baked into strings, a private wiki-syntax nobody else can read, or walls of plain text with no structure at all.&lt;/p&gt;&#xA;&lt;p&gt;jterm now ships a third answer: a &lt;strong&gt;MarkdownWidget&lt;/strong&gt; that parses real markdown and renders it as styled terminal lines.&lt;/p&gt;</description>
			</item>
			<item>
				<title>The Case of the Blended Batches: A Race Hidden Inside a Replace</title>
				<link>https://phosphorbbs.net/blog/the-case-of-the-blended-batches/</link>
				<pubDate>Wed, 23 Sep 2026 00:00:00 +0000</pubDate>
				<guid>https://phosphorbbs.net/blog/the-case-of-the-blended-batches/</guid>
				<description>&lt;p&gt;The jterm thread-safety suite exists to answer one question: &lt;em&gt;can a widget survive being redrawn from one thread while another mutates it?&lt;/em&gt; It has run thousands of times across dozens of widgets, mostly to say yes. On the morning we cut the 0.1.1 release, it said no — and the test that failed wasn&amp;rsquo;t even the one that was broken.&lt;/p&gt;&#xA;&lt;h2 id=&#34;two-failures-two-days-apart&#34;&gt;Two failures, two days apart&lt;/h2&gt;&#xA;&lt;p&gt;The release gate runs the whole suite, 2,926 tests. The first full run failed one test: &lt;code&gt;FireworksTest.renderFrameProducesOutput&lt;/code&gt; — the fireworks animation was supposed to paint visible pixels after five frames and, once out of 2,926, it painted nothing. We re-ran the class in isolation: 12/12 green. Read the render path: fresh instance, deterministic math, five calls that each draw a rocket or particles. The failure looked like noise under load — the kind of flake you shrug at and re-run.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Chat From Anywhere: The Ctrl-T Popup</title>
				<link>https://phosphorbbs.net/blog/chat-anywhere-with-the-ctrl-t-popup/</link>
				<pubDate>Sun, 20 Sep 2026 00:00:00 +0000</pubDate>
				<guid>https://phosphorbbs.net/blog/chat-anywhere-with-the-ctrl-t-popup/</guid>
				<description>&lt;p&gt;On a traditional BBS, chat is a &lt;em&gt;place&lt;/em&gt;. You navigate to the chat section, you sit there, you talk. When you want to check your mail you leave chat — and the conversation — behind. That&amp;rsquo;s fine when chat is an event you schedule. It&amp;rsquo;s terrible when it&amp;rsquo;s ambient, the way a community actually works: someone drops a line into the channel while you&amp;rsquo;re reading the board, and you want to answer without losing your place.&lt;/p&gt;</description>
			</item>
			<item>
				<title>The Case of the Very Random Dead Ctrl-T</title>
				<link>https://phosphorbbs.net/blog/the-case-of-the-very-random-dead-ctrl-t/</link>
				<pubDate>Sun, 20 Sep 2026 00:00:00 +0000</pubDate>
				<guid>https://phosphorbbs.net/blog/the-case-of-the-very-random-dead-ctrl-t/</guid>
				<description>&lt;p&gt;Two weeks into the popup chat feature, users started reporting the same thing in different words: &lt;em&gt;Ctrl-T is very random.&lt;/em&gt; Sometimes it opened the chat popup instantly. Sometimes it did nothing — not even an error. Same terminal, same screen, same key. First press of the session would work, three presses in a row would work, then a press after closing the popup with ESC would go dead. Then it would work again. There was no pattern, which was the most maddening part — a consistently broken key is a bug report; an inconsistently broken key is a haunting.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Blackjack in Phosphor: A Door Game as a Language Test</title>
				<link>https://phosphorbbs.net/blog/blackjack-in-phosphor-a-door-game-as-a-language-test/</link>
				<pubDate>Sat, 19 Sep 2026 00:00:00 +0000</pubDate>
				<guid>https://phosphorbbs.net/blog/blackjack-in-phosphor-a-door-game-as-a-language-test/</guid>
				<description>&lt;p&gt;Every language looks fine in its own test suite. The tests were written&#xA;by the same people who wrote the language, with the same blind spots.&#xA;The only honest benchmark is a program someone actually wants to run.&lt;/p&gt;&#xA;&lt;p&gt;So when the question came up — &amp;ldquo;would it be easy to create a simple&#xA;blackjack game for the BBS?&amp;rdquo; — the answer was: yes, and that&amp;rsquo;s exactly&#xA;the test Phosphor needs. Blackjack is a sweet spot for a scripting&#xA;language&amp;rsquo;s first real workload. It has genuine state (a shuffled deck,&#xA;two hands, a bankroll), real control flow (a dealer AI that hits to&#xA;17, ace soft/hard counting), string wrangling (card glyphs), user&#xA;input, and persistence. If the parser, the evaluator, and the door&#xA;bridge can carry all of that, the language is real. If they can&amp;rsquo;t, we&#xA;find out where.&lt;/p&gt;</description>
			</item>
			<item>
				<title>The Screen That Lied: Building Idle Timers That Actually Tick</title>
				<link>https://phosphorbbs.net/blog/idle-timers-that-actually-tick-window-auto-refresh/</link>
				<pubDate>Fri, 04 Sep 2026 00:00:00 +0000</pubDate>
				<guid>https://phosphorbbs.net/blog/idle-timers-that-actually-tick-window-auto-refresh/</guid>
				<description>&lt;p&gt;Here&amp;rsquo;s a bug report you can&amp;rsquo;t reproduce with a test: &lt;em&gt;&amp;ldquo;the sessions view shows a list of users logged in, and when they change state Connected → disconnected the display updates immediately — but the idle time doesn&amp;rsquo;t update. If I quit the window and reopen it, the idle times are current again.&amp;rdquo;&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;The sysop was right, and the cause was architectural. Every screen in &lt;a href=&#34;https://phosphorbbs.net&#34;&gt;Phosphor BBS&lt;/a&gt; is drawn by our own terminal UI toolkit, jterm — a pure-Java, lanterna-style TUI framework with double-buffered, diff-based refresh. That diff-based refresh is the crown jewel: when you redraw, you only pay for the cells that changed. But it had a blind spot.&lt;/p&gt;</description>
			</item>
	</channel>
</rss>
