Repository navigation
[CPLAT-12912] Resizable panes with tuikit split - #181
Merged
Merged
Conversation
Bump tuikit to 5b7fabe and replace ccx's three resize paths with one
split.Model shared by the sessions, conversation, config and plugins
splits (they already shared one ratio).
- The ratio is a fraction, 0.15-0.85, list >= 30 and preview >= 20
columns, default 0.35. Every write clamps, so a key press always moves
the divider by at least a cell or reports "Split at its limit".
- SplitPane.layout (split.Model.Layout plus ccx's closed/zoomed/short
rules) is the one source of widths: Render, renderSessionSplit, the
drag-selection panes and the click/wheel hit tests read the same rects.
ListWidth/PreviewWidth now come from it, and the hand-written
`width - list - 1` arithmetic is gone.
- Mouse: the split sees each event before selection and clicks. A press
on the divider drags it (heavy glyph while dragging), a double click
resets it. tryStartDragResize/handleDragResize and the dragResizing
guard are removed. The old +-1 cell grab zone is dropped (Grab 0): those
cells belong to the panes, so a drag there selects text.
- Keys: `<` shrink, `>` grow, `=` reset (session keymap split_shrink,
split_grow, split_reset; defaults from split.Bindings). `[` `]` stay
as aliases where they already resized, not where they cycle config
pages or inspector facets. Conflicts() stays clean. Footer `<>=
resize`, and a row in every split view's `?` overlay.
- `set:ratio N` is Set(N/100); the config/plugins 20-80 clamp is gone.
- ui.json saves {"split": {"ratio": r}}. split_ratio (a percentage) from
an older ui.json or config.yaml is read once and dropped on the next
save; garbage keeps the default.
|
| Commit | Scanned at | New | Resolved | Net |
|---|---|---|---|---|
0de3d2e |
2026-10-21 15:50 UTC | 0 | 0 | 0 |
3f2895f < |
2026-10-21 23:07 UTC | 0 | 0 | 0 |
Last scanned: 3f2895f · 2026-10-21 23:07 UTC
Conflicts: - go.mod/go.sum: keep tuikit 5b7fabe (split), which contains master's b0beaa4 (selection v2, link); termenv drops as master did. - drag_selection.go: master's sel.Update (auto-scroll ticks, offset scroll) without the dragResizing guard; the split sees divider presses first in handleMouse, so selection never gets them. - drag_selection_test.go: master's tuitest.Reversed highlight check.
The OSC 8 title in the Outputs digest is followed by a detail cut to the preview width; with the divider moved, that width must come from the split, or the row overflows the pane and loses its ellipsis.
Kairo-Kim
approved these changes
Oct 21, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
JIRA: https://sendbird.atlassian.net/browse/CPLAT-12912
What
tuikit goes from c19aa4f to 5b7fabe, which adds the
splitpackage (keyolk/tuikit#11). ccx resized its panes three ways:[ ]±5 (15-85), a mouse drag that grabbed a cell of each pane (20-80), andset:ratio N; the config and plugins views clamped to 20-80 on their own. All of them now go through onesplit.ModelonApp, shared by the sessions, conversation, config and plugins splits, as the oldsplitRatiowas.This branch starts from master and does not depend on #180.
The split
splitRatio intpercentsplit.Model: fraction 0.15-0.85, default 0.35, step 0.05ListWidth), preview ≥ 20 (adjustSplitRatioonly)MinA: 30,MinB: 20, enforced on every write and every layoutListWidth/PreviewWidtharithmetic, plus ~20 copies ofwidth - list - 1SplitPane.layout→split.Model.Layout;ListWidth/PreviewWidthread it│DrawDivider:│, heavy┃while dragging (visible under NO_COLOR)Collapse: 40,minSplitHeight); a width that cannot fit 30 + 1 + 20 also shows the list alone, full widthSplitPane.layoutis the only source of widths:Render,renderSessionSplit, the drag-selection panes (splitRects) and the click/wheel hit tests (inPreview) all read the same rects.Mouse
handleSplitMouseruns first inhandleMouse, before selection, overlays and clicks. A press on the divider starts a drag and is consumed; motion moves the divider; release ends it. A double click on the divider resets it.tryStartDragResize,handleDragResize, thedragResizingfield and its guard inhandleSelectionMouseare removed.Grab: 0). Those cells belong to the panes, so a drag that starts there selects text.TestDragNextToDividerResizesInsteadOfSelectingasserted the opposite and is nowTestDragNextToDividerSelectsInsteadOfResizing.Keys
</>session.split_shrink,session.split_grow)=session.split_reset)[/]split.Bindings.keys.Conflictsstays clean, and no other binding in any scope uses<,>or=, so nothing was rebound.</>show "Split at its limit" instead of doing nothing silently.<>= resize, last in priority, where[]:resizewas.?overlay: a< / > / =row in the sessions list, sessions preview, conversation, inspector, config and plugins help.set:ratio NisSet(N/100), clamped like any other write.Saved state
{"split": {"ratio": 0.4}}.split_ratio(a percentage) from an older ui.json or config.yaml is read once, in 15-85, and dropped on the next save.split.ratiowins when both are present.40,-1,1.5) or an old percentage outside 15-85 keeps the default.Updatealready callssaveUIStateafter every message andquitflushes.Other
restoreConvPreviewAnchorre-rendered the preview at a width computed from a fixed 50% ratio. It now re-renders at the width the preview is drawn at.hexToRGBwas only used for the raw-ANSI divider and is removed.Tests
Positions come from the rendered
View(): the divider glyph left of the preview text, never the layout code. Persistence tests usewithUIStateDir(HOME,XDG_CONFIG_HOME,XDG_STATE_HOMEint.TempDir()).TestSplitDividerDragMovesBoundaryEverywhere: press on the divider found in the frame, move, release. The divider shows heavy during the drag and moves in View. The list and preview selection panes match the new position. A click on old preview columns now focuses the list, and a click right of the new divider focuses the preview. Nothing selects.TestSplitPanePressStillSelects,TestDragNextToDividerSelectsInsteadOfResizing,TestSplitDoubleClickOnDividerResets.TestSplitKeysMoveDivider(>,<,=,]),TestSplitClampHasNoDeadZone(40 ×>, then one<moves at once, in both directions, minimums held),TestSplitKeysInConfigViewKeepPageCycling.TestSplitKeysShowInHelpAndFooter,TestSplitKeysDoNotShadowBindings.TestSplitKeysRewrapConversationPreview: after>and after a drag, the conversation preview shows every word, in order, none cut.TestSetRatioCommandIsAPercentOfTheSplit,TestSplitNarrowTerminal(60 and 80 columns fit, 45 columns shows the list alone at full width).TestSplitRatioSavedAndRestored,TestSplitGarbageInUIJSONFallsBackToDefault,TestSplitMigratesOldPercent(ui.json, config.yaml, new field wins).Each new guard was checked with a compiling mutation, then restored:
TestSplitDividerDragMovesBoundaryEverywhere,TestSplitDoubleClickOnDividerResets,TestDragOnSplitBorderStillResizesTestSplitDividerDragMovesBoundaryEverywhereTestSplitDividerDragMovesBoundaryEverywhere</>armTestSplitKeysMoveDivider,TestSplitClampHasNoDeadZone=armTestSplitKeysMoveDividerMinB2 instead of 20TestSplitClampHasNoDeadZone,TestSetRatioCommandIsAPercentOfTheSplit,TestSplitNarrowTerminalTestSplitDividerDragMovesBoundaryEverywhereTestDragPastPreviewBottomScrollsAndKeepsLines,TestDragInScrolledPreviewCopiesWhatIsShownresizeAllTestSplitKeysRewrapConversationPreview=TestSplitKeysShowInHelpAndFooter?row missingTestSplitKeysShowInHelpAndFootersplit_resetdefault not from the kitTestSplitKeysShowInHelpAndFootersplit.Statenot savedTestSplitRatioSavedAndRestored,TestSplitMigratesOldPercentsplit_rationot migratedTestSplitMigratesOldPercentTestSplitGarbageInUIJSONFallsBackToDefaultTwo mutations survive because they are equivalent in what the user sees:
set:ratioclamping without the area:Layoutclamps to the minimums when it draws, and the next key steps from the drawn position.RenderandsyncSessionSplitLayoutre-size from the same layout on the next frame. The call stays, as the old drag code had it.gofmt -lis clean.go vet ./...passes.go test ./...,go test -race ./...(one run) andenv -u LC_ALL -u LC_CTYPE LANG=ko_KR.UTF-8 go test ./...all pass.