fix(tui): make the welcome panel responsive to terminal size (#1067) - #1069
Conversation
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>
There was a problem hiding this comment.
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.
|
This PR doesn't fully meet our contributing guidelines and PR template. What needs to be fixed:
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. |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (5)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughThe 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. ChangesResponsive Welcome Panel
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
Suggested reviewers: Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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
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. Comment |
There was a problem hiding this comment.
All reported issues were addressed across 3 files
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
sahrizvi
left a comment
There was a problem hiding this comment.
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:
- The breakpoint is computed from terminal width, but the panel never gets terminal width. In the session view with the sidebar open,
fullis 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) - The tests don't prove the width threshold the change is built around. Deleting
width < MEDIUM_MAX_WIDTHfrom 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-33—compact(),medium(),full()are three memos over one memo feeding three mutually-exclusive<Show>blocks (lines 51, 60, 78).routes/session/permission.tsxandsession/index.tsx:1349-1358both 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.tsis 32 + 1 gap + 17 = 50 columns, and the title line is 24. Trimming to ~52 would letfullengage 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 thefullwidth gate.- Degenerate sizes
(0, 0),(1, 1)— currently returncompact, 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)) |
There was a problem hiding this comment.
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:104wraps 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 aflexDirection="row"layout whose sibling is a 42-columnSidebar(routes/session/sidebar.tsx:30), auto-shown wheneverwidth > 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 |
There was a problem hiding this comment.
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%"> |
There was a problem hiding this comment.
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%">| <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> |
There was a problem hiding this comment.
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.
| /** 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 |
There was a problem hiding this comment.
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 formedium.MEDIUM_MAX_WIDTH = 110→ 110 is the minimum width forfull.
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>
|
Thanks — both blockers were spot on. Fixed in Blocking 1 — breakpoint read the whole terminal, not the panel's widthRight: the home slot pads the panel and the session view sits it beside a 42-col sidebar, so 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 Blocking 2 — tests didn't prove the width gateFixed. Each gate is now isolated on one axis so deleting its Minors / nits
Open question —
|
| <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 |
There was a problem hiding this comment.
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.
Code Review SummaryStatus: 1 Issue Found | Recommendation: Address before merge Overview
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 Issue Details (click to expand)SUGGESTION
Files Reviewed (5 files)
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 |
Round 2 review —
|
| 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 wrapperpaddingTop) + ~4-5 (prompt: input + separator row + status row) + 3 (footer —feature-plugins/home/footer.tsxispaddingTop1 + content 1 +paddingBottom1) ≈ 10-11 rows. Reserve 8 under-reserves by ~2-3. - Session: gap 1 + gap 1 +
paddingBottom1 + ~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.xinsidecreateMemotracks; only destructuring would break it. Sidebar toggle →contentWidthrecomputes →variantrecomputes. Verified. <Switch>/<Match>conversion kept theShow when={!ready()}CTA gating in bothmediumandfull.- Zero/negative available dimensions return
compactcorrectly, and the degenerate tests pin it. contentWidthsubtracts 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.
Closes #1067.
What & why
The welcome panel (
home_logoslot) rendered a fixed two-column boot box — blockALTIMATE CODEwordmark 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 inroutes/session/permission.tsxandcomponent/upgrade-indicator.tsx). Both axes gate each step — the wordmark is wide and tall:≥110w & ≥44h<60w or <18hOn 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
welcome-panel-utils.ts(mirroringupgrade-indicator-utils.ts), so it's unit-testable without the render/sync context. 4 tests cover the thresholds and that the smaller dimension wins.welcome-panel-utils.ts, easy to tune.Verified
tsgo --noEmitclean · 4 unit tests pass · existinghome-onboardingrender 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.
welcome-panel-utilsaswelcomePanelVariant(width, height)using available size andPANEL_VERTICAL_RESERVE; thresholds are floors (MEDIUM_MIN_*,FULL_MIN_*with height floor 36).WelcomePanelnow acceptsavailableWidth/availableHeight;homepasseswidth-4andheight-PANEL_VERTICAL_RESERVE,sessionpassescontentWidth()andheight-PANEL_VERTICAL_RESERVE.<Switch>/<Match>, deduped the CTA, trimmed the full left column to 54 cols, and shortened the medium copy; no change to CTA/dismiss behavior.Written for commit 4db83ea. Summary will update on new commits.
Summary by CodeRabbit
New Features
Tests