Skip to content

Improve event system structure and add reusable TUI components#50

Merged
silv-io merged 14 commits intomainfrom
silv-io/flc-369
Mar 2, 2026
Merged

Improve event system structure and add reusable TUI components#50
silv-io merged 14 commits intomainfrom
silv-io/flc-369

Conversation

@silv-io
Copy link
Member

@silv-io silv-io commented Feb 25, 2026

Motivation

The current event system has fragmented types (LogEvent, WarningEvent) that don't scale well as we add more UI feedback patterns. We also lack reusable TUI components for common patterns like spinners and structured error display.

This PR introduces a unified event system that makes it easier to:

  • Distinguish between informational messages, successes, warnings, and notes
  • Show/hide loading spinners with explicit control
  • Display rich, structured errors with recovery actions

Changes

New event types in internal/output/:

  • MessageEvent with severity levels (Info, Success, Note, Warning) — replaces LogEvent/WarningEvent
  • SpinnerEvent for explicit spinner start/stop control
  • ErrorEvent for structured errors with title, summary, detail, and suggested actions

New TUI components in internal/ui/components/:

  • Spinner — wraps Bubble Tea's spinner with simple Start/Stop API
  • RenderMessage() — styled message rendering by severity
  • ErrorDisplay — rich error panel with actions

Backward compatible: EmitLog() and EmitWarning() still work (marked deprecated, forward to MessageEvent).

Tests

  • Unit tests for all new event formatting
  • Component tests for Spinner and ErrorDisplay
  • App integration tests for event handling
  • Updated existing tests to use new event types
go test ./internal/output/... ./internal/ui/...

Follow-ups

  • Use EmitSpinnerStart/EmitSpinnerStop in start command during image pull (or introduce a loading bar -> we'll see what's better)
  • Use EmitError for Docker connection failures and auth errors

Related

@coderabbitai
Copy link

coderabbitai bot commented Feb 25, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR refactors the event system to replace LogEvent and WarningEvent with a severity-based MessageEvent type (Info, Success, Note, Warning), introduces SpinnerEvent and ErrorEvent for enhanced UI feedback, and adds corresponding UI components (Spinner, ErrorDisplay) to the TUI application. All callers and tests are updated to use the new event types, and new formatting handlers are introduced to render these events in the plain and TUI sinks.

Changes

Cohort / File(s) Summary
Event System Refactoring
internal/output/events.go
Added public MessageEvent with severity levels (SeverityInfo, SeveritySuccess, SeverityNote, SeverityWarning), SpinnerEvent, and ErrorEvent. Removed public LogEvent and WarningEvent. Added new emitters: EmitInfo, EmitSuccess, EmitNote, EmitSpinnerStart, EmitSpinnerStop, EmitError. Marked EmitLog as deprecated (now maps to MessageEvent with SeverityInfo).
Event Formatting & Sinks
internal/output/plain_format.go, internal/output/plain_format_test.go
Replaced LogEvent/WarningEvent handling with MessageEvent, SpinnerEvent, and ErrorEvent cases. Added formatMessageEvent helper for severity-based prefixes and formatErrorEvent for multi-line error rendering with actions. Added comprehensive unit tests.
Sink Test Updates
internal/output/format_test.go, internal/output/plain_sink_test.go, internal/output/tui_sink_test.go
Updated test cases to emit MessageEvent variants instead of LogEvent/WarningEvent. Added tests for spinner and error event rendering. Adjusted expected outputs and prefixes (e.g., "> Warning: ")
UI Spinner Component
internal/ui/components/spinner.go, internal/ui/components/spinner_test.go
Added new Spinner component wrapping bubbles/spinner.Model with methods: Start(), Stop(), Visible(), Update(), View(), Tick(). Provides fluent-style control over spinner visibility and rendering.
UI Error Display Component
internal/ui/components/error_display.go, internal/ui/components/error_display_test.go
Added new ErrorDisplay component to render ErrorEvent with title, summary, detail, and actionable items. Methods: Show(), Visible(), View()
UI Message Rendering
internal/ui/components/message.go
Added RenderMessage() helper to format MessageEvent based on severity with styled prefixes (Success, Note, Warning) or plain text.
TUI App Integration
internal/ui/app.go, internal/ui/app_test.go
Added spinner and errorDisplay fields to App struct. Updated Init() to delegate to spinner tick, Update() to handle SpinnerEvent, ErrorEvent, and MessageEvent, and View() to render spinner above messages and error display below. Added tests for spinner lifecycle, message rendering, and error handling.
Caller Updates
internal/auth/auth.go, internal/auth/login.go, internal/container/start.go
Replaced EmitLog() calls with EmitInfo(), EmitSuccess(), or EmitWarning() based on message semantics. No logic changes.
Test Updates
internal/auth/auth_test.go
Updated test assertion to check for MessageEvent with SeverityWarning instead of WarningEvent.
Styling
internal/ui/styles/styles.go
Added new public style variables: Success, Note, Warning, Secondary, ErrorTitle, ErrorDetail, ErrorAction, SpinnerStyle with corresponding foreground colors and formatting.
Dependencies
go.mod
Added indirect dependency github.com/charmbracelet/bubbles v1.0.0 and updated github.com/charmbracelet/x/exp/golden to v0.0.0-20241011142426-46044092ad91.

