Skip to content

feat(tui): flip the terminal UI to the designed LEM mockup#21

Merged
Snider merged 1 commit into
mainfrom
lane/ctml-design
Jul 23, 2026
Merged

feat(tui): flip the terminal UI to the designed LEM mockup#21
Snider merged 1 commit into
mainfrom
lane/ctml-design

Conversation

@Snider

@Snider Snider commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Aligns the lem tui to the canonical design mockup (files/Lethean-tui/tui/LEM TUI.html) across both layers — the .ctml structure/labels and the host-provided runtime text in the .go where the design's strings live.

What changed, by panel

  • Tabs — ○/● glyphs, canonical panel names at every width (no narrow rename)
  • Chat — designed empty-state, SESSIONS bar, teal hierarchy
  • Models — dense three-column table (Model / Engine / Snapshot path)
  • Service — state toggle, ‹ :36911 › address stepper, OpenAI/Anthropic/Ollama client-hint grid at localhost:36911, smoke curl, shared-lane note
  • Work / Data — designed empty-states
  • Inspector — per-tab sections match the mockup (SESSION/MODEL/GENERATION/SETTINGS/MODE/TOOLS/KNOWLEDGE; MODEL DETAIL/LOADED; ADDRESS/REQUESTS/STATE)
  • Footertab panels · ctrl+k commands · ctrl+o inspector · f1 help

Panel update loops and domain behaviour preserved. Intentional render changes are pinned in the sibling _test.go (incl. new service_test.go); no golden files gamed.

Gates

  • go build ./cli/... — exit 0
  • go test ./cli/tui/...424 pass
  • Headless frame render exact at 150×44 (every line 150 cols, no ragged chrome)

🤖 Generated with Claude Code
Co-Authored-By: Virgil virgil@lethean.io

Summary by CodeRabbit

  • New Features
    • Redesigned the Models panel with a compact table, filtering, clearer columns, improved selection, pagination and path truncation.
    • Added clearer session labels, adaptive footer controls and refreshed panel navigation.
  • Improvements
    • Updated chat prompts and placeholders based on model availability.
    • Refined empty-state guidance for Chat, Work and Data panels.
    • Improved Service status messaging and controls.
    • Standardised tab labels and refreshed colours, markers and layout styling.
  • Documentation
    • Clarified terminal rendering, panel, table and command-palette documentation.

Aligns both layers to the canonical design (files/Lethean-tui/tui/LEM TUI.html):
the .ctml structure/labels and the host-provided runtime text in the .go where
the design's strings actually live.

