Skip to content

Conversation

dhth
Copy link
Owner

@dhth dhth commented Aug 17, 2025

Summary by CodeRabbit

  • New Features

    • Optional UI frame logging: set HOURS_LOG_FRAMES=1 to save each screen as plain-text files (ANSI colors stripped) in a hidden per-run ".frames/" folder; debug footer shows "[frames rendered: N]".
  • Bug Fixes

    • Clearer error when the frames directory cannot be created.
  • Chores

    • Added ".frames" to .gitignore.

Copy link

coderabbitai bot commented Aug 17, 2025

Walkthrough

Adds per-run, optional frame logging: a new internal type logFramesConfig and fields frameCounter + logFramesCfg on Model; InitialModel now accepts logFramesCfg. UI initialization may create a hidden .frames/<unix_timestamp> dir and passes config to the model. View strips ANSI and persists rendered frames when enabled.

Changes

Cohort / File(s) Summary
API: InitialModel signature
internal/ui/initial.go, internal/ui/ui.go
Changes InitialModel to accept logFramesCfg logFramesConfig and updates all call sites to pass the new config.
Model struct & config type
internal/ui/model.go
Adds internal type logFramesConfig { log bool; framesDir string } and adds frameCounter uint and logFramesCfg logFramesConfig fields to Model.
UI initialization & env handling
internal/ui/ui.go
Reads HOURS_LOG_FRAMES, derives logFrames boolean, creates ".frames/<unix_timestamp>" directory when enabled (uses time + filepath), introduces errCouldnCreateFramesDir, and constructs/passes logFramesCfg to InitialModel.
View rendering & frame logging
internal/ui/view.go
Adds ansiRegex and stripANSI; View builds result into a local var, appends debug footer with m.frameCounter when debug, increments/persists frames: calls logFrame (strips ANSI and writes <frameIndex>.txt into framesDir) when m.logFramesCfg.log is true.
Update loop counter
internal/ui/update.go
Increments m.frameCounter++ at the start of Model.Update on each update.
VCS ignore
.gitignore
Adds .frames pattern to ignore generated frames directories.

Sequence Diagram(s)

sequenceDiagram
  participant App
  participant UI_Init as UI:init
  participant FS as Filesystem
  participant Model
  participant View

  App->>UI_Init: Read env HOURS_LOG_FRAMES
  UI_Init->>FS: MkdirAll(".frames/<unix_ts>") [if enabled]
  FS-->>UI_Init: ok / error
  UI_Init->>Model: InitialModel(db, style, debug, logFramesCfg)

  loop updates
    App->>Model: Update()
    Model->>Model: frameCounter++
    App->>View: View()
    View->>View: render result (lipgloss.JoinVertical)
    alt logFrames enabled
      View->>View: stripANSI(result)
      View->>FS: Write "<frameIndex>.txt" into framesDir
      FS-->>View: ok / error
    end
    View-->>App: rendered output
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

I twitch my whiskers, nibble code,
Save each hop down the terminal road.
Colors gone, plain text to claim,
Little files in a hidden frame.
Hop, record — another tame 🥕

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.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch allow-logging-frames

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

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 (4)
internal/ui/ui.go (2)

12-15: Nit: variable name reads truncated; prefer clearer error identifier

errCouldnCreateFramesDir reads like a truncated “couldn’t”. Consider errCouldNotCreateFramesDir for clarity.

Apply this diff:

 var (
   errFailedToConfigureDebugging = errors.New("failed to configure debugging")
-  errCouldnCreateFramesDir      = errors.New("couldn't create frames directory")
+  errCouldNotCreateFramesDir    = errors.New("couldn't create frames directory")
 )

Follow-up: update references accordingly (see below).


31-31: Update usage to match the renamed error (if you apply the nit above)

-      return fmt.Errorf("%w: %s", errCouldnCreateFramesDir, err.Error())
+      return fmt.Errorf("%w: %s", errCouldNotCreateFramesDir, err.Error())
internal/ui/view.go (2)

33-33: ANSI stripping regex is minimal; consider more general CSI coverage

This only matches SGR color codes (ending in ‘m’). If the output ever includes other CSI sequences, broaden the regex:

-var ansiRegex = regexp.MustCompile(`\x1b\[[0-9;]*m`)
+// Match general ANSI CSI sequences (ESC [ ... final byte in @-~)
+var ansiRegex = regexp.MustCompile(`\x1b\[[0-9;?]*[ -/]*[@-~]`)

This remains safe and covers more cases without pulling a new dependency.


320-331: Side-effectful frame logging in View is acceptable for a debug flag; consider throttling if it becomes too noisy

As written, every render writes a file. That’s okay behind m.logFrames, but if perf/noise becomes an issue, consider deduping identical consecutive frames or sampling (e.g., every Nth frame).

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Knowledge Base: Disabled due to Reviews > Disable Knowledge Base setting

📥 Commits

Reviewing files that changed from the base of the PR and between d298983 and 5f06cd3.