Sequence Diagram(s)

sequenceDiagram
    participant App as App<br/>(TUI)
    participant Sink as Output<br/>Sink
    participant Formatter as Formatter
    participant Display as Display<br/>Components

    rect rgba(100, 150, 200, 0.5)
    note over App,Display: MessageEvent Flow
    App->>Sink: EmitInfo/Success/Warning/Note
    Sink->>App: MessageEvent
    App->>Formatter: FormatEventLine(MessageEvent)
    Formatter->>Display: styled text with prefix
    Display->>App: rendered line
    end

    rect rgba(100, 200, 150, 0.5)
    note over App,Display: SpinnerEvent Flow
    App->>Sink: EmitSpinnerStart/Stop
    Sink->>App: SpinnerEvent (Active)
    App->>App: spinner.Start(text)
    App->>Display: spinner.View()
    Display->>App: "Loading..."
    end

    rect rgba(200, 150, 100, 0.5)
    note over App,Display: ErrorEvent Flow
    App->>Sink: EmitError(ErrorEvent)
    Sink->>App: ErrorEvent
    App->>App: errorDisplay.Show(event)
    App->>App: spinner.Stop()
    App->>Display: errorDisplay.View()
    Display->>App: formatted error with title/summary/actions
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • carole-lavillonniere
🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main changes: introducing a unified event system and adding reusable TUI components for spinners and error display.
Description check ✅ Passed The description is comprehensive and directly related to the changeset, detailing motivation, new event types, TUI components, backward compatibility, testing approach, and follow-ups.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch silv-io/flc-369

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copy link

@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: 3

🧹 Nitpick comments (1)
internal/ui/styles/styles.go (1)

31-57: Consider removing self-explanatory section comments.

The grouping comments here restate what the code already makes obvious; keeping just semantic variable names is cleaner.

As per coding guidelines **/*.go: Don't add comments for self-explanatory code. Only comment when the "why" isn't obvious from the code itself.

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

In `@internal/ui/styles/styles.go` around lines 31 - 57, Remove the
self-explanatory grouping comments and leave the semantic style variables as-is;
specifically delete the comment lines "Message severity styles",
"Secondary/muted style for prefixes", "Error styles" and "Spinner style" while
keeping the style declarations (Success, Note, Warning, Secondary, ErrorTitle,
ErrorDetail, ErrorAction, SpinnerStyle) and their formatting intact so only
non-actionable, redundant comments are removed.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@internal/output/events.go`:
- Around line 126-128: The deprecation comment on EmitWarning is
self-referential; update the doc comment to point users to the non-deprecated
alternative by instructing them to call Emit with a MessageEvent (e.g., use
Emit(sink, MessageEvent{Severity: SeverityWarning, Text: ...})); modify the
comment above func EmitWarning to mention Emit and MessageEvent/SeverityWarning
explicitly instead of referencing EmitWarning itself.

In `@internal/ui/components/message.go`:
- Around line 8-19: RenderMessage currently duplicates severity label/prefix
logic; replace that logic by calling the shared formatter
output.FormatEventLine(e) to get the canonical line semantics and then apply TUI
styling. Concretely, remove the switch in RenderMessage, call formatted :=
output.FormatEventLine(e), and return the TUI prefix (styles.Secondary.Render(">
")) plus the styled formatted string (e.g. styles.Message.Render(formatted)) so
the same semantics are produced by FormatEventLine while keeping TUI-specific
styling in RenderMessage.

In `@internal/ui/components/spinner_test.go`:
- Around line 51-54: The test assigns the updated spinner to variable s from
spinner.Update but never uses it, causing SA4006; change the assignment to use
the blank identifier so the linter is satisfied—replace the left-hand variable
`s` with `_` in the call to `spinner.Update` (e.g., use `_, cmd :=
spinner.Update(spinner.TickMsg{})`) so `cmd` is still captured and `s` is not
unused.

---

Nitpick comments:
In `@internal/ui/styles/styles.go`:
- Around line 31-57: Remove the self-explanatory grouping comments and leave the
semantic style variables as-is; specifically delete the comment lines "Message
severity styles", "Secondary/muted style for prefixes", "Error styles" and
"Spinner style" while keeping the style declarations (Success, Note, Warning,
Secondary, ErrorTitle, ErrorDetail, ErrorAction, SpinnerStyle) and their
formatting intact so only non-actionable, redundant comments are removed.

ℹ️ Review info

Configuration used: Repository UI (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3200ba4 and 53aa465.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (15)
  • go.mod
  • internal/auth/auth_test.go
  • internal/output/events.go
  • internal/output/format.go
  • internal/output/format_test.go
  • internal/output/plain_sink_test.go
  • internal/output/tui_sink_test.go
  • internal/ui/app.go
  • internal/ui/app_test.go
  • internal/ui/components/error_display.go
  • internal/ui/components/error_display_test.go
  • internal/ui/components/message.go
  • internal/ui/components/spinner.go
  • internal/ui/components/spinner_test.go
  • internal/ui/styles/styles.go

Copy link

@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 (2)
internal/container/start.go (1)

216-223: Consider migrating the remaining EmitWarning calls in this file.

Lines 216 and 222 still use the deprecated EmitWarning. While these are backward-compatible (they forward to MessageEvent internally), migrating them now keeps this file consistent with the changes introduced in this PR and reduces future churn.

♻️ Proposed migration of EmitWarning calls
-			output.EmitWarning(sink, fmt.Sprintf("failed to close response body: %v", err))
+			output.EmitWarning(sink, fmt.Sprintf("failed to close response body: %v", err)) // or EmitWarning equivalent via MessageEvent if an EmitWarning helper exists

Verify what the new severity-tagged equivalent of EmitWarning is (e.g., output.EmitWarning already forwarding, or a new EmitWarning/EmitNote), then apply consistently to both occurrences.

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

In `@internal/container/start.go` around lines 216 - 223, Replace the deprecated
output.EmitWarning(sink, ...) calls with the new severity-tagged message API
used elsewhere in the PR: call output.MessageEvent(sink, output.SeverityWarning,
fmt.Sprintf("failed to close response body: %v", err)) (or the project's
equivalent helper that accepts a severity and message) for both occurrences
around resp.Body.Close and the early-return block; keep the same fmt.Sprintf
text and the same sink and error variable names so behavior and context remain
unchanged.
internal/auth/login.go (1)

87-103: Consider SpinnerEvent for the three transient progress steps in completeAuth.

Lines 87, 95, and 102 are intermediate loading states ("Checking…", "exchanging…", "Fetching…") that are immediately superseded by the next step or by an error. They're semantically SpinnerStart/Stop pairs rather than persistent Info messages. The PR already notes this migration as a follow-up ("Use EmitSpinnerStart/EmitSpinnerStop in relevant commands"), so tagging for awareness.

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

In `@internal/auth/login.go` around lines 87 - 103, Replace the three transient
output.EmitInfo calls in completeAuth (the calls emitting "Checking if auth
request is confirmed...", "Auth request confirmed, exchanging for token...", and
"Fetching license token...") with spinner events: call
output.EmitSpinnerStart(l.sink, "<message>") before each async operation
(CheckAuthRequestConfirmed, ExchangeAuthRequest, GetLicenseToken) and ensure
matching output.EmitSpinnerStop(l.sink) on both success paths and in error
returns so spinners are stopped when an error occurs; keep the same messages and
leave non-transient final messages as EmitInfo.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@internal/auth/login.go`:
- Around line 87-103: Replace the three transient output.EmitInfo calls in
completeAuth (the calls emitting "Checking if auth request is confirmed...",
"Auth request confirmed, exchanging for token...", and "Fetching license
token...") with spinner events: call output.EmitSpinnerStart(l.sink,
"<message>") before each async operation (CheckAuthRequestConfirmed,
ExchangeAuthRequest, GetLicenseToken) and ensure matching
output.EmitSpinnerStop(l.sink) on both success paths and in error returns so
spinners are stopped when an error occurs; keep the same messages and leave
non-transient final messages as EmitInfo.

In `@internal/container/start.go`:
- Around line 216-223: Replace the deprecated output.EmitWarning(sink, ...)
calls with the new severity-tagged message API used elsewhere in the PR: call
output.MessageEvent(sink, output.SeverityWarning, fmt.Sprintf("failed to close
response body: %v", err)) (or the project's equivalent helper that accepts a
severity and message) for both occurrences around resp.Body.Close and the
early-return block; keep the same fmt.Sprintf text and the same sink and error
variable names so behavior and context remain unchanged.

ℹ️ Review info

Configuration used: Repository UI (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 53aa465 and 5836aba.

📒 Files selected for processing (5)
  • internal/auth/auth.go
  • internal/auth/login.go
  • internal/container/start.go
  • internal/output/events.go
  • internal/ui/components/spinner_test.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • internal/ui/components/spinner_test.go
  • internal/output/events.go

@silv-io silv-io marked this pull request as draft February 25, 2026 16:48
@silv-io silv-io marked this pull request as ready for review February 27, 2026 12:20
Copy link

@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)
internal/output/plain_sink_test.go (1)

172-184: Consider adding Detail assertion in this plain-sink error test.

This case verifies title/summary/actions, but not ErrorEvent.Detail. Adding one Detail case here would strengthen sink-level parity for multiline error rendering.

Based on learnings, "When adding a new event type, update all of: internal/output/events.go (event type + union + emit helper), internal/output/format.go (line formatting fallback), tests in internal/output/*_test.go for formatter/sink behavior parity".

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

In `@internal/output/plain_sink_test.go` around lines 172 - 184, Add an
ErrorEvent.Detail to the TestPlainSink_EmitsErrorEvent case and update the
expected output to include the detail line(s) rendered by the plain sink;
specifically, in the TestPlainSink_EmitsErrorEvent test where Emit(sink,
ErrorEvent{...}) is called (and NewPlainSink, ErrorAction, out.String() are
used), add a Detail field to the ErrorEvent and extend the expected string to
include the plain-sink formatting for Detail so the test asserts title, summary,
detail, and actions parity.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@internal/output/plain_sink_test.go`:
- Around line 172-184: Add an ErrorEvent.Detail to the
TestPlainSink_EmitsErrorEvent case and update the expected output to include the
detail line(s) rendered by the plain sink; specifically, in the
TestPlainSink_EmitsErrorEvent test where Emit(sink, ErrorEvent{...}) is called
(and NewPlainSink, ErrorAction, out.String() are used), add a Detail field to
the ErrorEvent and extend the expected string to include the plain-sink
formatting for Detail so the test asserts title, summary, detail, and actions
parity.

ℹ️ Review info

Configuration used: Repository UI (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5836aba and 7b4557c.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (18)
  • go.mod
  • internal/auth/auth.go
  • internal/auth/auth_test.go
  • internal/auth/login.go
  • internal/container/start.go
  • internal/output/events.go
  • internal/output/format.go
  • internal/output/format_test.go
  • internal/output/plain_sink_test.go
  • internal/output/tui_sink_test.go
  • internal/ui/app.go
  • internal/ui/app_test.go
  • internal/ui/components/error_display.go
  • internal/ui/components/error_display_test.go
  • internal/ui/components/message.go
  • internal/ui/components/spinner.go
  • internal/ui/components/spinner_test.go
  • internal/ui/styles/styles.go
🚧 Files skipped from review as they are similar to previous changes (9)
  • internal/output/tui_sink_test.go
  • go.mod
  • internal/auth/auth.go
  • internal/ui/app_test.go
  • internal/ui/components/spinner.go
  • internal/auth/login.go
  • internal/ui/components/error_display_test.go
  • internal/ui/components/spinner_test.go
  • internal/ui/components/error_display.go

Copy link

@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: 1

🧹 Nitpick comments (1)
internal/output/events.go (1)

126-128: Add deprecation notice to EmitWarning.

EmitLog has a deprecation comment directing to EmitInfo, but EmitWarning lacks similar guidance. Consider adding a deprecation notice or clarifying its status for consistency.

+// Deprecated: Use Emit(sink, MessageEvent{Severity: SeverityWarning, Text: message}) for direct emission
 func EmitWarning(sink Sink, message string) {
 	Emit(sink, MessageEvent{Severity: SeverityWarning, Text: message})
 }

Alternatively, if EmitWarning is intended to remain as a convenience helper (not deprecated), that's fine—just ensure the intent is clear.

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

In `@internal/output/events.go` around lines 126 - 128, The EmitWarning helper
lacks any deprecation or intent comment; update the declaration for EmitWarning
to either add a deprecation doc comment pointing users to the preferred API
(e.g., "Deprecated: use EmitInfo or Emit with SeverityWarning") or add a clear
comment stating it is an intentional convenience helper that wraps
Emit/MessageEvent with SeverityWarning; reference EmitWarning, Emit,
MessageEvent, SeverityWarning and EmitInfo/EmitLog in the comment so readers
know the recommended alternative or that it is intentionally retained.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@go.mod`:
- Line 29: Move the module github.com/charmbracelet/bubbles from the indirect
list into the main require block of go.mod because it is directly imported (see
imports of github.com/charmbracelet/bubbles in your code). Edit the require
block to include github.com/charmbracelet/bubbles with the current version
(v1.0.0) and remove its indirect entry, then re-run go mod tidy to update the
module graph so imports in internal/ui (where bubbles is used) resolve as a
direct dependency.

---

Nitpick comments:
In `@internal/output/events.go`:
- Around line 126-128: The EmitWarning helper lacks any deprecation or intent
comment; update the declaration for EmitWarning to either add a deprecation doc
comment pointing users to the preferred API (e.g., "Deprecated: use EmitInfo or
Emit with SeverityWarning") or add a clear comment stating it is an intentional
convenience helper that wraps Emit/MessageEvent with SeverityWarning; reference
EmitWarning, Emit, MessageEvent, SeverityWarning and EmitInfo/EmitLog in the
comment so readers know the recommended alternative or that it is intentionally
retained.

ℹ️ Review info

Configuration used: Repository UI (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7b4557c and 1154ca6.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (18)
  • go.mod
  • internal/auth/auth.go
  • internal/auth/auth_test.go
  • internal/auth/login.go
  • internal/container/start.go
  • internal/output/events.go
  • internal/output/format.go
  • internal/output/format_test.go
  • internal/output/plain_sink_test.go
  • internal/output/tui_sink_test.go
  • internal/ui/app.go
  • internal/ui/app_test.go
  • internal/ui/components/error_display.go
  • internal/ui/components/error_display_test.go
  • internal/ui/components/message.go
  • internal/ui/components/spinner.go
  • internal/ui/components/spinner_test.go
  • internal/ui/styles/styles.go
🚧 Files skipped from review as they are similar to previous changes (9)
  • internal/ui/components/error_display_test.go
  • internal/output/tui_sink_test.go
  • internal/auth/login.go
  • internal/ui/styles/styles.go
  • internal/ui/components/error_display.go
  • internal/ui/components/spinner_test.go
  • internal/output/format_test.go
  • internal/container/start.go
  • internal/ui/components/message.go

github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/aymanbagabas/go-udiff v0.3.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/charmbracelet/bubbles v1.0.0 // indirect
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify whether bubbles is directly imported by project source.

echo "=== Non-test files importing bubbles ==="
rg -n --type=go '"github.com/charmbracelet/bubbles' -g '!**/*_test.go'

