Repository navigation
fix(tui): line editor edge cases (merge on the last widget, move wrap, save guard return) - #689
Open
eric-engberg wants to merge 4 commits into
Open
eric-engberg wants to merge 4 commits into
eric-engberg wants to merge 4 commits into
Conversation
The merge toggle was guarded to widgets with another widget after them, because the last one has nothing to merge into. But a widget can become last with its merge still set: merge Git Branch into the widget after it, then delete that widget. Git Branch still shows "(merged→)", m does nothing, and "(m)erge" is gone from the help line, so the flag can't be cleared. The next widget added after it then merges into it silently (`⎇ mainModel: Claude` in the preview). On the last widget, m now clears a merge that is set, and the help line offers it there. It still can't set a merge on the last widget, and the merged, merged-without-padding and off cycle is unchanged elsewhere.
Move mode wraps past either end of the list, but it did so by swapping the moved item with the one at the other end. Moving Model up from the top of Model, Context Length, Git Branch, Git Changes gave Git Changes, Context Length, Git Branch, Model: Model reached the bottom, but Git Changes jumped from the bottom to the top. The line selector's move mode did the same with whole lines. Both move modes now take the item out and put it back in at the target, so wrapping past either end moves only that item and the rest keep their order. A move to a neighbour is still a swap.
… guard When settings.json is invalid, Ctrl+S first asks before overwriting it. The question can come up on any screen, but both answers went to the main menu: with Edit Line 1 open, Ctrl+S then ESC (or Yes) left the line editor, and you had to navigate back in to carry on. The guard now returns to the screen Ctrl+S was pressed on, for both answers. Save & Exit, the other route into the guard, starts from the main menu and still returns there. The screen comes back as it does when you navigate to it: a sub-editor that was open on it, such as the label editor, and its unsaved text aren't restored.
The App sets chalk's global color level from the settings it loads, and the whole-App test helper didn't restore it. Test files that run later in the same process then rendered at that level instead of their own: a No Color check could see 256-color codes. The sandbox now restores the level along with the mocks and the config path.
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.
Three small line-editor fixes, one commit each.
What
mnow removes a merge that's still set there, and the help line offers(m)ergein that case. It still can't set a new merge on the last widget.settings.json, Ctrl+S asks before overwriting the file. Whether you answer ESC or Yes, you now go back to the screen you pressed it on instead of the main menu.Why
m((merged→)) → ↓ →ddeletes Git Changes. Git Branch is now last and still shows(merged→).mdoes nothing there and(m)ergeis gone from the help line. The next widget you add after it merges into it silently: add Git Changes back and the preview shows⎇ main (+42,-10)in one segment, with no separator.mon Line 1 then ↑ gives empty, 2 widgets, 4 widgets. The wrap-around came in with feat(tui): add wrap-around navigation to all menus and move modes #332 to move items "cyclically through list boundaries"; the swap is a side effect of reusing the neighbour swap for the wrap.{"lines":"oops"}assettings.json: Edit Lines → Line 1 → Ctrl+S → ESC lands on the main menu, and you have to navigate back into the line. Yes does the same after saving.How
The
mhandler and the editor'scanMergenow allow the last widget when it has a merge set. There,mclears the merge from either merged state. Everywhere else the merged → merged without padding → off cycle is unchanged. I picked this over stripping the merge automatically whenever a delete or move makes a widget last: that would silently drop a setting during a reorder (move a merged widget to the end and back and it would lose its merge), and it wouldn't help a merge already saved on a last widget.A small
moveItemhelper (src/utils/move-item.ts) removes the item and inserts it at the target. Both move modes use it. Moving to a neighbour is still a swap, so only the wrap changes.buildInvalidConfigSaveConfirmtakes the screen to return to (it defaults tomain). The Ctrl+S handler passes the current screen, and its Yes path goes back there before saving, so the success flash shows on that screen. Save & Exit starts from the main menu and still goes back there.Not fixed: the screen comes back the way it does when you navigate to it. A sub-editor that was open on it, such as the label editor with unsaved text, isn't restored. The confirm dialog replaces the whole screen, so
ItemsEditorunmounts along with the sub-editor and draft it holds. Keeping them would mean showing the dialog over a screen that stays mounted, and pausing every screen's input handling while the dialog is up. That's a bigger change, so I left it out. The draft wasn't part of the save anyway, since a label is only applied on Enter.Demo
Real TUI, recorded from
main(before) and this branch (after) with the demo kit's sample config.1. Merge on the last widget
Merge Git Branch, delete Git Changes, press
mon Git Branch, then add Git Changes back.Powerline: before
Powerline: after
Plain: before
Plain: after
2. Move past the top
Enter on Model, then ↑.
Powerline: before
Powerline: after
Plain: before
Plain: after
3. Save guard with an invalid settings.json
Edit Line 1 → Ctrl+S → ESC, then Ctrl+S → Yes. With an invalid
settings.jsonthe TUI shows its defaults whatever the file says, so there's no Powerline version of this one. The demo hides the parse warning the CLI prints to stderr at launch.Before
After
Testing
mainand pass here:input-handlers.test.ts:mclearstrueand'no-padding'on the last widget.[2, 3, 1]and[3, 1, 2]);maingives[3, 2, 1].ItemsEditor.test.ts:(m)ergeshows on a last widget that's merged, andmremoves(merged→).LineSelector.test.ts: the wrap past the bottom keeps the other lines in order.App.test.ts: the guard builder honours the return screen. A whole-Apptest with an invalidsettings.jsonreturns to Edit Line 1 after ESC and after Yes, and checks the file was saved.mcan't set a merge on the last widget.bun test: 2788 pass, 0 fail.bun run lintpasses.mainpasses 86 in the same files (6 new tests).HOMEandCLAUDE_CONFIG_DIR, ran all three flows above. A line-selector wrap with three different lines gave Line 2, empty, Line 1 on this branch and empty, Line 2, Line 1 onmain. With the label editor open and "MyLabel" typed, Ctrl+S → ESC returns to Edit Line 1 (the label editor closes, as described above).