Repository navigation
fix(live-monitor): borders are misaligned in some terminal environments - #366
Conversation
|
Caution Review failedThe pull request is closed. """ WalkthroughThe changes introduce a new Changes
Estimated code review effort🎯 2 (Simple) | ⏱️ ~8 minutes Assessment against linked issues
Assessment against linked issues: Out-of-scope changesNo out-of-scope changes detected. Possibly related PRs
Poem
📜 Recent review detailsConfiguration used: CodeRabbit UI 📒 Files selected for processing (1)
✨ Finishing Touches
🧪 Generate unit tests
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Summary of Changes
Hello @AsPulse, 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!
This pull request addresses an issue where borders in the live monitor display were misaligned in certain terminal environments. The core solution involves introducing a new utility function to ensure consistent rendering of emojis, which often cause these alignment problems due to varying character widths across terminals.
Highlights
- New
drawEmojiUtility: Implemented adrawEmojifunction insrc/_terminal-utils.tsthat standardizes emoji rendering to a consistent two-character width, regardless of the terminal's interpretation of the emoji's actual width. - Consistent Emoji Rendering: All existing emoji usages within
src/_live-rendering.tshave been updated to utilize the newdrawEmojifunction, ensuring uniform display and preventing border misalignment. - Robustness with ANSI Escapes: The
drawEmojifunction leverages specific ANSI escape sequences (\u001B7,\u001B8,ansiEscapes.cursorForward) to precisely control cursor positioning and ensure correct rendering, even in challenging terminal environments like NeoVim. - In-source Testing: Added Vitest tests directly within
src/_terminal-utils.tsto validate that thedrawEmojifunction consistently produces output with a string width of 2 for both single and double-width emojis.
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 or fill out our survey to provide feedback.
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.
Code Review
This pull request introduces a drawEmoji function to standardize emoji widths in the terminal, which is a great approach to fixing the alignment issues. My review focuses on improving the implementation of this new function for better consistency and maintainability.
I've identified a logic issue in drawEmoji that causes inconsistent spacing, a typo in a constant name, and a minor performance improvement. Addressing these points will make the new utility more robust and easier to use correctly throughout the codebase.
…r emoji" This reverts commit 9179cd5.
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
src/_terminal-utils.ts (1)
335-337: Consider updating the test comment for clarity.The comment mentions "1-width emojis (should be padded to 2)" but the function doesn't pad - it moves the cursor by the actual emoji width. Consider updating the comment to reflect the actual behavior: "1-width emojis (cursor moves by their actual width)".
This would align the comment with both the implementation and test expectations.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
src/_live-rendering.ts(7 hunks)src/_terminal-utils.ts(1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit Inference Engine (CLAUDE.md)
**/*.{js,jsx,ts,tsx}: Lint code using ESLint MCP server (available via Claude Code tools)
Format code with ESLint (writes changes) usingbun run format
No console.log allowed except where explicitly disabled with eslint-disable
Do not use console.log. Use logger.ts instead.
Files:
src/_live-rendering.tssrc/_terminal-utils.ts
**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (CLAUDE.md)
Type check with TypeScript using
bun typecheck
Files:
src/_live-rendering.tssrc/_terminal-utils.ts
**/*.ts
📄 CodeRabbit Inference Engine (CLAUDE.md)
**/*.ts: File paths always use Node.js path utilities for cross-platform compatibility
Use.tsextensions for local file imports (e.g.,import { foo } from './utils.ts')
Prefer @praha/byethrow Result type over traditional try-catch for functional error handling
UseResult.try()for wrapping operations that may throw (JSON parsing, etc.)
UseResult.isFailure()for checking errors (more readable than!Result.isSuccess())
Use early return pattern (if (Result.isFailure(result)) continue;) instead of ternary operators
For async operations: create wrapper function withResult.try()then call it
Keep traditional try-catch only for: file I/O with complex error handling, legacy code that's hard to refactor
Always useResult.isFailure()andResult.isSuccess()type guards for better code clarity
Variables: start with lowercase (camelCase) - e.g.,usageDataSchema,modelBreakdownSchema
Types: start with uppercase (PascalCase) - e.g.,UsageData,ModelBreakdown
Constants: can use UPPER_SNAKE_CASE - e.g.,DEFAULT_CLAUDE_CODE_PATH
Only export constants, functions, and types that are actually used by other modules
Internal/private constants that are only used within the same file should NOT be exported
Always check if a constant is used elsewhere before making itexport constvs justconst
All test files must use current Claude 4 models, not outdated Claude 3 models
Test coverage should include both Sonnet and Opus models for comprehensive validation
Model names in tests must exactly match LiteLLM's pricing database entries
When adding new model tests, verify the model exists in LiteLLM before implementation
Tests depend on real pricing data from LiteLLM - failures may indicate model availability issues
Dynamic imports usingawait import()should only be used within test blocks to avoid tree-shaking issues
Mock data is created usingfs-fixturewithcreateFixture()for Claude data directory simulation
In-source testing pattern: Tests are written...
Files:
src/_live-rendering.tssrc/_terminal-utils.ts
**/_*.ts
📄 CodeRabbit Inference Engine (CLAUDE.md)
Internal files: use underscore prefix - e.g.,
_types.ts,_utils.ts,_consts.ts
Files:
src/_live-rendering.tssrc/_terminal-utils.ts
🧠 Learnings (2)
📓 Common learnings
Learnt from: AsPulse
PR: ryoppippi/ccusage#328
File: src/_live-rendering.ts:133-167
Timestamp: 2025-07-20T23:46:21.740Z
Learning: When AsPulse points out that variables are clearly type-inferred as strings from their construction (using template literals, string methods like .toFixed(), .padStart(), etc.), avoid suggesting unnecessary type assertions that would reduce code readability. ESLint type safety errors about "error type" often indicate type definition issues with imported packages rather than actual type problems in the user's code.
src/_live-rendering.ts (1)
Learnt from: CR
PR: ryoppippi/ccusage#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-19T10:58:04.397Z
Learning: Applies to **/*.ts : Variables: start with lowercase (camelCase) - e.g., usageDataSchema, modelBreakdownSchema
🪛 ESLint
src/_live-rendering.ts
[error] 38-38: Unsafe return of a value of type error.
(ts/no-unsafe-return)
[error] 38-38: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 38-38: Unsafe member access .green on an error typed value.
(ts/no-unsafe-member-access)
[error] 197-197: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 197-197: Unsafe member access .bold on an error typed value.
(ts/no-unsafe-member-access)
[error] 224-224: Unsafe assignment of an error typed value.
(ts/no-unsafe-assignment)
[error] 224-224: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 224-224: Unsafe member access .red on an error typed value.
(ts/no-unsafe-member-access)
[error] 276-276: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 276-276: Unsafe member access .bold on an error typed value.
(ts/no-unsafe-member-access)
[error] 382-382: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 382-382: Unsafe member access .red on an error typed value.
(ts/no-unsafe-member-access)
[error] 384-384: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 384-384: Unsafe member access .yellow on an error typed value.
(ts/no-unsafe-member-access)
[error] 385-385: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 385-385: Unsafe member access .green on an error typed value.
(ts/no-unsafe-member-access)
[error] 386-386: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 386-386: Unsafe member access .green on an error typed value.
(ts/no-unsafe-member-access)
[error] 389-389: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 389-389: Unsafe member access .bold on an error typed value.
(ts/no-unsafe-member-access)
[error] 439-439: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
src/_terminal-utils.ts
[error] 320-320: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 320-320: Unsafe member access .cursorForward on an error typed value.
(ts/no-unsafe-member-access)
[error] 320-320: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 325-325: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 326-326: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 328-328: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 328-328: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 328-328: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 328-328: Unsafe member access .toBe on an error typed value.
(ts/no-unsafe-member-access)
[error] 329-329: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 329-329: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 329-329: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 329-329: Unsafe member access .toBe on an error typed value.
(ts/no-unsafe-member-access)
[error] 330-330: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 330-330: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 330-330: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 330-330: Unsafe member access .toBe on an error typed value.
(ts/no-unsafe-member-access)
[error] 331-331: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 331-331: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 331-331: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 331-331: Unsafe member access .toBe on an error typed value.
(ts/no-unsafe-member-access)
[error] 332-332: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 332-332: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 332-332: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 332-332: Unsafe member access .toBe on an error typed value.
(ts/no-unsafe-member-access)
[error] 333-333: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 333-333: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 333-333: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 333-333: Unsafe member access .toBe on an error typed value.
(ts/no-unsafe-member-access)
[error] 336-336: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 336-336: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 336-336: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 336-336: Unsafe member access .toBe on an error typed value.
(ts/no-unsafe-member-access)
[error] 337-337: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 337-337: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 337-337: Unsafe call of a(n) error type typed value.
(ts/no-unsafe-call)
[error] 337-337: Unsafe member access .toBe on an error typed value.
(ts/no-unsafe-member-access)
🔇 Additional comments (6)
src/_terminal-utils.ts (1)
303-321: Well-implemented emoji rendering solution!The
drawEmojifunction properly addresses the terminal alignment issues by:
- Using compatible ANSI sequences (
\u001B7/\u001B8) instead of problematic ones- Moving cursor forward by the actual emoji width using
stringWidth(emoji)- Module-level constants for better performance
The detailed comments explaining the sequence choice are valuable for maintainability.
src/_live-rendering.ts (5)
19-19: Proper import of the new utility function.The
drawEmojiimport is correctly added to the existing import statement from_terminal-utils.ts.
38-38: Consistent emoji rendering in rate indicator.Good application of
drawEmojito ensure the checkmark displays consistently in the "NORMAL" rate status.
197-197: Consistent emoji rendering across all section labels.All main section labels (SESSION, USAGE, PROJECTION) now use
drawEmojifor consistent terminal rendering while preserving the existing formatting and spacing.Also applies to: 276-276, 389-389
224-224: Improved reliability of critical status indicators.Status messages now use
drawEmojifor consistent rendering of warning (⚠️ ), error (❌), and success (✓) indicators. This ensures users can reliably see important status information regardless of terminal environment.Also applies to: 382-386
438-438: Consistent emoji rendering in footer elements.Good application of
drawEmojito the models display and refresh indicator. The spacing appears consistent with other UI elements.Also applies to: 445-445
commit: |
This PR implemented the drawEmoji function, which draws all emojis using fixed width characters,
and used it for all emoji drawing parts in the code.
Screenshots
This PR

bdc7980b(terminal: Zellij over Ghostty)main

786ddba0(terminal: Zellij over Ghostty)Summary by CodeRabbit
Summary by CodeRabbit