Skip to content

feat(tui): Two-column layout with detail preview#42

Merged
hmans merged 13 commits intohmans:mainfrom
sotte:tui-two-col-lauout
Jan 17, 2026
Merged

feat(tui): Two-column layout with detail preview#42
hmans merged 13 commits intohmans:mainfrom
sotte:tui-two-col-lauout

Conversation

@sotte
Copy link
Copy Markdown
Contributor

@sotte sotte commented Dec 28, 2025

Implements a two-column TUI layout that shows bean details alongside the list, enabling quick scanning without opening each bean individually.

Summary

  • Left pane: Bean list with compact single-character type/status codes (M/E/B/F/T for types, D/T/I/C/S for statuses)
  • Right pane: Read-only detail preview showing ID, title, metadata, tags, and markdown-rendered body
  • Responsive: Automatically switches to single-column below 120 terminal columns
  • Cursor sync: Preview updates instantly as you navigate the list

Layout

╭───────────────────────────────────────╮╭──────────────────────────────────────╮
│ Beans                                 ││ beans-t0tv                           │
│                                       ││ Refactor TUI to two-column layout    │
│▌beans-t0tv     F  I  Refactor TUI...  ││                                      │
│ ├─ beans-3f64  T  C  Phase 1: Comp... ││ Status: in-progress  Type: feature   │
│ ├─ beans-433o  T  C  Phase 2: Deta... ││                                      │
│ └─ beans-6x50  T  C  Phase 5: Inte... ││   ## Summary                         │
│                                       ││   Refactor the TUI to a two-column   │
│                                       ││   format...                          │
╰───────────────────────────────────────╯╰──────────────────────────────────────╯
space select  enter view  e edit  p parent  s status  ? help  q quit

Key Design Decisions

  1. No hierarchy drilling - list stays flat with tree structure, filtering handles focus
  2. Read-only right pane - no keyboard focus, just visual preview
  3. Enter for full detail - opens existing full-screen detail view with all features
  4. Right pane max 80 chars - follows text file conventions, left pane gets remaining space
  5. App-global footer - help bar spans full terminal width

Changes

  • internal/ui/styles.go: Added ShortType/ShortStatus helpers, UseFullNames option for BeanRowConfig
  • internal/tui/preview.go: New read-only preview component with markdown rendering
  • internal/tui/list.go: Extracted Footer() method, added ViewConstrained() for two-column mode
  • internal/tui/tui.go: Two-column composition with calculatePaneWidths(), cursor sync via messages

Testing

  • Added preview_test.go with tests for preview rendering
  • Manual testing across various terminal widths

Refs: beans-t0tv, beans-m3mq, beans-tbtr

Examples

On a bit screen:
image

On a medium bit screen:
image

On a small screen:
image

Detail view is unchanged:
image

sotte and others added 11 commits December 28, 2025 19:37
Single-character codes for compact list display:
- Types: M(ilestone), E(pic), B(ug), F(eature), T(ask)
- Statuses: D(raft), T(odo), I(n-progress), C(ompleted), S(crapped)

Refs: beans-t0tv
- Type column: 3 chars (M/E/B/F/T)
- Status column: 3 chars (D/T/I/C/S)
- Updated CalculateResponsiveColumns base width calculation (40 -> 20)
- Updated tree headers to use "T" and "S" instead of "TYPE" and "STATUS"
- Frees up ~20 chars per row for title

Refs: beans-t0tv
Lightweight component for two-column layout right pane:
- Shows bean ID, title, status, type, priority
- Renders markdown body (truncated to fit)
- Shows 'No bean selected' when empty

Refs: beans-t0tv
- TwoColumnMinWidth: 120 columns
- LeftPaneWidth: 55 characters
- isTwoColumnMode() helper method

Refs: beans-t0tv
- Add preview previewModel field to App struct
- Initialize preview in New() with newPreviewModel(nil, 0, 0)

Refs: beans-t0tv
- View() checks isTwoColumnMode() before rendering
- renderTwoColumnView() composes list + preview horizontally
- ViewConstrained() renders list with constrained width
- Falls back to single-column for narrow terminals

Refs: beans-t0tv

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
- cursorChangedMsg emitted when list cursor moves
- App updates preview on cursor change
- Preview initialized when beans are loaded

Refs: beans-t0tv
- Update preview dimensions on window resize
- Handle empty list state in preview
- Add 'enter' shortcut to help overlay

Refs: beans-t0tv
- Footer now app-global, spanning full terminal width
- Right pane capped at 80 chars max (left pane gets remaining space)
- Preview height properly constrained to prevent overflow
- Detail view linked beans show full type/status names

Refs: beans-m3mq, beans-tbtr
When markdown content caused line wrapping within the preview pane,
lipgloss rendered more lines than expected. Our truncation was cutting
off the bottom border. Now we preserve the bottom border when truncating.

Also fixed height calculation in ViewConstrained() - was using -4
(for footer) instead of -2 (no footer in two-column mode).

Refs: beans-t0tv
@hmans
Copy link
Copy Markdown
Owner

hmans commented Dec 28, 2025