echo ""
echo "=== Test files importing bubbles ==="
rg -n --type=go '"github.com/charmbracelet/bubbles' -g '**/*_test.go'

Repository: localstack/lstk

Length of output: 367


🏁 Script executed:

#!/bin/bash
# Check the current go.mod structure for bubbles
echo "=== go.mod direct requires section ==="
head -40 go.mod | cat -n

echo ""
echo "=== Search for bubbles in entire go.mod ==="
rg -n 'charmbracelet/bubbles' go.mod

Repository: localstack/lstk

Length of output: 2097


Move bubbles to direct dependencies in the require block

github.com/charmbracelet/bubbles is directly imported in internal/ui/app.go:7 and internal/ui/components/spinner.go:4, so it should be listed as a direct dependency for clearer ownership and stable go mod tidy behavior. Move it from the indirect section to the main require block.

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

In `@go.mod` at line 29, Move the module github.com/charmbracelet/bubbles from the
indirect list into the main require block of go.mod because it is directly
imported (see imports of github.com/charmbracelet/bubbles in your code). Edit
the require block to include github.com/charmbracelet/bubbles with the current
version (v1.0.0) and remove its indirect entry, then re-run go mod tidy to
update the module graph so imports in internal/ui (where bubbles is used)
resolve as a direct dependency.

