Skip to content

prd: CMS blog operations — build, serve, open, deploy, theme #5

@leandronsp

Description

@leandronsp

Date: 2026-04-11
Status: Draft

Problem

The DevTUI CMS screen lets you write and manage articles, but every operation on the blog itself lives outside the TUI. To build, serve, open in a browser, deploy, or switch themes, you drop to a terminal and run make blog.build.<name>, make blog.serve.<name>, make deploy.cp.<name>, or hand-edit blog.toml. That context switch breaks the flow DevTUI is trying to become: a k9s-style CMS where everything about a blog happens in one screen.

Background

DevTUI is evolving into a full TUI blog CMS. The article list (src/editor/list.rs) is already the home screen in CMS mode, with publish, pin, delete, search, and edit. What's missing is the surrounding workflow. A writer finishing a post today has to quit the TUI, remember the blog name, type a Makefile target, wait, open a browser, and repeat for deploy. The engine already exposes devtui::engine::build::build() as a direct Rust call. The editor already imports and calls engine functions for sync and publish. Wiring blog operations into the list screen is a small step with a large UX payoff, and it keeps parity with the Curupira CMS we're replacing.

Requirements

Must Have

  • Build the current blog from the list screen with a single keystroke. Show progress (building, done, error) in the footer. No terminal drop.
  • Serve the current blog locally with a single keystroke. The TUI builds first, then starts a local HTTP server on a known port, and shows the URL in the footer. A second keystroke stops the server. If the user edits a post and serves again, the blog rebuilds automatically before restart.
  • Open the served blog in the default browser with a single keystroke. If the server is not running, start it first.
  • Deploy the current blog with a single keystroke. Deploy means whatever the blog's infrastructure is configured to do (today: rsync to the blog repo via deploy.cp.<name>). Show a confirmation before deploying. Show success or failure in the footer.
  • Choose the blog theme from a picker showing available themes (paper, newspaper, terminal). Selecting a theme writes it to blog.toml. If the blog is currently being served, rebuild and restart serve so the change is visible immediately.
  • Status indicator in the header showing serve state: stopped, building, serving on :PORT. The user always knows where they stand.
  • Error surfaces: build and deploy failures show the first line of the error in the footer, with a keystroke to open a scrollable error overlay with the full output.
  • Last-build timestamp in the header so the user knows if a rebuild is needed before deploying.
  • Footer help bar shows the new keybindings alongside existing ones. Current blog name visible in the header.

Should Have

  • (none for v1 — all promoted to Must Have)

Out of Scope

  • Multi-blog switcher inside the list screen. Today a blog is chosen at launch via --cms <blog_dir>. Separate PRD.
  • Deploy target configuration UI. Deploy uses whatever is already configured for the blog.
  • Remote preview deploys or staging environments. Local serve and production deploy only.
  • Theme editing. Only selecting an existing theme.

Constraints

  • The editor today does not shell out to make or cargo. All engine work is direct Rust calls. Build and serve should follow the same pattern: call devtui::engine::build::build() directly, run the HTTP server in a background thread. Deploy is the one exception — rsync is an external tool, so deploy will shell out.
  • The TUI event loop must stay responsive during long operations. Builds and deploys run on a background thread and post status updates to the main thread via a channel.
  • Theme changes must not corrupt blog.toml. The write preserves all other fields and comments where possible.
  • Keybindings must not collide with existing ones (j/k/G/g, Enter, n, p, i, d, /, ?, q). New actions need fresh keys from what's left.

Acceptance Criteria

Build

  • Given I'm on the CMS list screen, when I press the build key, then the footer shows "building..." and within a few seconds shows "built in 1.2s" or the first line of the error.
  • Given a build is already running, when I press the build key again, then it does nothing and the footer keeps showing progress.
  • Given a build fails, when I press the error overlay key, then I see the full error output in a scrollable panel.

Serve

  • Given the server is stopped, when I press the serve key, then the blog builds, the server starts on a free port, and the footer shows "serving on http://localhost:PORT".
  • Given the server is running, when I press the serve key again, then the server stops and the footer shows "stopped".
  • Given the server is running and I edit and save a post, when I press the serve key or trigger a rebuild, then the blog rebuilds and the running server picks up the new files without restart or with a clean restart.
  • Given I quit the TUI while the server is running, when the TUI exits, then the server shuts down cleanly.

Open in Browser

  • Given the server is running, when I press the open key, then the default browser opens to the served URL.
  • Given the server is stopped, when I press the open key, then the TUI starts the server and then opens the browser.

Deploy

  • Given I press the deploy key, when the confirmation modal appears, then I can confirm or cancel.
  • Given I confirm deploy, when deploy runs, then the footer shows "deploying..." and then "deployed" or an error.
  • Given deploy fails, when I open the error overlay, then I see the rsync output.

Theme

  • Given I press the theme key, when the theme picker opens, then I see the list of available themes with the current one highlighted.
  • Given I select a new theme, when I confirm, then blog.toml is updated and the footer shows "theme changed to ".
  • Given the server is running when I change the theme, when the change is applied, then the blog rebuilds and the serve refreshes so the new theme is visible in the browser.
  • Given I cancel the theme picker, when it closes, then blog.toml is unchanged.

Header status

  • Given the server is stopped, when I look at the header, then I see "stopped".
  • Given a build is running, when I look at the header, then I see "building...".
  • Given the server is running on port 8000, when I look at the header, then I see "serving :8000".
  • Given a build just finished, when I look at the header, then I see the last-build timestamp (e.g. "built 12s ago" or "built 14:32").

Error overlay

  • Given a build or deploy failed, when I press the error overlay key, then a scrollable panel shows the full stderr output.
  • Given the overlay is open, when I press Esc, then it closes and I'm back on the list.

Header and Footer

  • Given I'm on the list screen, when I look at the header, then I see the current blog name and the current serve state.
  • Given I'm on the list screen, when I look at the footer, then I see all existing keybindings plus the new ones for build, serve, open, deploy, and theme.

Metadata

Metadata

Assignees

No one assigned

    Labels

    prdProduct Requirements Document

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions