Skip to content

feat: dynamic table row counts based on terminal height#61

Open
yfarjoun wants to merge 3 commits intofg-labs:mainfrom
yfarjoun:yf_dynamic-table-rows
Open

feat: dynamic table row counts based on terminal height#61
yfarjoun wants to merge 3 commits intofg-labs:mainfrom
yfarjoun:yf_dynamic-table-rows

Conversation

@yfarjoun
Copy link
Copy Markdown
Contributor

@yfarjoun yfarjoun commented Apr 5, 2026

Summary

  • Replace hardcoded max_visible values (10 for running/pending, 8 for completions/stats) with dynamic calculation based on terminal height
  • Tables now fill available vertical space on tall terminals and shrink gracefully on short ones
  • Accounts for layout mode (compact vs full) and number of panels per column (2 or 3, depending on whether failed/incomplete panels are shown)
  • Minimum 3 rows always displayed

Closes #59

Test plan

  • All 1082 tests pass
  • ruff format/lint clean
  • mypy clean
  • Manual: resize terminal vertically and verify tables grow/shrink
  • Manual: verify with failed/incomplete jobs (3-panel columns)
  • Manual: verify compact layout gives running table more rows

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Enhancements
    • Tables and panels now dynamically adjust visible rows based on terminal height, layout mode, and which panels are active; COMPACT layout shows more rows than FULL. Very small or unknown terminal heights are clamped to a sensible minimum to preserve readability.
  • Tests
    • Added tests verifying visible-row behavior across layouts, panel counts, tiny terminals, and missing console dimensions.

Replace hardcoded max_visible (10/8) with dynamic calculation based on
terminal height, layout mode, and number of panels per column. Tables
now fill available space on tall terminals and shrink gracefully on
short ones. Minimum 3 rows always shown.

Closes fg-labs#59

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@yfarjoun yfarjoun requested a review from nh13 as a code owner April 5, 2026 21:52
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 5, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d94cc88f-42a6-4270-a5d9-0ab6f9dc9512

📥 Commits

Reviewing files that changed from the base of the PR and between 7c0a21e and b67af8b.

📒 Files selected for processing (1)
  • tests/test_tui.py
✅ Files skipped from review due to trivial changes (1)
  • tests/test_tui.py

📝 Walkthrough

Walkthrough

Replaces hardcoded table row limits with dynamic per-panel sizing based on terminal height and layout. Adds _left_panel_count and _right_panel_count, _max_visible_rows(num_panels), and _update_panel_counts(progress), and wires dynamic max_visible into running, completions, pending, and stats table constructors.

Changes

Cohort / File(s) Summary
TUI monitor updates
snakesee/tui/monitor.py
Added _left_panel_count and _right_panel_count (default 2); implemented _max_visible_rows(num_panels) with a minimum clamp of 3; added _update_panel_counts(progress) (sets counts to 3 in FULL layout when extra panels present) and invoked it from _make_layout(...); replaced hardcoded max_visible values in _make_running_table, _make_completions_table, _make_pending_jobs_panel, and _make_stats_panel to use the new dynamic sizing.
Tests
tests/test_tui.py
Added TestMaxVisibleRows validating _max_visible_rows(panel_count) across LayoutMode (FULL, COMPACT, MINIMAL), several console heights (including None -> default), ensuring COMPACT > FULL for same panel count and clamping to minimum 3 rows on small heights.

Sequence Diagram(s)

(omitted — changes are local sizing logic without multi-component sequential flow)

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐇 I count the rows with twitchy paws,
Panels stretch and bend without a pause,
Tall or small, they find their space,
Tables hop into a snug embrace,
Carrots for every visible place! 🥕

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: making table row counts dynamic based on terminal height, which directly reflects the core objective of the PR.
Description check ✅ Passed The description covers the main changes, includes test status, and references the closed issue. However, some checklist items are incomplete and documentation updates are not addressed.
Linked Issues check ✅ Passed The PR successfully implements all objectives from issue #59: replaces hardcoded max_visible values, computes row counts dynamically from terminal height, accounts for layout modes and panel counts, and enforces minimum 3 rows.
Out of Scope Changes check ✅ Passed All changes are in-scope: monitor.py modifications implement the dynamic row calculation, and test additions validate the new _max_visible_rows() functionality. No unrelated changes detected.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (1)
snakesee/tui/monitor.py (1)

3025-3027: ⚠️ Potential issue | 🟠 Major

Panel count logic is correct but executed too late.