Copy link
Collaborator

@carole-lavillonniere carole-lavillonniere left a comment

Choose a reason for hiding this comment

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

Added a few minor questions/suggestions but overall I approve the direction!

Copy link

@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: 1

🧹 Nitpick comments (2)
internal/output/plain_format.go (1)

105-109: Avoid unconditional space between error action label and value.

If ErrorAction.Value is empty, this emits a trailing space. Consider appending the value conditionally.

Suggested patch
 	for _, action := range e.Actions {
 		sb.WriteString("\n  → ")
 		sb.WriteString(action.Label)
-		sb.WriteString(" ")
-		sb.WriteString(action.Value)
+		if action.Value != "" {
+			sb.WriteString(" ")
+			sb.WriteString(action.Value)
+		}
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/output/plain_format.go` around lines 105 - 109, The loop over
e.Actions currently always writes a space before action.Value which produces a
trailing space when action.Value is empty; update the loop that iterates
e.Actions to only append the space and action.Value when action.Value is
non-empty (e.g., check if action.Value != "" then sb.WriteString(" ");
sb.WriteString(action.Value)), leaving sb.WriteString(action.Label) unchanged so
labels are still printed without an extra trailing space.
internal/output/plain_format_test.go (1)

14-37: Add one case for unknown MessageSeverity fallback.

A guard case for an out-of-range severity would lock in current default behavior and catch future enum-expansion regressions.

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

In `@internal/output/plain_format_test.go` around lines 14 - 37, Add a test case
to the table-driven tests in plain_format_test.go to exercise an out-of-range
MessageSeverity fallback: create a MessageEvent with a severity value cast from
an invalid integer (e.g., MessageSeverity(999) or MessageSeverity(-1)) and Text
set to something like "unknown", then assert the formatter still returns the
current default output (plain text) and wantOK true. Reference the existing
table entries using MessageEvent and the Severity constants (SeverityInfo,
SeveritySuccess, SeverityNote, SeverityWarning) and add the new case alongside
them so future enum expansions/regressions are caught.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@internal/output/events.go`:
- Around line 109-123: Add back the deprecated EmitLog helper to restore
backwards compatibility: implement a function EmitLog(sink Sink, text string)
that simply calls Emit(sink, MessageEvent{Severity: SeverityLog, Text: text})
(matching the pattern of EmitInfo/EmitSuccess/EmitNote/EmitWarning) so existing
call sites compile; keep the name EmitLog and the same signature and mark it as
deprecated in comments if desired.

---

Nitpick comments:
In `@internal/output/plain_format_test.go`:
- Around line 14-37: Add a test case to the table-driven tests in
plain_format_test.go to exercise an out-of-range MessageSeverity fallback:
create a MessageEvent with a severity value cast from an invalid integer (e.g.,
MessageSeverity(999) or MessageSeverity(-1)) and Text set to something like
"unknown", then assert the formatter still returns the current default output
(plain text) and wantOK true. Reference the existing table entries using
MessageEvent and the Severity constants (SeverityInfo, SeveritySuccess,
SeverityNote, SeverityWarning) and add the new case alongside them so future
enum expansions/regressions are caught.

In `@internal/output/plain_format.go`:
- Around line 105-109: The loop over e.Actions currently always writes a space
before action.Value which produces a trailing space when action.Value is empty;
update the loop that iterates e.Actions to only append the space and
action.Value when action.Value is non-empty (e.g., check if action.Value != ""
then sb.WriteString(" "); sb.WriteString(action.Value)), leaving
sb.WriteString(action.Label) unchanged so labels are still printed without an
extra trailing space.

ℹ️ Review info

Configuration used: Repository UI (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1154ca6 and feff9b7.

📒 Files selected for processing (3)
  • internal/output/events.go
  • internal/output/plain_format.go
  • internal/output/plain_format_test.go

@silv-io silv-io merged commit 36f4eed into main Mar 2, 2026
8 checks passed
@silv-io silv-io deleted the silv-io/flc-369 branch March 2, 2026 13:05
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