Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
231 changes: 231 additions & 0 deletions docs/design/0160-static-title-screen.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
---
title: "DX-0160 - Static title screen only"
legend: "DX"
lane: "design"
issue: "https://github.com/flyingrobots/jedit/issues/338"
status: "active"
owners:
- "@flyingrobots"
created: "2026-10-14"
updated: "2026-10-14"
---

# DX-0160 - Static title screen only

## Linked Issue

- https://github.com/flyingrobots/jedit/issues/338

## Decision Summary

Remove the ray tracer from the live title-screen integration. The static Jim
logo is the only no-buffer workspace body. Former camera, shader, material,
mouse-look, and scene-picker actions cannot switch it to an animated scene.
Standalone preview, profile, and recording tools retain their existing engine.

## Sponsored Human

A keyboard user wants to use the title screen without accidentally activating
ray tracing by pressing Space or moving the pointer.

## Sponsored Agent

An agent needs a production module graph and runtime witnesses proving that
ray tracing is unreachable from the workspace.

## Hill

Former title-scene input leaves the static logo intact, schedules no scene
work, and does not create camera state or reenable idle animation.

## Current Truth

Space is interpreted as camera jump, and any camera update selects the legacy
ray-traced backdrop. Ctrl+L, shader keys, and mouse look can also activate it.
The renderer accepts legacy scene state and injected ray-tracer renderers.
See [title-screen-key-bindings.ts#154:ecefbbb695ddeedaed94e0046c580e2041cca40f](https://github.com/flyingrobots/jedit/blob/ecefbbb695ddeedaed94e0046c580e2041cca40f/src/app/workspace/title-screen-key-bindings.ts#L154)
and [viewer-content.ts#135:ecefbbb695ddeedaed94e0046c580e2041cca40f](https://github.com/flyingrobots/jedit/blob/ecefbbb695ddeedaed94e0046c580e2041cca40f/src/app/workspace/viewer-content.ts#L135).

## Problem

Ordinary input can unexpectedly replace the static logo and start ray tracing.
Changing one shortcut leaves other activation paths reachable.

## Scope

- Remove live ray-tracer rendering, caches, controls, scene picker, and title-only stats.
- Remove scene/camera state, messages, loader composition, and advertised bindings.
- Preserve the static logo, file browsing, Settings, editor, quit flow, and generic instrumentation.

## Non-Goals

- Delete or redesign the standalone engine, scene assets, preview/profile/record tools.
- Change text authority, Leader settings, or implement leader dispatch.

## User Experience / Product Shape

The no-buffer view always shows the existing static Jim logo. Space, WASD,
arrow-camera controls, number/shader keys, material keys, and mouse movement
perform no title-scene action. Ctrl+L no longer opens a scene picker. Existing
file, Settings, command-line, and quit actions retain their focus ownership.
Help and footer stop advertising scene selection. There is no new layout,
transition, input capture, or persistent preference.

### User Journey

Start Jim → see static logo → open Settings or a file, or quit. Former scene
input cannot change that view. Closing the last buffer returns to the logo.

### Wide UI Mockup

Retain the existing logo at 100×32; no visual redesign. The footer lists only
currently available workspace actions. Actual rendered output is verified.

### Narrow UI Mockup

Retain the existing logo and drawer behavior at 60×16. Below the existing
60×12 minimum, the small-terminal notice retains precedence.

### Accessibility Considerations

Remove obsolete help. Existing text labels, theme, keyboard navigation,
Settings, file-drawer focus, and quit confirmation remain available.

## Runtime / API Contract

The viewer no longer accepts or invokes a ray-tracer renderer. Workspace
initialization no longer builds scene/camera state. The runtime no longer
accepts scene-load results or camera animation messages. A stale external scene
message cannot mutate the workspace. Title Escape still opens quit confirmation.
Standalone renderer APIs remain separate from production workspace composition.

## Lower Modes

The static logo and ordinary help remain available in pipe/no-color rendering.
Small-terminal notice behavior remains unchanged. No optional scene loader,
mesh, camera, or ray tracer is required for startup or title input.

## Data / State Model

| Category | Description |
| --- | --- |
| Source of truth | Existing static artwork and workspace/editor state. |
| Derived state | Logo surface sized to the viewer viewport. |
| Removed state | Scene, meshes, camera, material/shader selection, scene-picker focus, and tracing caches. |
| Reset behavior | Return to the logo when no buffer is displayed. |
| Persistence | No configuration or Echo contract changes. |

## Accessibility Posture

No new interaction or color-only state. Escape, Settings, and file browsing
remain keyboard reachable. Mouse movement is inert on the logo.

## Localization / Directionality Posture

Remove obsolete scene-picker hints from help, footer, and catalogs. Retain
existing localized labels and LTR/RTL logo presentation. Regenerate catalogs.

## Agent Inspectability / Explainability Posture

Runtime tests compare rendered surfaces and emitted commands. A production
module-loading witness verifies that no ray-tracer renderer or scene loader is
reachable through the live workspace dependency graph.

## Linked Invariants

- Tests exercise runtime and rendered output, not design-document assertions.
- Preserve editor behavior and the Echo authority boundary.
- Use the existing `title-unfreeze` label under the
[freeze policy](../method/backlog/leash/title-scene-freeze.md). The human's
explicit removal request authorizes this narrow title integration change;
engine/asset polish remains frozen.

## Design Alternatives Considered

Only removing Space leaves mouse look, shader controls, and scene selection
reachable. Hiding the scene UI retains dormant production tracing and state.
Deleting the standalone engine expands the request beyond the live title screen.

## Decision

Remove the complete live integration and retain the isolated standalone tools.

## Implementation Slices

- [x] Slice 1: Remove live rendering and title-scene input paths.
- [x] Slice 2: Remove scene state/dependencies/help and verify preserved workspace flows.

## Tests To Write First

- [x] Former keyboard and mouse controls leave the logo unchanged and emit no scene work.
- [x] Startup and stale scene/camera messages cannot create ray-tracer state.
- [x] Logo remains idle and renders even when legacy properties are supplied.
- [x] Settings, file drawer, quit, minimum geometry, and standalone tools retain behavior.

## Acceptance Criteria

- [x] All live title ray-tracer paths are removed.
- [x] Real runtime/render/module-graph witnesses pass.
- [x] Obsolete integration tests are replaced by static-title regressions.
- [x] Full repository validation passes and CI is reported at the published head.

## Validation Plan

```bash
npm run check
npm run ci:verify-shards
```

## Playback / Witness

Run Jim at 100×32 and 60×16. Press Space, WASD, 1, 2, period, M, Ctrl+L,
and move the pointer. The logo remains. F2 opens Settings; Ctrl+B opens Files;
Escape opens quit confirmation. After startup completion, an idle time tick
retains the same model reference.

## Risks

The legacy title spec mixes removed scene behavior with current file-drawer
behavior. Remove only obsolete assertions and keep the live file/Settings/quit
witnesses. Standalone tools share the engine; do not delete their dependencies.

## Follow-On Debt

None introduced. Reintroducing a title ray tracer would require a separate
explicit product decision and new runtime integration.

## Retrospective

The live integration is removed, including title camera/shader/material input,
mouse look, scene picker, scene/camera state and messages, scene-loader
composition, renderer injection, caches/governor, and title-scene stats. The
static logo is the only no-buffer body. The standalone engine, assets, and
preview/profile/record tools are unchanged.

Five of the six new runtime/render/module-loading witnesses failed before
removal; the preserved workspace-action witness also passed on the baseline.
All six passed afterward. They cover former inputs, pointer movement, stale messages,
legacy property injection, preserved Settings/Files/quit actions, and the
production dependency graph. The real graph no longer loads `ui/title-screen`,
`ui/title-scene`, `app/title-camera-session`, or `adapters/title-scene-loader`.
Obsolete integration tests were retired; current workspace-flow and standalone
engine/tool tests remain. Unused old title-test helpers were removed.

`npm run check` passed: 830 JavaScript tests passed, 12 existing skips, 99 native
Rust tests passed, and no quality regressions. The final focused title tests
passed 25/25 after removing unused helpers. Test-shard coverage passed with
130 spec files. The test-file size ratchet was lowered to the new bounds for
workspace command-line, runtime, and title-screen specs.

Live PTY verification used the real native Echo host with an isolated WAL at
100×32 and 60×16. At each size, 17 former keyboard/mouse activation events
produced one unchanged rendered-cell digest, zero commands, and no scene or
camera properties. At 100×32 F2 opened/closed Settings and Ctrl+B opened Files.
At 60×16 Escape opened quit confirmation, and confirmation exited cleanly.
Rendered static-logo cells were visually inspected. No text mutation was needed
for this presentation change, and no Echo API or authority boundary changed.

PR: https://github.com/flyingrobots/jedit/pull/339

No follow-on work is required for the removal. The separate Leader-settings
PR #336 and its unresolved configuration-ownership question are unchanged.
2 changes: 1 addition & 1 deletion docs/topics/ui/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ Jim UI documents use three terminal width profiles:

| Page | Surface |
| --- | --- |
| [Title Screen](title-screen.md) | Startup scene, title browser, and first file-open path. |
| [Title Screen](title-screen.md) | Static logo, workspace actions, and first file-open path. |
| [Editor Chrome](editor-chrome.md) | Source viewport, gutter, status footer, dirty state, and cursor position. |
| [Settings Menu](settings-menu.md) | F2 settings drawer, keyboard controls, diagnostics, and change feedback. |
| [Command Line And Completions](command-line-and-completions.md) | Normal-mode `:` input, command help, file completion, and inline suggestions. |
Expand Down
78 changes: 42 additions & 36 deletions docs/topics/ui/title-screen.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
# Title Screen

The title screen is the first Jim surface when no file has been opened. It is
both a product identity surface and a usable file-opening surface.

<img src="./title-screen.svg" alt="Title screen layout across wide, narrow, and xs terminal profiles." />
The title screen is the first Jim surface when no file has been opened. It
shows the static Jim logo and ordinary workspace chrome.

## How To Open It

Expand All @@ -13,52 +11,60 @@ Start Jim without a file argument:
npm run dev
```

In an installed build, launch the Jim executable without a path. The title
screen remains active until a file or workspace entry is opened.
In a built checkout, use `npm start`. The no-buffer view remains until a file
is opened and returns when no buffer is displayed.

## What It Shows

The current title screen renders a ray-traced scene in terminal cells. The
scene is decorative, but it is still a live Jim screen and must obey the same
chrome rules as the editor:

- the active title appears in the top chrome;
- performance and scene details can appear in debug overlays;
- footer hints describe available navigation and command shortcuts;
- file opening should route through the same production text path as later
editor commands.
The body renders committed logo artwork sized to the available viewport. It
has no live ray tracing, scene selection, camera controls, shader/material
selection, mouse look, or title-scene performance panel. Startup does not load
meshes or a scene loader. Once startup completes, an idle tick preserves the
model reference and does not request another render.

The title renderer is intentionally separate from source editing. It must not
own text authority, file authority, or settings authority.
The title row, footer, file drawer, Settings, theme selection, and generic
performance instrumentation retain their existing behavior. The title renderer
has no text authority. File opening uses the production Echo-backed text session.

## Keyboard Use

Common title-screen actions:

| Key | Action |
| --- | --- |
| `:` | Open Normal-mode command line. |
| `ctrl+b` | Open the files drawer. |
| `ctrl+g` | Open the Graft drawer when available. |
| `ctrl+t` | Cycle theme. |
| `F2` | Open settings. |

The exact footer text is the runtime hint source. If this page and the footer
disagree, the footer is more likely to match the running build.
| `:` | Open the command line. |
| `Ctrl+B` | Toggle the files drawer. |
| `Ctrl+G` | Toggle the Graft drawer when available. |
| `Ctrl+T` | Cycle theme. |
| `F2` | Toggle Settings. |
| `Esc` or `q` | Open quit confirmation. |

Space, WASD, arrow keys, `1`, `2`, `.`, `m`, and `M` have no camera or shader
meaning on the title screen. `Ctrl+L` no longer opens a scene picker. Pointer
movement does not change the logo. Focused drawers and the command line retain
ownership of their own keys.

## File Opening

From the title screen, `:edit <path>` and file completion should use the same
workspace file-opening behavior as the files drawer. Selecting a directory must
descend into that directory. Selecting a file must open it through the
production Echo-backed text session.
`:edit <path>` and file completion use the same workspace file-opening behavior
as the files drawer. Selecting a directory descends into it. Selecting a file
opens it through the production text session.

## Small Terminals

At 60×16, the logo fits the viewer and drawers retain their existing scrolling
behavior. Below the 60×12 minimum, Jim shows its existing size notice.

## Standalone Scene Tools

The ray-tracer engine and scene assets remain available to the explicit
`title:preview`, `title:profile`, and `title:record` tools. They are separate
from the live workspace; no title-screen action loads them.

## Implementation Map

| File | Responsibility |
| --- | --- |
| [`src/ui/title-screen.ts`](../../../src/ui/title-screen.ts) | Title scene surface rendering. |
| [`src/ui/title-scene.ts`](../../../src/ui/title-scene.ts) | Ray-traced title scene rendering. |
| [`src/app/title-camera-session.ts`](../../../src/app/title-camera-session.ts) | Title camera interaction state. |
| [`src/app/workspace/title-screen-key-bindings.ts`](../../../src/app/workspace/title-screen-key-bindings.ts) | Title-screen key routing. |
| [`src/app/workspace/file-tree.ts`](../../../src/app/workspace/file-tree.ts) | Shared file entry open behavior. |
| [`src/ui/jim-logo-screen.ts`](../../../src/ui/jim-logo-screen.ts) | Static logo artwork rendering and viewport fit. |
| [`src/app/workspace/viewer-content.ts`](../../../src/app/workspace/viewer-content.ts) | Select static logo, source, or Markdown preview. |
| [`src/app/workspace/title-screen-key-bindings.ts`](../../../src/app/workspace/title-screen-key-bindings.ts) | No-buffer Escape quit-confirmation routing. |
| [`src/app/workspace/file-tree.ts`](../../../src/app/workspace/file-tree.ts) | Shared file entry opening. |
| [`spec/static-title-screen.spec.mjs`](../../../spec/static-title-screen.spec.mjs) | Input, rendering, stale-message, idle, and module-loading witnesses. |
4 changes: 2 additions & 2 deletions quality-baseline.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@
"spec/workspace-command-completion.spec.mjs": 932,
"spec/workspace-command-line.spec.mjs": 1974,
"spec/workspace-helpers.mjs": 623,
"spec/workspace-runtime.spec.mjs": 619,
"spec/workspace-runtime.spec.mjs": 520,
"spec/workspace-text-boundaries.spec.mjs": 559,
"spec/workspace-text-cutover.spec.mjs": 2045,
"spec/workspace-title-screen.spec.mjs": 1184,
"spec/workspace-title-screen.spec.mjs": 730,
"tests/replace-range-law-cycle.spec.mjs": 539
},
"forbiddenTypeKeywords": {},
Expand Down
5 changes: 3 additions & 2 deletions spec/help.spec.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@ async function loadHelpModule() {
return import(pathToFileURL(HELP_PATH).href);
}

test('help text scopes scene picker shortcut to the title screen', async () => {
test('help advertises current workspace actions without a scene picker', async () => {
const help = await loadHelpModule();
const overlay = help.createEditorHelpOverlay(120, 40);

assert.match(overlay.content, /ctrl\+l\s+open scene picker \(title screen only\)/);
assert.doesNotMatch(overlay.content, /ctrl\+l|scene picker/i);
assert.match(overlay.content, /f2\s+open or close settings/);
});
12 changes: 0 additions & 12 deletions spec/keybindings.spec.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,3 @@ test('settings and markdown preview use different function keys', async () => {
assert.equal(keybindings.JEDIT_MARKDOWN_PREVIEW_TOGGLE_KEY, 'f3');
assert.notEqual(keybindings.JEDIT_SETTINGS_TOGGLE_KEY, keybindings.JEDIT_MARKDOWN_PREVIEW_TOGGLE_KEY);
});

test('scene picker label is derived from the key binding metadata', async () => {
const keybindings = await loadKeybindingsModule();

assert.equal(
keybindings.JEDIT_SCENE_PICKER_TOGGLE_LABEL,
keybindings.formatJeditKeyLabel({
key: keybindings.JEDIT_SCENE_PICKER_TOGGLE_KEY,
ctrl: true,
}),
);
});
Loading
Loading