📒 Files selected for processing (4)
  • internal/ui/initial.go (2 hunks)
  • internal/ui/model.go (1 hunks)
  • internal/ui/ui.go (2 hunks)
  • internal/ui/view.go (4 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (2)
internal/ui/initial.go (2)
internal/ui/styles.go (1)
  • Style (13-44)
internal/ui/model.go (1)
  • Model (95-131)
internal/ui/ui.go (1)
internal/ui/initial.go (1)
  • InitialModel (18-113)
⏰ 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 (7)
internal/ui/model.go (1)

130-130: Field addition to Model is straightforward and safe

New boolean flag defaults to false; naming and placement next to debug are sensible. No concerns.

internal/ui/initial.go (2)

76-77: Wiring the new flag into Model looks good

The new parameter is correctly assigned into the Model. No additional changes needed here.


18-18: All call sites updated — signature change complete

I ran the grep and verified the only call in internal/ui/ui.go now uses the 4-argument form (InitialModel(db, style, debug, logFrames)), so there are no remaining 3-arg invocations.

Optional: to avoid boolean blindness in the future, consider grouping flags into an options/config struct instead of passing multiple bools.

internal/ui/ui.go (2)

27-33: Frames directory setup is reasonable

Conditional creation with 0o755 and proper error wrapping is fine. Good use of %w to preserve the underlying error.


35-35: Passing the new flag through to InitialModel is correct

Call site updated to include logFrames. Looks good.

internal/ui/view.go (2)

5-8: Imports added for frame logging are appropriate

os, path/filepath, and regexp are all used by the new helpers. No issues.


421-423: ANSI stripping helper looks good

Simple and effective; pairs well with the precompiled regex.

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: 0

♻️ Duplicate comments (1)
internal/ui/view.go (1)

413-420: Fix shadowing of the filepath package and tighten file perms (frames may contain sensitive content)

The local variable named filepath shadows the imported package name and is confusing. Also, frames can include task details; default 0644 makes them world-readable. Prefer a distinct variable name and 0600 perms.

Apply this diff:

-	filename := fmt.Sprintf("%d.txt", frameIndex)
-	filepath := filepath.Join(framesDir, filename)
-
-	_ = os.WriteFile(filepath, []byte(cleanContent), 0o644)
+	filename := fmt.Sprintf("%d.txt", frameIndex)
+	outPath := filepath.Join(framesDir, filename)
+
+	// Restrictive perms; frames can contain task details
+	_ = os.WriteFile(outPath, []byte(cleanContent), 0o600)
🧹 Nitpick comments (4)
internal/ui/model.go (1)

135-137: Minor type nit: consider avoiding architecture-dependent uint

Using plain uint ties the width to the architecture. If you ever serialize or compare across processes, consider int or uint64 for consistency. Not blocking.

internal/ui/view.go (1)

33-34: ANSI stripping regex is narrowly scoped to SGR “m”; consider a more general CSI matcher

If you ever render other control sequences (cursor movement, erase line, etc.), this won’t strip them. Optional, but safer long-term.

Apply this diff:

-var ansiRegex = regexp.MustCompile(`\x1b\[[0-9;]*m`)
+// Match CSI sequences broadly: ESC [ <params> <intermediates> <final-byte>
+var ansiRegex = regexp.MustCompile(`\x1b\[[0-?]*[ -/]*[@-~]`)
internal/ui/ui.go (2)

34-40: Avoid per-run directory collisions and prefer private perms

Two processes started within the same second could share the same “.frames/<unix_seconds>” directory. Also, for privacy, prefer 0700 over 0755.

Apply this diff:

-		framesDir := filepath.Join(".frames", fmt.Sprintf("%d", time.Now().Unix()))
-		err := os.MkdirAll(framesDir, 0o755)
+		base := ".frames"
+		// Ensure base exists with private perms
+		if err := os.MkdirAll(base, 0o700); err != nil {
+			return fmt.Errorf("%w: %s", errCouldnCreateFramesDir, err.Error())
+		}
+		// Use higher-resolution timestamp to reduce collision risk
+		framesDir := filepath.Join(base, fmt.Sprintf("%d", time.Now().UnixNano()))
+		err := os.MkdirAll(framesDir, 0o700)

16-16: Nit: fix typo in error identifier

Minor readability polish: errCouldnCreateFramesDir → errCouldntCreateFramesDir.

Apply this diff:

-	errCouldnCreateFramesDir      = errors.New("couldn't create frames directory")
+	errCouldntCreateFramesDir     = errors.New("couldn't create frames directory")

And update its usage:

-			return fmt.Errorf("%w: %s", errCouldnCreateFramesDir, err.Error())
+			return fmt.Errorf("%w: %s", errCouldntCreateFramesDir, err.Error())

Also applies to: 37-37

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Knowledge Base: Disabled due to Reviews > Disable Knowledge Base setting

📥 Commits

Reviewing files that changed from the base of the PR and between 5f06cd3 and 1e176bc.

📒 Files selected for processing (6)
  • .gitignore (1 hunks)
  • internal/ui/initial.go (2 hunks)
  • internal/ui/model.go (2 hunks)
  • internal/ui/ui.go (2 hunks)
  • internal/ui/update.go (1 hunks)
  • internal/ui/view.go (4 hunks)
✅ Files skipped from review due to trivial changes (1)
  • .gitignore
🚧 Files skipped from review as they are similar to previous changes (1)
  • internal/ui/initial.go
🧰 Additional context used
🧬 Code Graph Analysis (1)
internal/ui/ui.go (1)
internal/ui/initial.go (1)
  • InitialModel (18-113)
⏰ 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 (4)
internal/ui/update.go (1)

21-21: Incrementing frameCounter at the start of Update looks correct

This ensures a monotonic per-update index that View can use for logging and the debug footer.

internal/ui/model.go (1)

95-99: Config object for frame logging is clear and appropriately unexported

logFramesConfig cleanly scopes logging concerns within the ui package. No issues.

internal/ui/view.go (2)

311-318: Double-check the semantics of “[frames rendered: N]”

This counter reflects updates processed (incremented in Update) rather than the exact count of View invocations. If that’s the intended meaning, all good. If you want to display strictly “frames written,” consider clarifying the label.


321-332: Side-effectful frame logging in View gated by config is fine

The conditional write after assembling result is straightforward and non-invasive to normal execution.

@dhth dhth merged commit 70049e3 into main Aug 17, 2025
14 checks passed
@dhth dhth deleted the allow-logging-frames branch August 17, 2025 10:52
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.

1 participant