Skip to content

@mspec/web-ui — Web UI Reference

@mspec/web-ui is a React SPA dashboard that surfaces every change, artifact, spec, and test result produced by the mspec workflow. It is served automatically by @mspec/cli whenever you run mspec new or mspec continue in a project.


Table of Contents

  1. Installation
  2. Starting the server
  3. Configuration
  4. Pages and routes
  5. Theme system
  6. Markdown rendering pipeline
  7. Step progress states
  8. Change modes
  9. API endpoints
  10. Data types
  11. Auto-refresh behavior

Installation

@mspec/web-ui is an optional peer dependency of @mspec/cli. It is installed automatically when you install the CLI:

sh
npm install -g @mspec/cli

The CLI locates the built static assets via:

js
require.resolve('@mspec/web-ui/dist/index.html')

To install the package on its own:

sh
npm install @mspec/web-ui

Starting the server

The web UI server starts automatically when you run any workflow command:

sh
mspec new my-feature
mspec continue

The server binds to http://localhost:3847 by default. The port is printed to stdout on startup.


Configuration

Port is configurable in .mspec/config.yaml:

yaml
ui:
  port: 3847  # default

Pages and routes

Dashboard /

The main landing page. Lists all changes in the project, sorted by most-recently-updated.

Layout — two-column grid (260 px sidebar + fluid main area)

SectionControls
StatusToggle buttons: In progress, Ready to read, Shipped, Archived — each shows a live count
ModeToggle buttons: All, Full, Bugfix, Minor, Typo — each shows a live count
NavigateLink to Spec Viewer; Archived toggle with count

Clicking an active filter a second time deselects it and returns to the "all" view. Status and mode filters compose: both must match for a row to appear.

Status definitions:

LabelCondition
In progressNot archived and at least one step is not done
Ready to readIn progress and currentStep is spec or plan
ShippedNot archived and every step is done
ArchivedisArchived === true

App bar

  • Left: /dashboard breadcrumb
  • Right: search input (searches name, title, summary, tags) + ThemePicker

Change list rows

Each row is a three-column grid:

ColumnContents
Main infoSerif title (or slug if no separate title), monospace slug (when title differs), optional archived badge, optional summary line, mode chip, counts (N reqs · N scenarios · N artifacts), relative timestamp
Current stepLabel of currentStep (e.g., Impl, Test)
Step progressColor-coded bar per step — see Step progress states

Relative timestamp format: just now / Nm ago / Nh ago / Nd ago / Mon D (>7 days).

The count in the top-left of the main area shows N of M (filtered vs. total) and a StepLegend color key.


Change Detail /changes/:id

Displays all artifacts for a single change. The URL parameter :id is the change slug (e.g., 2026-05-24-130128-mspec-web-ui).

Header row

  • Back button → Dashboard
  • Change ID in monospace
  • Link to Test Results for this change
  • ThemePicker

Two-panel layout (active only when an artifact is selected)

When no artifact is selected the sidebar occupies the full width. Once a file is clicked the layout switches to a 280 px sidebar + fluid viewer grid.

Artifact list sidebar

Files are grouped by the artifact list returned by the API. Each entry shows:

  • Filename (monospace, clickable)
  • type label in muted text (e.g., markdown, html)
  • Doc-type color coding via a left border on the list item:
docTypeLeft border color
ReferenceBlue
ExplanationPurple
How-toGreen
TutorialYellow
(none / other)Gray

Clicking a file that is already selected closes the viewer (toggle behavior).

Inline viewer panel

The right panel renders the selected artifact. For .html files a sandboxed <iframe> is used (prototype viewer). All other files are rendered as markdown — see Markdown rendering pipeline.

A close button () in the top-right of the panel collapses back to sidebar-only view.


