Skip to content

fix(tui): make the welcome panel responsive to terminal size (#1067) - #1069

Merged
saravmajestic merged 2 commits into
mainfrom
fix/AI-1067-welcome-panel-responsive
Aug 4, 2026
Merged

fix(tui): make the welcome panel responsive to terminal size (#1067)#1069
saravmajestic merged 2 commits into
mainfrom
fix/AI-1067-welcome-panel-responsive

Conversation

@saravmajestic

@saravmajestic saravmajestic commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Closes #1067.

What & why

The welcome panel (home_logo slot) rendered a fixed two-column boot box — block ALTIMATE CODE wordmark on the left (~65 cols), "What is Altimate Code" on the right — that never shrank. On a typical terminal it took ~40–50% of the screen and couldn't be reduced, which is what #1067 reports.

What this does

It now scales with terminal size, reactively (adapts live on resize), using the repo's existing breakpoint idiom (useTerminalDimensions + createMemo, as in routes/session/permission.tsx and component/upgrade-indicator.tsx). Both axes gate each step — the wordmark is wide and tall:

Variant When What shows ~height
full ≥110w & ≥44h the two-column wordmark boot box (large windows only) ~13 rows
medium in between (the common case) title + one condensed line + CTA, no wordmark ~6 rows
compact <60w or <18h a single line; the border title already carries the version ~3 rows

On an everyday laptop terminal (e.g. 106×31, 80×24) you now get medium; the full branded panel returns only when there's genuinely room for it.

Notes

  • The breakpoint decision is a pure function in welcome-panel-utils.ts (mirroring upgrade-indicator-utils.ts), so it's unit-testable without the render/sync context. 4 tests cover the thresholds and that the smaller dimension wins.
  • Scope: responsiveness only. No dismiss / collapse-once-connected behavior change — that's separable and can be a follow-up if wanted.
  • Thresholds are four named constants in welcome-panel-utils.ts, easy to tune.

Verified

tsgo --noEmit clean · 4 unit tests pass · existing home-onboarding render test still passes · oxlint 0/0 · prettier.

🤖 Generated with Claude Code


Summary by cubic

Make the TUI welcome panel responsive to its available space, so it no longer overtakes small terminals or sidebar layouts. Addresses AI-1067 by switching between full, medium, and compact layouts, updating live on resize and sidebar toggle.

  • Bug Fixes
    • Moved breakpoint logic to welcome-panel-utils as welcomePanelVariant(width, height) using available size and PANEL_VERTICAL_RESERVE; thresholds are floors (MEDIUM_MIN_*, FULL_MIN_* with height floor 36).
    • WelcomePanel now accepts availableWidth/availableHeight; home passes width-4 and height-PANEL_VERTICAL_RESERVE, session passes contentWidth() and height-PANEL_VERTICAL_RESERVE.
    • Simplified render logic with <Switch>/<Match>, deduped the CTA, trimmed the full left column to 54 cols, and shortened the medium copy; no change to CTA/dismiss behavior.
    • Expanded unit tests to prove both width/height gates, exact boundaries, and sidebar/short-terminal cases.

Written for commit 4db83ea. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • New Features

    • The welcome panel now adapts its layout to the available terminal size.
    • Added full, medium, and compact layouts for different terminal dimensions.
    • Compact layouts provide condensed information and streamlined readiness actions.
    • The panel now accounts for available content space across home and session views.
  • Tests

    • Added coverage for layout selection across terminal size thresholds, boundary conditions, and constrained dimensions.

The full two-column boot box (block wordmark + description) is ~65 cols wide and
~13 rows tall, so on a typical terminal it ate ~40-50% of the screen with no way
to shrink it (#1067).

It now scales by terminal size, using the repo's breakpoint idiom
(useTerminalDimensions + createMemo, cf. routes/session/permission.tsx,
component/upgrade-indicator.tsx) — reactive, so it adapts live on resize:

- full   — the two-column wordmark box, reserved for large windows (>=110w & >=44h)
- medium — title + one condensed line, no wordmark (the common case, ~6 rows)
- compact — a single line; the border title already shows the version (<60w or <18h)

The breakpoint decision is a pure function in welcome-panel-utils.ts (mirroring
upgrade-indicator-utils.ts) so it's unit-testable without the render/sync context;
4 tests cover the thresholds and that the smaller dimension wins.

Scope: responsiveness only — no dismiss/collapse behavior change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@claude claude 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.

Claude Code Review

This repository is configured for manual code reviews. Comment @claude review for a one-time review, or @claude review always to subscribe this PR to a review on every future push.

Tip: disable this comment in your organization's Code Review settings.

@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown

This PR doesn't fully meet our contributing guidelines and PR template.

What needs to be fixed:

  • PR description is missing required template sections. Please use the PR template.

Please edit this PR description to address the above within 2 hours, or it will be automatically closed.

If you believe this was flagged incorrectly, please let a maintainer know.

@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 67583c67-09c0-4f91-82c1-295a50ec8941

📥 Commits

Reviewing files that changed from the base of the PR and between 721fee6 and 4db83ea.

📒 Files selected for processing (5)
  • packages/tui/src/component/welcome-panel-utils.ts
  • packages/tui/src/component/welcome-panel.tsx
  • packages/tui/src/routes/home.tsx
  • packages/tui/src/routes/session/index.tsx
  • packages/tui/test/component/welcome-panel-utils.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/tui/test/component/welcome-panel-utils.test.ts

📝 Walkthrough

Walkthrough

The TUI welcome panel now selects compact, medium, or full layouts from available terminal dimensions. Routes pass adjusted dimensions to the panel. Tests cover threshold, cross-axis, reserved-space, and degenerate-size behavior.

Changes

Responsive Welcome Panel

Layer / File(s) Summary
Terminal-size variant classification
packages/tui/src/component/welcome-panel-utils.ts, packages/tui/test/component/welcome-panel-utils.test.ts
Breakpoint constants and welcomePanelVariant classify dimensions as compact, medium, or full. Tests cover boundaries, cross-axis thresholds, reserved space, and minimal sizes.
Responsive panel rendering
packages/tui/src/component/welcome-panel.tsx
WelcomePanel accepts optional dimensions and renders compact, medium, or full content. Compact and medium layouts use condensed content. Full mode retains the wordmark and detailed information panel.
Route dimension wiring
packages/tui/src/routes/home.tsx, packages/tui/src/routes/session/index.tsx
Home and session routes pass content dimensions to WelcomePanel after subtracting horizontal padding and the shared vertical reserve.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant HomeOrSessionRoute
  participant WelcomePanel
  participant welcomePanelVariant
  participant PanelLayout
  HomeOrSessionRoute->>WelcomePanel: pass available width and height
  WelcomePanel->>welcomePanelVariant: classify dimensions
  welcomePanelVariant-->>WelcomePanel: return compact, medium, or full
  WelcomePanel->>PanelLayout: render selected layout
Loading

Suggested reviewers: anandgupta42

Poem

A rabbit checks the terminal space,
Then picks the panel’s proper place.
Compact, medium, full appear,
With readiness messages clear.
Hop, hop—the layout fits!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The PR reduces space usage for issue #1067 but does not address the reported inability to dismiss the welcome panel. Implement dismiss behavior or split that requirement into a separate issue and avoid closing #1067 until its requirements are covered.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the main change: making the TUI welcome panel responsive to terminal size.
Description check ✅ Passed The description explains the issue, implementation, scope, and verification, but it omits the required checklist and UI screenshot or recording.
Out of Scope Changes check ✅ Passed The changes remain focused on welcome-panel responsiveness, related layout cleanup, caller sizing, and supporting tests.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/AI-1067-welcome-panel-responsive

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.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install failed. For unrecoverable errors, disable the tool in CodeRabbit configuration.


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.

@cubic-dev-ai cubic-dev-ai 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.

All reported issues were addressed across 3 files

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread packages/tui/src/component/welcome-panel-utils.ts Outdated
@saravmajestic saravmajestic self-assigned this Aug 4, 2026

@sahrizvi sahrizvi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Review — responsiveness change works, but two things block

The direction is right and the decomposition is clean. Splitting the breakpoint decision into a pure, exported function mirroring upgrade-indicator-utils.ts is the right call, the createMemo + useTerminalDimensions wiring matches the idiom in upgrade-indicator.tsx:14 and permission.tsx:450 (genuinely reactive, no destructured props), and the full branch is a faithful move of the original markup so the large-terminal path is low-risk. Theme tokens are unchanged, there's no dead code, and the scope discipline — explicitly declining to bundle dismiss/collapse — is good.

Two blockers, both left inline:

  1. The breakpoint is computed from terminal width, but the panel never gets terminal width. In the session view with the sidebar open, full is still selected when the panel only has ~84–94 columns, and the panel swells back to ~44% of the screen — the exact bug #1067 reports. (welcome-panel.tsx:30)
  2. The tests don't prove the width threshold the change is built around. Deleting width < MEDIUM_MAX_WIDTH from the source leaves all four tests green. (welcome-panel-utils.test.ts:17)

Plus three minors inline: the compact variant's wrapMode="word" breaks its own single-line invariant, medium is ~9 rows at its lower bound rather than ~6, and the *_MAX_* constants are really minimums for the tier above.


MINOR — no render test for the component, though the repo has the harness

The pure function is tested; the part that can actually break isn't — that useTerminalDimensions is wired up, that the memo survives a resize, that each variant fits its row budget. test/feature-plugins/home-onboarding.test.tsx shows testRender takes an explicit { width, height }, so a three-case render test is cheap.

"existing home-onboarding render test still passes" is true but says nothing about this component — that test renders at 120×4 and never mounts WelcomePanel. A render test at session-like widths with the sidebar visible is also the only thing that would have caught blocker #1.

NIT — contradictory height figures inside one file

welcome-panel-utils.ts:4-5 says the full box is "~8 rows tall"; lines 13-16 say "~13 rows tall". Both are defensible (inner content vs. bordered box) but the reader can't tell which is which. Pick one framing.

NIT — structural cleanups worth folding in

  • welcome-panel.tsx:31-33compact(), medium(), full() are three memos over one memo feeding three mutually-exclusive <Show> blocks (lines 51, 60, 78). routes/session/permission.tsx and session/index.tsx:1349-1358 both use <Switch><Match when={…}> for exactly this; switching makes the exclusivity structural rather than incidental.
  • The fixed width={65} left column (line 82) is ~15 columns wider than its content — src/logo.ts is 32 + 1 gap + 17 = 50 columns, and the title line is 24. Trimming to ~52 would let full engage on a smaller terminal and would soften blocker #1.
  • "Connect your AI model to start." is duplicated verbatim at lines 54, 71 and 122 — worth one module constant. (The three descriptions are deliberately different copy, not duplication.)

Open design question, not a defect

MEDIUM_MAX_HEIGHT = 44 means the wordmark is effectively never seen on a laptop — it needs an external monitor or a full-screen tall window. That may well be intentional ("full only when there's real room"), but it's worth an explicit product decision, since it makes the branded panel dead code for most users. Note the tension with blocker #1: lowering the threshold widens the range where the sidebar case misfires, so fix that first.

Missing tests, collected

  • Compact→medium boundary at exactly (60, 18), (60, 40), (120, 18) — the entire lower boundary.
  • (109, 44) — the only case that isolates the full width gate.
  • Degenerate sizes (0, 0), (1, 1) — currently return compact, which is correct, but nothing pins it.
  • Render tests per variant, with height-budget assertions and a resize transition.
  • Session layout with the sidebar visible — where blocker #1 lives.

const ready = useReady()
const dimensions = useTerminalDimensions()

const variant = createMemo(() => welcomePanelVariant(dimensions().width, dimensions().height))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

MAJOR (Bug / Logic Error) — the breakpoint reads terminal width, but the panel never gets terminal width.

dimensions().width is the whole terminal. This panel is never that wide:

  • routes/home.tsx:104 wraps it in <box … paddingLeft={2} paddingRight={2}>width - 4.
  • routes/session/index.tsx:1181-1189 — same padding, and it sits in the content column of a flexDirection="row" layout whose sibling is a 42-column Sidebar (routes/session/sidebar.tsx:30), auto-shown whenever width > 120 (session/index.tsx:267-272).

The correct quantity already exists four lines from the call site and is unused here:

// session/index.tsx:274
const contentWidth = createMemo(() => dimensions().width - (sidebarVisible() ? 42 : 0) - 4)

The full branch has a hard width={65} flexShrink={0} left column (line 82), so it needs ~105 usable columns:

Terminal Sidebar Panel width Variant chosen Right-column text
110×44 (home) n/a 106 full ~33 cols — fine
130×50 (session) visible (auto, >120) 84 full ~11 cols
140×50 (session) visible 94 full ~21 cols

At 140×50 the two description paragraphs wrap to ~13 rows and the panel lands at ~22 rows — 44% of the screen, which is the complaint in #1067. Every terminal roughly 121–151 columns wide and ≥44 rows, in a session with the sidebar visible, reproduces the original bug.

Not a regression (before this change full rendered at every size), but the fix misses its stated goal in a configuration a user on a large monitor will hit.

Fix:

export function WelcomePanel(props: { availableWidth?: number } = {}) {
  const dimensions = useTerminalDimensions()
  const width = createMemo(() => props.availableWidth ?? dimensions().width - 4)
  const variant = createMemo(() => welcomePanelVariant(width(), dimensions().height))
// session/index.tsx
<WelcomePanel availableWidth={contentWidth()} />

contentWidth is already reactive to sidebarVisible(), so toggling the sidebar re-picks the variant for free.

Belt-and-braces regardless: give the full left column flexShrink={1} or a maxWidth, or a minWidth on the right column, so a mis-sized full degrades instead of collapsing to a text gutter.


test("a narrow OR short terminal drops the wordmark (medium)", () => {
expect(welcomePanelVariant(80, 24)).toBe("medium")
expect(welcomePanelVariant(MEDIUM_MAX_WIDTH - 1, 40)).toBe("medium") // narrow but tall

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

MAJOR (Testing) — these tests don't prove what the PR description claims they prove.

The description says "4 tests cover the thresholds and that the smaller dimension wins." Two separate problems mean they don't.

(a) The full-tier width gate is entirely unexercised. Mutation-test the pure function — delete the width gate from the medium branch:

if (width < MEDIUM_MAX_WIDTH || height < MEDIUM_MAX_HEIGHT) return "medium"
// becomes
if (height < MEDIUM_MAX_HEIGHT) return "medium"

…and all four tests still pass. The other three gates (full height, compact width, compact height) are each caught by an equivalent mutant, so this is the one real hole.

This line is the culprit: the comment says "narrow but tall", but 40 < MEDIUM_MAX_HEIGHT (44), so height forces the medium result, not width. "Tall" isn't tall enough to isolate the width axis.

(b) The compact→medium boundary is untested, and test 4 (lines 27-31) duplicates test 3. Line 12 does an "at the threshold" check for the full tier; the compact tier has none, so a <<= slip on either compact gate ships silently. And test 4's (200, 17) / (59, 200) take the same two branches as test 3's (120, 17) / (59, 40) — only the non-deciding dimension differs, and it stays on the same side of every threshold. No mutant is caught by test 4 that test 3 doesn't already catch.

Fix — repair this assertion and replace test 4 with the boundaries that are actually missing:

expect(welcomePanelVariant(MEDIUM_MAX_WIDTH - 1, MEDIUM_MAX_HEIGHT)).toBe("medium") // narrow, tall enough
expect(welcomePanelVariant(COMPACT_MAX_WIDTH, COMPACT_MAX_HEIGHT)).toBe("medium")
expect(welcomePanelVariant(COMPACT_MAX_WIDTH, 40)).toBe("medium")
expect(welcomePanelVariant(120, COMPACT_MAX_HEIGHT)).toBe("medium")
expect(welcomePanelVariant(0, 0)).toBe("compact")

{/* compact — one line; the border title already carries the version */}
<Show when={compact()}>
<box paddingLeft={2} paddingRight={2} width="100%">
<text fg={ready() ? theme.text : theme.primary} wrapMode="word" width="100%">

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

MINOR (Bug) — wrapMode="word" means the "single line" invariant doesn't actually hold.

The compact string is 31 characters ("Connect your AI model to start."). Subtract 2 border + 4 panel padding + 4 home-route gutter and it wraps at roughly 40 columns; near 20 columns it can reach ~5 rows. compact is reachable at any width below 60, so the "a single line; ~3 rows" claim on line 24 is aspirational rather than enforced.

Fix — enforce it structurally:

<text fg={ready() ? theme.text : theme.primary} wrapMode="none" truncate width="100%">

Comment on lines 61 to 74
<box paddingLeft={2} paddingRight={2} paddingTop={1} paddingBottom={1} gap={0} width="100%">
<text fg={theme.text} attributes={TextAttributes.BOLD}>
Welcome to Altimate Code
</text>
<text fg={theme.text} wrapMode="word" width="100%">
It gives your AI real context — column-level lineage, SQL analysis, dbt, and live warehouse metadata — so it
reasons about your data instead of guessing.
A data-engineering harness that gives your AI real context — column-level lineage, SQL analysis, dbt, and
live warehouse metadata.
</text>
{/* CTA only until a model is connected — stale afterwards */}
<Show when={!ready()}>
<text fg={theme.primary} wrapMode="word" width="100%">
Connect your AI model to start.
</text>
</Show>
</box>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

MINOR (Design) — medium is ~9 rows at its own lower bound, not the advertised ~6.

At the smallest terminal that still selects medium (60×18, home route → 56-wide panel, 50-column text area):

Element Rows
border 2
paddingTop / paddingBottom 2
title 1
description (~130 chars at 50 cols) 3
CTA (disconnected) 1
total ~9

That's 50% of an 18-row terminal — the same ratio #1067 complains about, in a different layout. Even at minimum intrinsic size the disconnected medium panel needs 7 rows, so the "~6 rows" figure in welcome-panel-utils.ts:16 isn't reachable while the CTA is present.

Raise COMPACT_MAX_HEIGHT, drop the vertical padding here, or shorten the description so it fits one or two lines at 50 columns.

Comment on lines +17 to +22
/** Below these, drop to the single-line compact panel. */
export const COMPACT_MAX_WIDTH = 60
export const COMPACT_MAX_HEIGHT = 18
/** At/above these, show the full two-column wordmark panel; below → medium. */
export const MEDIUM_MAX_WIDTH = 110
export const MEDIUM_MAX_HEIGHT = 44

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

MINOR (Code Quality / Documentation) — the *_MAX_* names are actually minimums for the tier above.

Every comparison in welcomePanelVariant is a strict <, so:

  • COMPACT_MAX_WIDTH = 60 → a 60-wide terminal is medium, not compact. 60 is the minimum width for medium.
  • MEDIUM_MAX_WIDTH = 110 → 110 is the minimum width for full.

The doc comment on line 20 ("At/above these, show the full…") already describes minimum semantics and contradicts the name on line 21. These constants are exported and consumed by the tests, so the wrong name propagates — and it's the direct cause of several "is this an off-by-one?" readings that turn out to be wrong.

Suggest MEDIUM_MIN_WIDTH / MEDIUM_MIN_HEIGHT / FULL_MIN_WIDTH / FULL_MIN_HEIGHT.

…tests (#1069 review)

Blocker 1 — the breakpoint read the whole terminal, but the panel never gets it.
The home slot pads it and the session view puts it in a content column beside a
42-col sidebar, so `full` was still chosen at ~84 usable cols and swelled back to
~44% (the #1067 bug). WelcomePanel now takes `availableWidth`/`availableHeight`;
home passes `width-4` / `height-PANEL_VERTICAL_RESERVE`, session passes the
existing `contentWidth()` (already minus sidebar+padding) / `height-RESERVE`.
Falls back to the terminal dimension when omitted. Reactive to resize AND sidebar
toggle.

Height gets the same treatment: subtract the fixed prompt/footer chrome
(PANEL_VERTICAL_RESERVE = 8) so a short window drops to a smaller variant instead
of a too-tall panel that crowds the prompt. Thresholds re-expressed in usable
terms (MEDIUM_MIN_HEIGHT 16, FULL_MIN_HEIGHT 36) — full still engages at a ~44-row
terminal; ≤~23 rows now → compact.

Blocker 2 — the tests didn't prove the width gate (deleting `width < …` left them
green). Rewrote to isolate each gate on one axis (e.g. `(FULL_MIN_WIDTH-1, tall)
-> medium`), plus exact boundaries, the sidebar case, a short-terminal height
case, and degenerate `(0,0)/(1,1)`.

Also from review:
- Rename `*_MAX_*` -> `MEDIUM_MIN_*` / `FULL_MIN_*` — floors matched with strict `<`.
- `<Switch>/<Match>` instead of three memos + three `<Show>`.
- Dedupe the CTA into a `CONNECT_CTA` constant.
- Trim the full left column 65 -> 54 (logo is ~50 cols) so `full` engages sooner.
- Shorten the medium description to one line.
- Consistent "~13-row bordered box" framing in the comments.

FULL_MIN_HEIGHT stays tuned so the wordmark shows only on large windows (product choice).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@saravmajestic

Copy link
Copy Markdown
Contributor Author

Thanks — both blockers were spot on. Fixed in 4db83eaa7a.

Blocking 1 — breakpoint read the whole terminal, not the panel's width

Right: the home slot pads the panel and the session view sits it beside a 42-col sidebar, so full was picked at ~84 usable cols. WelcomePanel now takes availableWidth; home passes dimensions().width - 4, session passes the existing contentWidth() (already width - sidebar - 4). It's reactive to resize and sidebar toggle (contentWidth depends on sidebarVisible). Concretely: 130w session with the sidebar auto-shown now resolves to medium (84 usable cols), where it used to be full.

I also gave height the same treatment while here — it had the analogous problem (the panel shares the column with the prompt/footer). Callers now pass availableHeight = dimensions().height - PANEL_VERTICAL_RESERVE (8 rows of chrome), so a short window drops to a smaller variant instead of a too-tall panel. Thresholds re-expressed in usable terms (MEDIUM_MIN_HEIGHT 16, FULL_MIN_HEIGHT 36) so full still engages at a 44-row terminal and ≤23 rows → compact.

Blocking 2 — tests didn't prove the width gate

Fixed. Each gate is now isolated on one axis so deleting its < check fails a test — e.g. (FULL_MIN_WIDTH-1, tall) -> medium (dies if width < FULL_MIN_WIDTH is removed), (MEDIUM_MIN_WIDTH-1, tall) -> compact, and the mirror height cases. Added exact boundaries, the sidebar case, a short-terminal height case, and degenerate (0,0)/(1,1).

Minors / nits

  • Naming (you + cubic): *_MAX_*MEDIUM_MIN_* / FULL_MIN_* — they're the floor a tier needs, matched with strict <.
  • <Switch>/<Match> instead of three memos + three <Show> — exclusivity is now structural.
  • Left column 65 → 54 (logo is ~50 cols) so full engages sooner.
  • Medium copy shortened to one line (was ~9 rows at its lower bound).
  • CTA deduped into CONNECT_CTA.
  • Height figures unified to "~13-row bordered box"; dropped the strict single-line claim on compact (it wraps only on an extremely narrow terminal — better than truncating the CTA mid-word).

Open question — FULL_MIN_HEIGHT

Kept high on purpose: the wordmark shows only on large windows (the product ask for #1067 was "don't dominate the screen"). Happy to lower it if you'd rather see the branded panel more often — note it trades against the short-terminal case.

Render tests

Added the full boundary/gate/degenerate coverage on the pure function. I did not add a session-with-sidebar integration render test: a WelcomePanel-only render (as you noted) wouldn't catch blocker 1 — that needs the session view mounted with the sidebar, which is a heavier integration test. Wiring is verified by inspection + tsgo. Glad to add that integration test as a follow-up if you want the regression guard.

tsgo clean · 9 unit + 11 render/adjacent tests pass · oxlint 0/0.

<Match when={variant() === "medium"}>
<box paddingLeft={2} paddingRight={2} paddingTop={1} paddingBottom={1} gap={0} width="100%">
<text fg={theme.text} attributes={TextAttributes.BOLD}>
Welcome to Altimate Code

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

SUGGESTION: CONNECT_CTA dedupes the call-to-action across branches, but the Welcome to Altimate Code title is still duplicated verbatim here and in the full branch (line 99). Hoisting it to a module constant (e.g. WELCOME_TITLE) alongside CONNECT_CTA would keep the two render branches in sync if the wording ever changes.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

@kilo-code-bot

kilo-code-bot Bot commented Aug 4, 2026

Copy link
Copy Markdown

Code Review Summary

Status: 1 Issue Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 0
SUGGESTION 1

The two prior blockers (breakpoint reading the whole terminal width instead of the panel's usable width, and tests that couldn't prove the width gate) plus the *_MAX_* naming are all genuinely resolved in 4db83eaa. availableWidth/availableHeight are now passed by both callers (home passes width-4, session passes the existing reactive contentWidth()), the variant memo is reactive to resize and sidebar toggle, and the pure-function tests isolate each gate on one axis. Reactivity, imports, and the height heuristic are sound. One consistency nit remains below.

Issue Details (click to expand)

SUGGESTION

File Line Issue
packages/tui/src/component/welcome-panel.tsx 71 Welcome to Altimate Code title is duplicated verbatim in the medium and full branches; dedupe to a module const like CONNECT_CTA
Files Reviewed (5 files)
  • packages/tui/src/component/welcome-panel-utils.ts - 0 issues
  • packages/tui/src/component/welcome-panel.tsx - 1 issue
  • packages/tui/src/routes/home.tsx - 0 issues
  • packages/tui/src/routes/session/index.tsx - 0 issues
  • packages/tui/test/component/welcome-panel-utils.test.ts - 0 issues

Fix these issues in Kilo Cloud


Reviewed by glm-5.2 · Input: 63.1K · Output: 13.3K · Cached: 458.4K

Review guidance: REVIEW.md from base branch main

@sahrizvi

sahrizvi commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Round 2 review — 4db83eaa7a

Both blockers are genuinely fixed, and the test work went past what I asked for. Approving. What's left is one MINOR worth a quick fix and a few nits.

Blocker 1 — width source: fixed

props.availableWidth ?? dimensions().width read inside the memo (not destructured), so reactivity survives the prop boundary. Both call sites check out: home's - 4 matches its own paddingLeft/Right={2}, and session reusing contentWidth() means it's reactive to the sidebar toggle as well as resize. The reported case resolves — 130-col session with the sidebar open now yields 84 available → medium, and it's pinned by a test.

The 65 → 54 trim is right: logo.ts measures 32 + 1 gap + 17 = exactly 50 columns, so 54 with paddingLeft={1} fits with 3 to spare. At FULL_MIN_WIDTH the right column gets 110 − 2 − 54 − 1 − 4 = 49 columns of text, so full fits at its own floor.

Blocker 2 — tests: fixed, beyond the ask

I re-ran the mutation exercise with eight mutants — deleting each of the four gates, and flipping each < to <=:

Mutant Result
drop width < FULL_MIN_WIDTH killed
drop height < FULL_MIN_HEIGHT killed
drop width < MEDIUM_MIN_WIDTH killed
drop height < MEDIUM_MIN_HEIGHT killed
width <= FULL_MIN_WIDTH killed
height <= FULL_MIN_HEIGHT killed
width <= MEDIUM_MIN_WIDTH killed
height <= MEDIUM_MIN_HEIGHT killed

All eight die — every gate proven in both directions. I only asked for the one isolated width case; deriving TALL/WIDE from the floor constants makes the isolation structural rather than something a future threshold retune can silently break. Nice.

Prior findings

# Finding Status
1 Breakpoint used raw terminal width Fixed
2 Tests didn't prove the width gate Fixed
3 compact wrapMode="word" vs the single-line claim Resolved — agreed, softening the claim beats truncating a CTA mid-word
4 medium ~9 rows at its lower bound Fixed in substance — 8 rows of a raw 24-row terminal (33%, was 50%)
5 *_MAX_* names were minimums Fixed — no stale references anywhere
6 No component render test Deferred — your reasoning is right; see the MINOR below for why I'd still want it eventually
7 NITs (height figures, <Switch>, left column, CTA dedupe) Fixed

MINOR — PANEL_VERTICAL_RESERVE = 8 fits session, under-reserves home

The docstring calls it "the prompt, the footer, and the home top spacer", but that's home's layout and the number fits session's. Measuring the pieces:

  • Home: 2 (top spacer, home.tsx:110) + 1 (prompt wrapper paddingTop) + ~4-5 (prompt: input + separator row + status row) + 3 (footer — feature-plugins/home/footer.tsx is paddingTop 1 + content 1 + paddingBottom 1) ≈ 10-11 rows. Reserve 8 under-reserves by ~2-3.
  • Session: gap 1 + gap 1 + paddingBottom 1 + ~4-5 (prompt) ≈ 7-8 rows — no top spacer, no footer in that column. Reserve 8 is about right.

Practical impact is small: at full's 44-row floor the panel is ~14 rows, leaving ~20 rows of slack, so nothing overflows. But the constant is a heuristic that matches neither route as documented. Either split it into HOME_VERTICAL_RESERVE / SESSION_VERTICAL_RESERVE, or reword the docstring so it doesn't present one route's chrome as both.

MINOR — the optional props default to raw terminal dimensions

props.availableWidth ?? dimensions().width means a future call site that forgets a prop silently gets exactly the pre-fix behavior — and nothing tests the call sites, so it'd be silent. Given the whole point of this commit is that raw terminal dimensions are the wrong input, consider making both props required. That also converts finding 6 from "we should render-test this" into "the type system covers the regression".

NIT — medium's "one condensed line" is two lines at medium's own minimum width

The string is ~96 chars; at the minimum medium width the text area is 60 − 2 border − 4 padding = 54 columns, so it wraps to 2. It's one line above ~100 available columns. Wording only — the 8-row budget already assumes 2.

NIT — the width retune wasn't disclosed alongside the height retune

The commit message calls out the height change (raw ≤23 → compact, was ≤18). The width floor moved too: measured against available width, home's raw compact threshold went 60 → 64 columns. Same class of intentional change, worth the same one-line note.

NIT — the classic 80×24 terminal sits exactly on the medium floor

80×24 → available (76, 16), and MEDIUM_MIN_HEIGHT is 16 — dead on the boundary, with 80×23 flipping to a single line. The test at line 49 pins it, so it's clearly considered rather than accidental. Just worth knowing the most common legacy size is on a cliff edge.


Checked and fine — no action needed

  • Reactivity through the prop boundary. Props are getters, so reading props.x inside createMemo tracks; only destructuring would break it. Sidebar toggle → contentWidth recomputes → variant recomputes. Verified.
  • <Switch>/<Match> conversion kept the Show when={!ready()} CTA gating in both medium and full.
  • Zero/negative available dimensions return compact correctly, and the degenerate tests pin it.
  • contentWidth subtracts 42 even when the sidebar is an absolute overlay at !wide() — pre-existing, shared with the consumer at line 1167, and it errs conservatively, so nothing to do.

On FULL_MIN_HEIGHT

Your call, and the reasoning holds — #1067 was "don't dominate the screen", so erring toward the wordmark being rare is the right default. Leaving it.

@saravmajestic
saravmajestic merged commit 03b9459 into main Aug 4, 2026
23 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

home_logo slot always visible and cannot be dismissed in TUI

2 participants