That's a very cool change, thanks for taking it on! I have a few suggestions, let me know what you think.

  • I like the single-letter abbreviated types/status in the left pane, but maybe we can responsively expand them to the full words when there's enough space? (Similar to how we're dealing with the tags in that list.)
  • I think we can now fully replace or even outright remove the "detail view" that you see when hitting Enter on a selected bean. If anything, hitting Enter on a bean might keep us in the same dual-pane view, but filter the list of beans in the left pane to just the selected bean itself and its children (basically becoming a "drill-down" view.)

@sotte
Copy link
Copy Markdown
Contributor Author

sotte commented Dec 29, 2025

I like the single-letter abbreviated types/status in the left pane, but maybe we can responsively expand them to the full words when there's enough space? (Similar to how we're dealing with the tags in that list.)

This one is easy. I'll implement it.

I think we can now fully replace or even outright remove the "detail view" that you see when hitting Enter on a selected bean. If anything, hitting Enter on a bean might keep us in the same dual-pane view, but filter the list of beans in the left pane to just the selected bean itself and its children (basically becoming a "drill-down" view.)

This one is a bit trickier. Initially I wanted to get rid of the detail view, but then

  • I would have to refactor the shortcuts quite a bit
  • I would have to rethink navigation (keep tab to switch between areas? different behaviour for arrow keys depending on the area?)
  • What do we do when you don't have enough screen real estate to show both panes.

I had a dirty version of the drill down feature. I did not feel good and I did not know how I would use it. So I did not implement a proper version. This feature would potentially benefit from implementing proper filtering first, see #41

If it's ok with you, I would not implement this feature here, but we can open a dedicated ticket for it.

Show full type/status names (e.g., "feature", "in-progress") when
terminal is ≥120 columns wide, single-letter abbreviations (F, I)
when space is tight.

- Add UseFullTypeStatus to ResponsiveColumns struct
- Set flag and wider column widths when width >= 120
- Pass setting to RenderBeanRow via list delegate

Refs: beans-vn93
@sotte
Copy link
Copy Markdown
Contributor Author

sotte commented Dec 29, 2025

Implemented the change. This is what it looks like for different screen sizes.

image image image image image

@hmans
Copy link
Copy Markdown
Owner

hmans commented Dec 30, 2025

I really like the idea of just getting rid of the extra detail view screen, it's probably obsolete if you can see the full selected bean anyway. Some thoughts:

  • yeah, hitting Tab to switch between one view and the other would work, but this could also be "Enter" to focus the detail pane and then "Escape" to return to the list (this is how eg. tig does it)
  • with regard to responsively supporting different screen dimensions, this could then alternate between
    • the default left pane/right pane setup when the space allows it
    • alternating to a top pane/bottom pane layout when not enough horizontal space is available
    • ultimately switching to a modal-style layout (where entering the detail view hides the list view until the user goes back) when there's too little space for either

But these are just some suggestions. Ultimately I would always lean towards simplifying where possible.

@sotte
Copy link
Copy Markdown
Contributor Author

sotte commented Dec 30, 2025

Ok, I'll try something and open a sep. PR that sits on top of this one. I'm not fully convinced yet :)

@hmans
Copy link
Copy Markdown
Owner

hmans commented Dec 30, 2025

FWIW, I didn't intend to push more work onto you -- if you like, we can wrap up this PR and merge it, and I can do some experimentation around this stuff some time soon.

@sotte
Copy link
Copy Markdown
Contributor Author

sotte commented Dec 30, 2025

All good, using the additional claude code tokens for this, and experimenting with a a beans/superpowers integration :)

I think this PR could be merged (after a review, I'm no bubbletea expert). But let's wait a bit till I have the experiment for the unified detail view ready.

@sotte sotte mentioned this pull request Dec 31, 2025
@hmans
Copy link
Copy Markdown
Owner

hmans commented Jan 16, 2026

Hi and sorry for only getting back to you and this PR now, over the last two weeks other projects and also some family stuff required my attention. I'm intending to spend some time with Beans this weekend -- would you say this PR here is in a state that I could/should merge?

You mentioned in the other PR that you weren't happy with the changes there. I'd be happy to merge this PR here and then iterate a little over the TUI myself if that's fine with you.

@sotte
Copy link
Copy Markdown
Contributor Author

sotte commented Jan 16, 2026

No worries!

Try out both PRs and decide for yourself. This one is easier. The other one follows your initial design more closely (I think), but touches more code (and is less human curated).

Your call!

Copy link
Copy Markdown
Owner

@hmans hmans left a comment

Choose a reason for hiding this comment

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

Let's merge this -- thanks for the PR!

@hmans hmans merged commit e1c178c into hmans:main Jan 17, 2026
1 check passed
hmans added a commit to divaltor/beans that referenced this pull request Jan 17, 2026
* origin/main:
  feat(tui): Two-column layout with detail preview (hmans#42)
  feat: add ETag support for optimistic concurrency control (hmans#59)
  feat(plugin): improve OpenCode plugin robustness with availability checks (hmans#58)
  feat(cli): Add --prefix flag to create command (hmans#56)
  chore: clean up README.md
  docs: only push prime if it exists (hmans#52)
  fix: normalise short IDs when storing relationship links (hmans#50)
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.

2 participants