- Tabs: ○/● glyphs, canonical panel names at every width (no narrow rename)
- Chat: designed empty-state ("no model loaded — open Models and choose one.
  The prompt below stays put either way."), SESSIONS bar, teal hierarchy
- Models: dense three-column table (Model / Engine / Snapshot path)
- Service: state toggle, ‹ :36911 › address stepper, OpenAI/Anthropic/Ollama
  client-hint grid at localhost:36911, smoke curl, shared-lane note
- Work / Data: designed empty-states
- Inspector: per-tab sections match the mockup (SESSION/MODEL/GENERATION/
  SETTINGS/MODE/TOOLS/KNOWLEDGE; MODEL DETAIL/LOADED; ADDRESS/REQUESTS/STATE)
- Footer: "tab panels · ctrl+k commands · ctrl+o inspector · f1 help"

Panel update loops and domain behaviour preserved. Intentional render changes
pinned in the sibling _test.go (new service_test.go); no golden files gamed.

Gates: go build ./cli/... (exit 0), go test ./cli/tui/... (424 pass), frames
render exact at 150x44 (every line 150 cols).

Co-Authored-By: Virgil <virgil@lethean.io>
@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The TUI now uses canonical panel copy, a borderless fixed-width Models table, revised service and inspector indicators, dynamic chat/session/footer rendering, consistent panel labels, updated styling, and expanded rendering tests.

Changes

TUI rendering and interaction

Layer / File(s) Summary
Borderless model picker
cli/tui/picker.go, cli/tui/picker.ctml, cli/tui/picker_test.go, cli/tui/README.md
Models are rendered with fixed-width columns, filtering, counts, pagination, empty states, selection markers, and tail-preserving snapshot truncation.
Chat, sessions, and footer shell
cli/tui/app.go, cli/tui/layout.go, cli/tui/style.go, cli/tui/app_test.go, cli/tui/layout_test.go
Chat composer states, generic session ordinals, SESSIONS chips, footer key layouts, and adaptive theme colours are updated.
Service and inspector states
cli/tui/service.go, cli/tui/service_test.go, cli/tui/inspector.go, cli/tui/inspector_test.go, cli/tui/app.go, cli/tui/README.md
Service output uses stopped/listening states, and inspector indicators reflect selected-model loading and chat generation.
Canonical empty-panel content
cli/tui/datalist.ctml, cli/tui/datapanel.go, cli/tui/work.go, cli/tui/app.go, cli/tui/*_test.go
Chat, Work, and Data empty states use revised copy, styled command hints, and normalised rendering assertions.
Tabs and template conventions
cli/tui/tabs.go, cli/tui/tabs_test.go, cli/tui/palette.go, cli/tui/palette.ctml, cli/tui/README.md
Panel labels remain canonical across widths, with updated palette and shared template-row documentation.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 40.91% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and accurately reflects the main change: reworking the TUI to match the LEM mockup.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 golangci-lint (2.12.2)

Error: can't load config: unsupported version of the configuration: "" See https://golangci-lint.run/docs/product/migration-guide for migration instructions
The command is terminated due to an error: can't load config: unsupported version of the configuration: "" See https://golangci-lint.run/docs/product/migration-guide for migration instructions

Warning

Billing warning: we have not been able to collect payment for this subscription for more than 72 hours. Please update the payment method or pay any pending invoices in Billing to avoid service interruption.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@sonarqubecloud

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@cli/tui/inspector.go`:
- Around line 208-212: Update the loaded indicator logic in the models body
rendering to track the loaded model path from loadedMsg rather than the display
name. Compare selected.path against the loaded path when determining loadedClass
and loaded, while preserving the existing success and default indicator values.

In `@cli/tui/palette.go`:
- Around line 168-169: Update the go-html render-version notes in
cli/tui/palette.go and cli/tui/palette.ctml to cite v0.16.0, or revise both
notes to use version-neutral wording; keep the surrounding rendering guidance
unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 4ec2a7e9-d2af-4811-8a28-7ab94fa7c24a

📥 Commits

Reviewing files that changed from the base of the PR and between 071af6a and 1ac0ec7.

📒 Files selected for processing (22)
  • cli/tui/README.md
  • cli/tui/app.go
  • cli/tui/app_test.go
  • cli/tui/datalist.ctml
  • cli/tui/datapanel.go
  • cli/tui/datapanel_test.go
  • cli/tui/inspector.go
  • cli/tui/inspector_test.go
  • cli/tui/layout.go
  • cli/tui/layout_test.go
  • cli/tui/palette.ctml
  • cli/tui/palette.go
  • cli/tui/picker.ctml
  • cli/tui/picker.go
  • cli/tui/picker_test.go
  • cli/tui/service.go
  • cli/tui/service_test.go
  • cli/tui/style.go
  • cli/tui/tabs.go
  • cli/tui/tabs_test.go
  • cli/tui/work.go
  • cli/tui/work_test.go

Comment thread cli/tui/inspector.go
Comment on lines 208 to 212
loadedClass, loaded := "c-status", "○ none"
if target.modelName != "" {
loadedClass, loaded = "c-success", "● "+target.modelName
if selected, ok := target.picker.SelectedItem().(modelItem); ok && target.modelName == selected.name {
loadedClass, loaded = "c-success", "● loaded"
}
sequences["modelsBody"] = append(sequences["modelsBody"], map[string]any{"loadedClass": loadedClass, "loaded": loaded})

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== repo files matching inspector.go/app.go =="
fd -a '^(inspector\.go|app\.go)$' . | sed 's#^\./##'

echo "== outline cli/tui/inspector.go =="
ast-grep outline cli/tui/inspector.go --view expanded || true

echo "== relevant inspector.go lines =="
cat -n cli/tui/inspector.go | sed -n '170,230p'

echo "== search modelName/modelName assignment and beginModelLoad/loadedMsg =="
rg -n "modelName|modelPath|loading|beginModelLoad|loadedMsg|SelectedItem\\(\\)|modelsBody" cli/tui app.go || true

echo "== app.go relevant lines if present =="
if [ -f cli/tui/app.go ]; then
  cat -n cli/tui/app.go | sed -n '1,260p'
fi

Repository: dAppCore/go-inference

Length of output: 20339


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== app.go model item/build/discovery/load relevant sections =="
cat -n cli/tui/app.go | sed -n '330,390p'
cat -n cli/tui/app.go | sed -n '1320,1380p'
cat -n cli/tui/app.go | sed -n '2085,2125p'
cat -n cli/tui/app.go | sed -n '2248,2280p'

echo "== searches for item types and duplicate handling =="
rg -n "type modelItem|struct .*modelItem|DisplayName|displayName|modelItem|deduplicat|duplicate|map\\[string\\]|modelPath|pendingModel|a\\.modelName|target\\.modelName|beginModelLoad|loading = \"\"" cli/tui

echo "== dataListItem/type definitions =="
rg -n "type dataListItem|itemModel|ListItem|SelectedItem\\(\\)\\.(modelItem|dataListItem|workListItem)|\\bItem\\b:=" cli/tui

Repository: dAppCore/go-inference

Length of output: 39378


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== cli/tui/picker.go =="
cat -n cli/tui/picker.go | sed -n '1,85p'

echo "== inspector_test.go model same-name probe =="
cat -n cli/tui/inspector_test.go

echo "== model item path/name usage occurrences in cli/tui =="
python3 - <<'PY'
from pathlib import Path
p = Path("cli/tui")
for f in p.iterdir():
    if f.suffix != ".go":
        continue
    txt = f.read_text()
    if "modelItem" in txt or "modelPath" in txt or "modelName" in txt or "displayName(path)" in txt:
        print(f"\n--- {f} ---")
        for i, line in enumerate(txt.splitlines(), 1):
            if any(s in line for s in ["modelItem", "modelPath", "modelName", "displayName(path)"]):
                print(f"{i}: {line}")
PY

Repository: dAppCore/go-inference

Length of output: 13565


Use the loaded model path for the “loaded” indicator.

selected.name is the display name derived from displayName(m.Path), while model discovery de-duplicates by path. If two entries share the same name from different snapshot paths, selecting the wrong row can show ● loaded because only target.modelName == selected.name is checked. Track the selected loaded path through loadedMsg and compare selected.path instead.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cli/tui/inspector.go` around lines 208 - 212, Update the loaded indicator
logic in the models body rendering to track the loaded model path from loadedMsg
rather than the display name. Compare selected.path against the loaded path when
determining loadedClass and loaded, while preserving the existing success and
default indicator values.

Comment thread cli/tui/palette.go
Comment on lines +168 to +169
// go-html v0.13.0) and the marker glyph rides the row, the same form-row
// idiom settings.ctml uses — plus the zero-or-one-row conditional sections

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -eu

rg -n 'dappco\.re/go/html' -g 'go.mod' -g 'go.sum' .
rg -n 'go-html v0\.13\.0|go-html v0\.16\.0' cli/tui/palette.go cli/tui/palette.ctml

Repository: dAppCore/go-inference

Length of output: 1742


Update the go-html render-version note.

cli/go.mod declares dappco.re/go/html v0.16.0, but these render notes still cite go-html v0.13.0. Update the version in both cli/tui/palette.go and cli/tui/palette.ctml, or make the wording version-neutral.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cli/tui/palette.go` around lines 168 - 169, Update the go-html render-version
notes in cli/tui/palette.go and cli/tui/palette.ctml to cite v0.16.0, or revise
both notes to use version-neutral wording; keep the surrounding rendering
guidance unchanged.

@Snider
Snider merged commit 90cedbb into main Jul 23, 2026
5 of 6 checks passed
@Snider
Snider deleted the lane/ctml-design branch July 23, 2026 19:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant