-
Notifications
You must be signed in to change notification settings - Fork 8
persist user messages for a while #90
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Note Other AI code review bot(s) detectedCodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review. Caution Review failedThe pull request is closed. WalkthroughRefactors TUI messaging to a typed userMsg with kind and frame-limited lifetime, replaces string-based messages across handlers with errMsg/errMsgQuick, adds a debug flag to Model (sourced from HOURS_DEBUG) and threads it through InitialModel, and updates view/status bar to respect message frames. Changes
Sequence Diagram(s)sequenceDiagram
participant Env as Env (HOURS_DEBUG)
participant UI as UI Entrypoint
participant DB as *sql.DB
participant Model as Model
participant View as View
Env-->>UI: Read HOURS_DEBUG
UI->>Model: InitialModel(DB, style, debug)
activate Model
Model-->>UI: Initialized Model{debug}
deactivate Model
UI->>View: Start program with Model
sequenceDiagram
participant User as User
participant Update as Update loop
participant Model as Model.message (userMsg)
participant View as View/status bar
User->>Update: Trigger action
Update->>Model: set m.message = errMsg(...) / errMsgQuick(...)
loop per frame
Update->>Model: if framesLeft > 0 then framesLeft--
View->>Model: read value and framesLeft
alt framesLeft > 0 and value != ""
View-->>User: render status message
else
View-->>User: empty status bar
end
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Poem
Tip 🔌 Remote MCP (Model Context Protocol) integration is now available!Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats. 📜 Recent review detailsConfiguration used: CodeRabbit UI 📒 Files selected for processing (1)
✨ Finishing Touches🧪 Generate unit tests
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Summary of Changes
Hello @dhth, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!
The pull request enhances the user experience by making transient UI messages, such as error or informational alerts, persist for a short duration. Previously, these messages would disappear immediately, making them hard to notice. This change introduces a mechanism to display messages for a few frames, improving visibility and user feedback within the terminal UI.
Highlights
- Persistent UI Messages: Error and informational messages displayed to the user now remain visible for a configurable number of frames, preventing immediate disappearance.
- Structured Message Handling: The internal
Model
now uses a dedicateduserMsg
struct to manage messages, encapsulating the message content, its type (info or error), and its display duration. - Standardized Message Creation: New helper functions,
errMsg
anderrMsgQuick
, have been introduced to consistently create and assign these structured messages, simplifying message management across the application. - Improved User Feedback: By ensuring messages persist, users have more time to read and understand alerts, leading to a more intuitive and less frustrating interaction with the application.
Using Gemini Code Assist
The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.
Invoking Gemini
You can request assistance from Gemini at any point in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command>
or @gemini-code-assist <command>
. Below is a summary of the supported commands.
Feature | Command | Description |
---|---|---|
Code Review | /gemini review |
Performs a code review for the current pull request in its current state. |
Pull Request Summary | /gemini summary |
Provides a summary of the current pull request in its current state. |
Comment | @gemini-code-assist | Responds in comments when explicitly tagged, both in issue comments and review comments. |
Help | /gemini help |
Displays a list of available commands. |
Customization
To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/
folder in the base of the repository. Detailed instructions can be found here.
Limitations & Feedback
Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.
You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.
Footnotes
-
Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution. ↩
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code Review
This pull request introduces a mechanism to persist user messages for a few frames in the UI, which is a great improvement for user experience. The implementation is solid, introducing a userMsg
struct and helper functions to manage message display duration.
I've added a couple of suggestions:
- One to improve maintainability by removing a magic number.
- Another to fix a potential UI layout issue by re-introducing message trimming for the status bar.
Overall, great work on improving the user feedback mechanism.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🔭 Outside diff range comments (3)
internal/ui/handle.go (3)
698-704
: Critical: taking the address of a range variable stores the same pointer.Using &task inside a range loop captures the loop variable's address, which is reused each iteration; all pointers end up pointing to the last element. This breaks selection, updates, and any pointer-based lookup.
Apply this diff to materialize a new variable per iteration:
- for i, task := range msg.tasks { - task.UpdateListTitle() - task.UpdateListDesc() - tasks[i] = &task - m.taskMap[task.ID] = &task - m.taskIndexMap[task.ID] = i - } + for i := range msg.tasks { + t := msg.tasks[i] // copy to a new variable to take a stable address + t.UpdateListTitle() + t.UpdateListDesc() + tasks[i] = &t + m.taskMap[t.ID] = &t + m.taskIndexMap[t.ID] = i + }
712-716
: Same pointer-of-range bug for inactive tasks.All pointers will reference the last item.
Apply this diff:
- for i, inactiveTask := range msg.tasks { - inactiveTask.UpdateListTitle() - inactiveTask.UpdateListDesc() - inactiveTasks[i] = &inactiveTask - } + for i := range msg.tasks { + t := msg.tasks[i] + t.UpdateListTitle() + t.UpdateListDesc() + inactiveTasks[i] = &t + }
199-199
: Bug: clearing the wrong input collection (taskInputs vs tLInputs).In editActiveTLView escape handling, this should clear time-log inputs, not task inputs.
Apply this diff:
- m.taskInputs[entryBeginTS].SetValue("") + m.tLInputs[entryBeginTS].SetValue("")
♻️ Duplicate comments (2)
internal/ui/model.go (1)
174-180
: Derive “quick” frames from the default to avoid magic numbers.
Make the relationship explicit so future tuning stays consistent.func errMsgQuick(msg string) userMsg { return userMsg{ value: msg, kind: userMsgErr, - framesLeft: 2, + framesLeft: userMsgDefaultFrames - 1, } }internal/ui/view.go (1)
35-37
: Reintroduce trimming to avoid layout break with long messages.
Directly rendering long messages can overflow the status bar. Trim to terminal width.- if m.message.framesLeft > 0 && m.message.value != "" { - statusBar = m.message.value - } + if m.message.framesLeft > 0 && m.message.value != "" { + statusBar = utils.Trim(m.message.value, m.terminalWidth) + }
🧹 Nitpick comments (14)
internal/ui/model.go (3)
82-94
: Typed, frame-limited user message model is a solid foundation. Minor nit on type.
The structure and kind enum look good. Consider shrinking framesLeft to uint8 (or using int) to avoid unsigned arithmetic footguns and to better communicate the small domain.Apply this diff to narrow the field type:
type userMsg struct { value string kind userMsgKind - framesLeft uint + framesLeft uint8 }
129-129
: Debug flag on Model is fine; consider harmonizing env handling across the app.
You now have DEBUG (for logging) and HOURS_DEBUG (for UI footer). Optional: unify semantics/naming or doc both in README for clarity.
165-172
: Add a symmetric infoMsg helper (optional).
You already model info via userMsgInfo; adding a helper improves consistency with errMsg/errMsgQuick.Example to add near these helpers:
func infoMsg(msg string) userMsg { return userMsg{ value: msg, kind: userMsgInfo, framesLeft: userMsgDefaultFrames, } }internal/ui/ui.go (1)
23-25
: Environment switch for UI debug is fine; consider broader truthiness.
Optional: accept any non-empty HOURS_DEBUG (or common truthy forms) instead of only "1", or document this strictness.- debug := os.Getenv("HOURS_DEBUG") == "1" + // Alternative: treat any non-empty as enabled + debug := os.Getenv("HOURS_DEBUG") != ""internal/ui/view.go (1)
306-314
: Enrich debug footer (optional).
Consider including message kind for quick diagnostics; numeric enum is fine for debug.- if m.debug { - footer = fmt.Sprintf("%s [term: %dx%d] [msg frames left: %d]", - footer, - m.terminalWidth, - m.terminalHeight, - m.message.framesLeft, - ) - } + if m.debug { + footer = fmt.Sprintf("%s [term: %dx%d] [msg frames left: %d] [msg kind: %d]", + footer, + m.terminalWidth, + m.terminalHeight, + m.message.framesLeft, + m.message.kind, + ) + }internal/ui/update.go (2)
39-46
: TTL countdown logic is correct; small simplification possible.
Combine the two checks to avoid the second branch when framesLeft is 0.-if m.message.framesLeft > 0 { - m.message.framesLeft-- -} - -if m.message.framesLeft == 0 { - m.message.value = "" -} +if m.message.framesLeft > 0 { + m.message.framesLeft-- + if m.message.framesLeft == 0 { + m.message.value = "" + } +}
39-46
: Optional: make TTL time-based instead of event-based.
Right now TTL advances only on Update events. If you want “for a while” to mean wall-clock time, add a tea.Tick and decrement frames on ticks instead of key events.internal/ui/handle.go (7)
27-27
: Consistent use of wrappers; nit: reuse genericErrorMsg constant.
- Line 27: Good switch to errMsg for actionable validation feedback.
- Line 39: Prefer the existing genericErrorMsg constant over duplicating the literal.
Apply this diff:
- m.message = errMsg("Something went wrong") + m.message = errMsg(genericErrorMsg)Also applies to: 39-39
362-362
: Consider making this info message shorter-lived (errMsgQuick)."Nothing is being tracked right now" is ephemeral guidance; errMsgQuick may be a better UX fit unless you intentionally want a longer TTL.
Apply this diff:
- m.message = errMsg("Nothing is being tracked right now") + m.message = errMsgQuick("Nothing is being tracked right now")
688-688
: Fix spacing around colon in error message."Error fetching tasks: ..." reads correctly; current string has an extra space before the colon.
Apply this diff:
- m.message = errMsg("Error fetching tasks : " + msg.err.Error()) + m.message = errMsg("Error fetching tasks: " + msg.err.Error())
862-862
: Consider longer-lived or sticky message for reload suggestion.suggestReloadingMsg is multi-clause guidance that users may miss with a short TTL (errMsg framesLeft=3). Consider a longer-lived variant (e.g., errMsgFrames(5)) or a “sticky until next action” mode for such remedial instructions.
Also applies to: 872-872
208-211
: Nit: avoid resetting the comment input inside the loop.m.tLCommentInput.SetValue("") is called once per iteration; move it out of the loop.
Apply this diff:
- for i := range m.tLInputs { - m.tLInputs[i].SetValue("") - m.tLCommentInput.SetValue("") - } + for i := range m.tLInputs { + m.tLInputs[i].SetValue("") + } + m.tLCommentInput.SetValue("")
489-490
: Standardize timestamp precision for symmetry.Start timestamps are truncated to the second (Line 489), but quick-switch uses time.Now() (Line 533) without truncation. For consistent display/comparison and to avoid off-by-subsecond surprises, consider truncating both.
Apply this diff:
- return quickSwitchActiveIssue(m.db, task.ID, time.Now()) + return quickSwitchActiveIssue(m.db, task.ID, time.Now().Truncate(time.Second))Also applies to: 533-533
439-451
: Optional copy tweak for deactivation message– The “remove filter first” guardrail is clear and concise.
– The deactivation message can be smoothed out stylistically:- m.message = errMsg("Cannot deactivate a task being tracked; stop tracking and try again.") + m.message = errMsg("Cannot deactivate a task that's being tracked. Stop tracking and try again.")– Confirmed:
msgCouldntSelectATask
is defined ininternal/ui/update.go
(line 17), so this reference is safe.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (6)
internal/ui/handle.go
(28 hunks)internal/ui/initial.go
(2 hunks)internal/ui/model.go
(3 hunks)internal/ui/ui.go
(1 hunks)internal/ui/update.go
(6 hunks)internal/ui/view.go
(2 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (4)
internal/ui/initial.go (1)
internal/ui/model.go (1)
Model
(95-130)
internal/ui/ui.go (1)
internal/ui/initial.go (1)
InitialModel
(18-112)
internal/ui/model.go (1)
internal/ui/msgs.go (8)
struct{}
(9-9)err
(64-66)tsk
(68-72)err
(48-50)tlID
(36-40)entries
(58-62)tsk
(74-78)tsk
(26-29)
internal/ui/update.go (2)
internal/ui/cmds.go (1)
updateTask
(156-161)internal/ui/msgs.go (1)
tsk
(68-72)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: live-tests (ubuntu-latest)
🔇 Additional comments (29)
internal/ui/model.go (2)
76-80
: Good call: central default for message lifespan.
Introducing userMsgDefaultFrames centralizes tuning of the TTL. Looks good.
124-124
: No legacym.message
string assignments or comparisons found
Our regex scans returned no instances of:
m.message = "<literal>"
m.message == "<literal>"
or!=
- raw‐string uses with backticks
It appears all call sites have been updated to use the new structured type. You can consider this migration complete.
internal/ui/initial.go (2)
75-76
: Plumbing the debug flag into Model looks correct.
No concerns here.
18-18
: No stale InitialModel call sites remain—resolved.I ran a repository-wide search for
InitialModel(
and found only:
- internal/ui/ui.go:25 — already updated to
InitialModel(db, style, debug)
No other usages (tests, tools, etc.) exist.
internal/ui/ui.go (1)
28-29
: Simplified return path from p.Run is cleaner.
Nice tightening.internal/ui/update.go (1)
205-206
: Consistent use of errMsg wrapper—LGTM.
Centralizing message creation via helpers keeps behavior uniform and ties into the frame-limited model nicely.Also applies to: 287-288, 293-294, 305-306, 336-337, 349-350
internal/ui/handle.go (23)
20-20
: Capitalization tweak improves consistency."Time spent needs to be at least a minute" reads better and matches sentence-case style elsewhere.
53-58
: Right choice: transient parsing/future-time errors use errMsgQuick.Using errMsgQuick for parse and future-time validation keeps these ephemeral. Looks good.
75-75
: LGTM on transient error handling in finish-tracking flow.Consistent use of errMsgQuick for parse/future checks.
Also applies to: 81-81, 89-89, 94-94
133-133
: LGTM on messaging for manual create/edit TL path.Appropriate mix of errMsgQuick for parse/future checks and errMsg for generic selection failure.
Also applies to: 139-139, 145-145, 150-150, 174-174, 182-182
371-371
: LGTM: generic error on missing active index.Appropriate fallback.
413-413
: LGTM: generic error on invalid selection in edit-saved flow.
460-460
: LGTM: clear message for failed TL deletion selection.
468-474
: LGTM: consistent "remove filter" and generic error usage in activation flow.
485-485
: LGTM: generic error on missing active task selection.
514-514
: LGTM: generic error on quick-switch selection failure.
538-538
: LGTM: reuse of removeFilterMsg for task-creation guard.
550-556
: LGTM: remove-filter guard and generic error on update selection.
608-608
: LGTM: generic error on TL details selection failure.
725-725
: LGTM: surfacing DB errors to the user via errMsg.
742-742
: LGTM: consistent error handling for saved TL edits.
759-759
: LGTM: early return on TL fetch errors with errMsg.
786-786
: LGTM: consistent error surfacing for active task fetch.
816-816
: LGTM: consistent error reporting on tracking toggle failure.
826-826
: LGTM: genericErrorMsg on missing task map entry is appropriate.
854-854
: LGTM: error handling in quick-switch path.
886-886
: LGTM: scoped error message with context on TL delete failure.
902-902
: LGTM: clear messages for active TL delete path and generic fallback.Also applies to: 909-909
922-922
: LGTM: quick messages for duration validation, aligned with form UX.Also applies to: 927-927
Summary by CodeRabbit
New Features
Improvements
Refactor