The panel count calculation correctly determines whether 3 or 2 panels are visible based on whether failed_jobs_list and incomplete_jobs_list are non-empty. However, as noted in the previous comment, these updates happen after tables have already been rendered using stale panel counts from the previous frame.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@snakesee/tui/monitor.py` around lines 3025 - 3027, The panel count assignment
for self._left_panel_count and self._right_panel_count is happening too late
(after tables are rendered) so move the logic that sets these counts based on
failed_jobs_list and incomplete_jobs_list to run before any table rendering or
layout calculation; locate the code paths that render/update the UI (e.g., the
method that builds or draws the panels/tables) and update those to compute
self._left_panel_count = 3 if self.incomplete_jobs_list else 2 and
self._right_panel_count = 3 if self.failed_jobs_list else 2 prior to calling the
render/draw functions so the layout uses the fresh counts.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@snakesee/tui/monitor.py`:
- Around line 1645-1657: The _max_visible_rows method currently uses a hardcoded
chrome height (15) valid for FULL layout but wrong for COMPACT; change
_max_visible_rows to accept a layout_mode parameter (or inspect
self.layout_mode) and compute body_height based on layout-specific chrome (e.g.,
FULL: header+progress+summary_footer+footer=15, COMPACT: omit summary_footer =>
12), then return the same panel math; update all call sites (including the
COMPACT caller that currently invokes _max_visible_rows without layout) to pass
the correct layout_mode or let the method read self.layout_mode so the
calculation matches the active UI mode.
- Around line 2053-2054: The code reads self._left_panel_count to compute max
rows before it’s updated later in _make_layout, causing a one-frame lag; extract
the panel-count determination into a new helper (e.g. _update_left_panel_count)
that computes num_panels and sets self._left_panel_count based on current panel
visibility/LayoutMode and call it before computing max_visible =
self._max_visible_rows(num_panels) in the render path; then remove the duplicate
panel-count logic from _make_layout so both rendering and layout use the same
pre-updated value and eliminate the race.

---

Duplicate comments:
In `@snakesee/tui/monitor.py`:
- Around line 3025-3027: The panel count assignment for self._left_panel_count
and self._right_panel_count is happening too late (after tables are rendered) so
move the logic that sets these counts based on failed_jobs_list and
incomplete_jobs_list to run before any table rendering or layout calculation;
locate the code paths that render/update the UI (e.g., the method that builds or
draws the panels/tables) and update those to compute self._left_panel_count = 3
if self.incomplete_jobs_list else 2 and self._right_panel_count = 3 if
self.failed_jobs_list else 2 prior to calling the render/draw functions so the
layout uses the fresh counts.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7e85d280-1d1c-4792-955d-ac969b15e83e

📥 Commits

Reviewing files that changed from the base of the PR and between 453d660 and ce1cee5.

📒 Files selected for processing (1)
  • snakesee/tui/monitor.py