Artifact Preview /changes/:id/artifacts/*

A full-screen standalone viewer for a single artifact. Navigated to by constructing the URL directly; there is no in-app link to this page from the dashboard flow.

Header row

  • Back button → Change Detail for the same :id
  • Relative path of the artifact in monospace
  • ThemePicker

The artifact is rendered using the same ArtifactViewer component as the inline panel in Change Detail.


Spec Viewer /spec-viewer

Renders the source-of-truth spec files from the project's .mspec/specs/ directory.

Header row

  • Back button → Dashboard
  • Title: "Spec Viewer"
  • ThemePicker

Left sidebar (240 px)

Lists all spec capabilities returned by the API under the heading "Capabilities". Each entry is a monospace link that sets the active capability.

Main area

Displays the rendered markdown of the selected capability's spec. Uses the same markdown rendering pipeline as the artifact viewer (GFM, Mermaid, syntax highlighting, EARS/Gherkin keyword highlighting).

When no capability is selected the sidebar spans the full width and a prompt instructs the user to select a capability.

Route: /spec-viewer/:capability — the :capability segment maps directly to the spec slug returned by GET /api/specs.


Test Results /changes/:id/test-results

Displays parsed test output for a change. Supports both Playwright JSON and JUnit XML formats.

Header row

  • Back button → Change Detail for the same :id
  • Title: "Test Results"
  • ThemePicker

Test case list

All tests across all suites are flattened into a single sorted list: failures first, then passes and skips.

Each row shows:

ElementDetail
Status badgePASS (green), FAIL (red), SKIP (gray) in monospace
Test namePlain text
DurationRight-aligned, in milliseconds

Failed tests are clickable to expand a detail panel showing:

  • errorMessage in red text
  • stackTrace in a scrollable <pre> block with surface background

Theme system

Four themes are available. The active theme is applied by setting data-theme on <html> and is persisted to localStorage under the key mspec-ui-store (Zustand persist middleware).

ThemeDescriptionBackground
lightDefault warm white#fbfaf7
sepiaWarm parchment#f4ead5
greenSoft botanical#dee9d3
darkDark reading surface#15151a

The ThemePicker component renders as a radio group in every page header. Each option shows a small color swatch.

CSS custom properties

All themes define the same set of tokens:

TokenPurpose
--bgPage background
--paperCard / reader surface (slightly elevated from bg)
--panelSide panels, code block backgrounds
--inkPrimary foreground text
--ink-softSecondary text (labels, timestamps)
--ink-muteTertiary text, divider labels
--ruleHairline borders
--rule-softLighter hairline / hover background
--accentLinks, active indicators
--accent-2Prose link color
--accent-softTinted hover / selected surface
--selText selection highlight

Status color tokens used by StepProgress:

TokenState
--doneStep completed
--readyStep active / in progress
--blockedStep cannot start yet
--skippedStep intentionally omitted
--invalidStep has an error

EARS/Gherkin keyword color tokens: --k-shall, --k-must, --k-should, --k-may, --k-given, --k-when, --k-then, --k-and, --k-but.


Markdown rendering pipeline

All markdown artifacts and spec files are rendered with the following plugins applied in order:

StagePluginEffect
remarkremark-gfmGitHub Flavored Markdown (tables, strikethrough, task lists)
rehyperehype-rawPass raw HTML nodes through (enables <!-- comment --> nodes)
rehyperehypeCommentDimWraps HTML comment nodes in <span class="md-comment"> (dimmed, italic)
rehyperehypeGherkinEarsWraps EARS / Gherkin keywords in colored <span> elements
rehyperehypeInlineCodePropertyMarks inline <code> nodes so the code renderer can distinguish them from fenced blocks

Code blocks are routed by language tag:

  • ```mermaidMermaidRenderer component (renders diagram via the Mermaid library)
  • All other named languages → CodeBlock component (syntax highlighting via Shiki / react-shiki)
  • Inline code → plain <code> element

HTML artifacts (.html extension) bypass the markdown pipeline entirely and are rendered inside a PrototypeIframe component (sandboxed <iframe>).

EARS / Gherkin keyword highlighting

Keywords are highlighted in prose text outside of <code> and <pre> blocks:

Keyword classKeywords
.k-shallSHALL
.k-mustMUST
.k-must-notMUST NOT
.k-shouldSHOULD
.k-should-notSHOULD NOT
.k-mayMAY
.k-givenGIVEN
.k-whenWHEN
.k-thenTHEN
.k-andAND
.k-butBUT

Each class receives a bold weight and a theme-aware colored background pill (via color-mix).

HTML comment dimming

HTML comments in markdown source (<!-- … -->) are rendered as dimmed, italic text at opacity: var(--comment-opacity). The opacity value varies by theme (0.42 light, 0.40 sepia/green, 0.50 dark).


Step progress states

The StepProgress component renders one color bar per workflow step. The color is sourced from a CSS custom property named after the state:

StateCSS varVisualMeaning
done--doneGreenStep completed successfully
ready--readyBlue, pulsingStep is the active next step
blocked--blockedGray, 60% opacityWaiting on a prerequisite
skipped--skippedYellowStep was intentionally skipped
invalid--invalidRedStep has a validation error

Steps use the canonical mspec step IDs: discover, spec, plan, impl, test, docs, ship.

Human-readable step labels used in the UI:

IDLabel
discoverDiscover
specSpec
planPlan
implImpl
testTest
docsDocs
shipShip

A StepLegend component showing all five states with labels is rendered in the Dashboard header row.


Change modes

The mode field on a change controls which workflow steps are required. The UI renders the mode as a pill chip (ModeChip).

Mode valueDisplay labelDot color
fullFull-flowBlue
bugfixBugfixOrange-red
minorMinorGreen
typoTypoYellow

API endpoints

The API is served by @mspec/cli (Fastify) at http://localhost:3847. All endpoints are under /api.

GET /api/health

Health check. Returns 200 OK when the server is running.


GET /api/changes

List all non-archived changes.

Query parameters

ParameterTypeDefaultDescription
includeArchivedtrue | falsefalseInclude archived changes in the response

ResponseChangeInfo[] (see Data types)


GET /api/changes/:id/artifacts

List artifacts for a change.

Path parameters

ParameterDescription
:idChange slug

ResponseArtifactFile[]


GET /api/changes/:id/artifacts/*path

Get the raw content of an artifact file. The wildcard *path is the relative path of the file within the change directory.

Response — Raw text (markdown source, HTML, etc.)


GET /api/specs

List all spec capabilities.

ResponseSpecCapability[]


GET /api/specs/:capability

Get the markdown content of a spec.

Path parameters

ParameterDescription
:capabilitySpec capability slug

Response — Raw markdown text


GET /api/changes/:id/test-results

Get parsed test results for a change. The server parses vitest JSON output and JUnit XML.

ResponseTestSuite[]


Data types

The following TypeScript interfaces describe the shapes returned by the API (defined in src/api/client.ts).

ChangeInfo

ts
interface ChangeInfo {
  id: string;
  name: string;
  title?: string;
  summary?: string;
  author?: string;
  createdAt: string;          // ISO 8601
  updatedAt?: string;         // ISO 8601
  mode: 'typo' | 'minor' | 'bugfix' | 'full';
  currentStep: string;        // e.g. "impl"
  steps: StepState[];
  isArchived: boolean;
  counts?: {
    reqs: number;
    scenarios: number;
    artifacts: number;
  };
  tags?: string[];
}

StepState

ts
interface StepState {
  id: string;                 // e.g. "impl"
  state: 'done' | 'ready' | 'blocked' | 'skipped' | 'invalid';
}

ArtifactFile

ts
interface ArtifactFile {
  name: string;               // display filename
  relativePath: string;       // path used in artifact content requests
  type: 'markdown' | 'html' | 'json' | 'xml' | 'other';
  docType?: 'Reference' | 'Explanation' | 'How-to' | 'Tutorial';
}

SpecCapability

ts
interface SpecCapability {
  capability: string;         // slug, e.g. "change-dashboard"
}

TestCase

ts
interface TestCase {
  name: string;
  status: 'pass' | 'fail' | 'skip';
  duration: number;           // milliseconds
  errorMessage?: string;
  stackTrace?: string;
}

TestSuite

ts
interface TestSuite {
  suiteName: string;
  format: 'playwright-json' | 'junit-xml';
  tests: TestCase[];
}

Auto-refresh behavior

The Dashboard and Change Detail pages poll the API automatically while the tab is open:

HookEndpointInterval
useChangesGET /api/changes2 000 ms
useChangeGET /api/changes/:id2 000 ms

All other data (artifacts, spec content, test results) is fetched once on mount and does not auto-refresh. Manually reload the page or navigate away and back to refresh those queries.


Typography

The UI uses three font stacks loaded from Google Fonts:

ClassFontsUsed for
.serifSource Serif 4, Noto Serif JP, GeorgiaArticle titles, prose content, change titles in rows
.sansGeist, Noto Sans JP, system-uiBody default, navigation, labels
.monoJetBrains Mono, ui-monospace, MenloChange slugs, step IDs, counts, code, timestamps