Returns at least 3 rows so tables are always usable.
"""
height = self.console.height or 24
# Fixed chrome: header(3) + progress(6) + summary_footer(3) + footer(3) = 15
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The hardcoded 15 is correct for FULL layout but wrong for COMPACT/MINIMAL (no summary_footer, so chrome is 12). This wastes 3 rows in COMPACT mode.

Suggestion:

fixed_chrome = 15 if self._layout_mode == LayoutMode.FULL else 12
body_height = height - fixed_chrome

Also, since this is a pure computation of terminal_height, num_panels, and layout_mode, consider extracting it as a standalone function — it would be trivially testable without needing a full WorkflowMonitorTUI instance.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 7c0a21e:

fixed_chrome = 15 if self._layout_mode == LayoutMode.FULL else 12

Re: extracting as a standalone function — it still reads self.console.height and self._layout_mode, so it would need those passed in. I kept it as a method for now since the tests can set those attributes directly on the TUI instance, but happy to extract if you prefer.

@@ -2034,7 +2050,8 @@ def _make_running_table(self, progress: WorkflowProgress) -> Panel: # noqa: C90
job_data = self._sort_running_job_data(job_data)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

No tests were added for _max_visible_rows. It's a pure computation that's straightforward to unit test. At minimum:

  • Standard terminal height with 2 and 3 panels
  • Floor of 3 rows on a very small terminal
  • COMPACT vs FULL chrome difference (once the chrome fix is applied)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Added in 7c0a21eTestMaxVisibleRows with 6 tests:

  • FULL layout with 2 and 3 panels
  • COMPACT layout with 1 panel
  • COMPACT vs FULL comparison (more rows in COMPACT)
  • Small terminal (height=10) hits floor of 3
  • None height falls back to 24

- Fix chrome calculation for COMPACT layout (12 not 15, no summary_footer)
- Move panel count update to start of _make_layout to avoid one-frame lag
- Extract _update_panel_counts helper method
- Add TestMaxVisibleRows tests covering FULL/COMPACT/small terminal/floor

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
tests/test_tui.py (1)

2368-2423: Consider adding an explicit MINIMAL-layout regression test.

_max_visible_rows currently treats LayoutMode.MINIMAL via the non-FULL branch. A dedicated assertion would lock this behavior and catch future branch drift.

Proposed test addition
 class TestMaxVisibleRows:
@@
     def test_compact_vs_full_gives_more_rows(self, tui: WorkflowMonitorTUI) -> None:
         """COMPACT layout yields more rows than FULL for the same panel count."""
         from snakesee.tui.monitor import LayoutMode
@@
         full_rows = tui._max_visible_rows(2)
         assert compact_rows > full_rows
+
+    def test_minimal_layout_matches_compact_chrome(self, tui: WorkflowMonitorTUI) -> None:
+        """MINIMAL layout should follow the same chrome math as COMPACT."""
+        from snakesee.tui.monitor import LayoutMode
+
+        tui._layout_mode = LayoutMode.MINIMAL
+        minimal_rows = tui._max_visible_rows(2)
+        tui._layout_mode = LayoutMode.COMPACT
+        compact_rows = tui._max_visible_rows(2)
+        assert minimal_rows == compact_rows
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/test_tui.py` around lines 2368 - 2423, Add a regression test asserting
that LayoutMode.MINIMAL is handled by the non-FULL branch of _max_visible_rows:
create a new test (alongside the other layout tests) that sets tui._layout_mode
= LayoutMode.MINIMAL, calls tui._max_visible_rows with a representative panel
count (e.g., 1 or 2), and asserts the expected row count (matching the
COMPACT/non-FULL behavior used elsewhere); reference the LayoutMode.MINIMAL enum
and the _max_visible_rows method so future changes that move MINIMAL into a
separate branch are caught.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@tests/test_tui.py`:
- Around line 2368-2423: Add a regression test asserting that LayoutMode.MINIMAL
is handled by the non-FULL branch of _max_visible_rows: create a new test
(alongside the other layout tests) that sets tui._layout_mode =
LayoutMode.MINIMAL, calls tui._max_visible_rows with a representative panel
count (e.g., 1 or 2), and asserts the expected row count (matching the
COMPACT/non-FULL behavior used elsewhere); reference the LayoutMode.MINIMAL enum
and the _max_visible_rows method so future changes that move MINIMAL into a
separate branch are caught.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 22753d55-4269-4cfb-9128-a7ddda611fa4

📥 Commits

Reviewing files that changed from the base of the PR and between ce1cee5 and 7c0a21e.

📒 Files selected for processing (2)
  • snakesee/tui/monitor.py
  • tests/test_tui.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • snakesee/tui/monitor.py

Ensures MINIMAL layout uses the same chrome calculation as COMPACT,
locking this behavior against future branch drift.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@yfarjoun
Copy link
Copy Markdown
Contributor Author

yfarjoun commented Apr 9, 2026

@coderabbitai help

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 9, 2026

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

CodeRabbit commands

These commands are invoked using PR/Issue comments.

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai evaluate custom pre-merge check --instructions <custom-checks-instructions> --name <custom-checks-title> [--mode <error|warning>] to validate the custom pre-merge checks instructions. Defaults to error when --mode is omitted.
    • @coderabbitai ignore pre-merge checks to override pre-merge checks and get an approval on PR.
    • @coderabbitai run pre-merge checks to run pre-merge checks on the pull request.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve merge conflict to automatically resolve merge conflicts.
  • @coderabbitai autofix to automatically fix issues identified in unresolved review comments.
  • @coderabbitai resolve to resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai generate configuration to create a PR that adds the current resolved configuration as .coderabbit.yaml (or show it if already present).
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore or @coderabbit ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit configuration file (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • You can also validate your configuration using the online YAML validator.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, support, documentation and community

  • Visit our status page to check the current availability of CodeRabbit.
  • Create a ticket on our support page for assistance with any issues or questions.
  • Visit our documentation site for detailed information on how to use CodeRabbit.
  • Join our Discord community to connect with other users and get help from the community.
  • Follow us on X/Twitter for updates and announcements.

@yfarjoun
Copy link
Copy Markdown
Contributor Author

yfarjoun commented Apr 9, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 9, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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.

Make table row counts dynamic based on terminal height

2 participants