Conversation
Show a semi-transparent white overlay (15% opacity) when the terminal window loses focus. This helps users quickly identify the active window when working with multiple terminals. - Add showUnfocusedOverlay setting (default: true) - Track window focus state reactively in TabbedTerminal - Render overlay conditionally based on focus and setting
Code ReviewSummaryThis PR adds a visual indicator (15% opacity white overlay) when the terminal window loses focus, helping users identify which window is active when working with multiple terminals. ✅ Strengths
🔍 Observations & Suggestions1. Performance Consideration -
|
On Linux, FontFamily.Default lacks symbol coverage (no Apple Color Emoji). MesloLGS Nerd Font already has symbols like ⏵ ★ ⚡, so use it instead. Change font selection priority: - Emoji WITH variation selector (☁️) → System font (color emoji) - Symbols WITHOUT variation selector (⏵ ★) → Terminal font (MesloLGS) - Math/cursive symbols → STIX Two Math → Terminal font fallback
Additional Technical ReviewI've reviewed this PR in detail and want to provide additional technical insights beyond the previous review. ✅ Implementation QualityThe implementation is solid overall. The approach of using a Box overlay is correct and performant. 🔍 Critical Technical Issues1. Incorrect Use of
|
New setting to control font selection for symbols like ⏵ ★ ⚡: - true (default): Use terminal font (MesloLGS) - better for Linux - false: Use system font (Apple Color Emoji on macOS)
Code Review: Unfocused Window Overlay IndicatorSummaryThis PR adds a visual indicator (15% white overlay) when the terminal window loses focus, making it easier to identify the active window. The implementation is clean and follows the project's patterns well. ✅ Strengths
🔍 Code Quality ObservationsTabbedTerminal.kt
TerminalCanvasRenderer.kt
TerminalSettings.kt
🐛 Potential Issues1. Performance: Reactive State Trackingval isWindowFocusedState by remember { derivedStateOf { isWindowFocused() } }
2. Accessibility: Overlay Color Choice.background(Color.White.copy(alpha = 0.15f))
3. Split Panes: Overlay Scope
🔒 Security Considerations
⚡ Performance Considerations
🧪 Test Coverage
📋 RecommendationsHigh Priority
Medium Priority
Low Priority
🎯 VerdictOverall Assessment: Good implementation with minor concerns Recommendation:
The core overlay feature is well-implemented and follows project conventions. The main issue is mixing two unrelated features in one PR. Once separated, this will be a clean, focused change. 📝 Testing Checklist Before Merge
Great work on a useful feature! 🎉 The implementation is solid, just needs the font rendering change split out and a few minor verifications. |
Add bundled symbol font (Noto Sans Symbols 2, 641KB) for symbols like ⏵ ★ ⚡ that don't render properly on Linux with FontFamily.Default. - Bundle NotoSansSymbols2-Regular.ttf (Apache 2.0 license) - Add lazy-loaded bundledSymbolFont in FontUtils.kt - Update renderer to use bundled symbol font when preferTerminalFontForSymbols=true - Setting defaults to true (Linux-friendly), set to false for macOS Apple Color Emoji
Code Review: Unfocused Window Overlay IndicatorSummaryThis PR adds a visual indicator (semi-transparent white overlay) when the terminal window loses focus, along with improved symbol font rendering using a bundled Noto Sans Symbols 2 font. Overall, this is a well-implemented feature with good attention to detail. ✅ Strengths1. Clean Implementation
2. Font Loading Pattern
3. Good Code Quality
🔍 Areas of Concern1. Performance: Recomposition Trigger (Medium Priority)Location: val isWindowFocusedState by remember { derivedStateOf { isWindowFocused() } }Issue: This will trigger recomposition of the entire Recommendation: Consider using a val isWindowFocusedState = remember { mutableStateOf(isWindowFocused()) }
// Update via a callback or LaunchedEffect when focus changes
LaunchedEffect(Unit) {
// If isWindowFocused is observable, subscribe to changes
}Impact: Low-medium - unlikely to cause visible performance issues, but worth optimizing for best practices. 2. Font Loading: Resource Leak Risk (Low Priority)Location: Issue: The temp file created for Recommendation: Consider using Impact: Very low - the 3. Color Hardcoding (Low Priority)Location: .background(Color.White.copy(alpha = 0.15f))Issue: The white color is hardcoded, which may not work well with all color schemes (especially light themes where the distinction might be less visible). Recommendation: Consider making the overlay color and opacity configurable via settings: // In TerminalSettings.kt
val unfocusedOverlayColor: Color = Color.White
val unfocusedOverlayAlpha: Float = 0.15f
// In TabbedTerminal.kt
.background(settings.unfocusedOverlayColor.copy(alpha = settings.unfocusedOverlayAlpha))Or use a dynamic approach based on the current theme (white for dark themes, black for light themes). Impact: Low - mostly a UX polish issue. 4. Symbol Font Logic Clarity (Low Priority)Location: Issue: The logic for when to use Recommendation: Add more detailed comments explaining:
Impact: Very low - documentation clarity only. 5. Missing Binary File CheckIssue: The PR includes a binary font file (
Recommendation: Add a comment or documentation noting:
🔒 Security Review✅ No security concerns identified:
🧪 Test CoverageMissing Tests:
Recommendation: Add tests for:
However, given that this is a UI feature and the codebase appears to rely on manual testing (per CLAUDE.md), this is acceptable for now. 📊 Performance Considerations✅ Generally good:
🎯 Final VerdictRecommendation: APPROVE with minor suggestions This is a solid implementation of a useful feature. The code follows existing patterns, has proper error handling, and introduces no security risks. The performance concerns are minor and can be addressed in follow-up work if needed. Suggested Follow-ups (Optional):
Test Plan ChecklistBefore merging, please verify:
Great work on this feature! 🚀 🤖 Generated with Claude Code |
Replace Noto Sans Symbols 2 (641KB) with Noto Color Emoji (10MB) for more vibrant, Apple-like symbol rendering on Linux. - Bundle NotoColorEmoji-Regular.ttf (Apache 2.0 license) - Update FontUtils.kt to load new font - Symbols like ⏵ ★ ⚡ now render with color
Code Review for PR #114: Unfocused Window Overlay IndicatorSummaryThis PR adds visual feedback when the terminal window loses focus and improves cross-platform symbol rendering. The implementation is generally solid with good architectural decisions. Below are my findings: ✅ Strengths1. Window Focus Overlay
2. Font System Enhancement
3. Cross-Platform Symbol Support
|
| Aspect | Impact | Severity |
|---|---|---|
| Bundle size increase | +11 MB | High |
| First symbol render delay | ~100-300ms (estimated) | Moderate |
| Memory usage | +11 MB (loaded font) | Moderate |
| Runtime performance | Negligible (after load) | Low |
🎯 Recommendations Priority
High Priority:
⚠️ Subset the font file to reduce from 11 MB to <1 MB- Only include needed Unicode ranges
- Or revert to Noto Sans Symbols 2 (641 KB) for basic symbols
Medium Priority:
- Fix outdated comment in
TerminalCanvasRenderer.kt:826 - Add performance testing for font loading delay
Low Priority:
- Add structured logging for font load failures
- Consider font caching strategy (reuse temp file across launches)
✅ Approval Status
Overall Assessment: Good implementation with one critical concern (font file size).
Recommendation: Request changes to address font size before merge. The feature itself is well-implemented and follows project conventions.
Suggested Next Steps:
- Subset font to include only needed symbols (high priority)
- Update comment to reflect Noto Color Emoji usage
- Test on Linux to verify symbol rendering works as expected
- Consider adding font load timing metrics for monitoring
Great work on the cross-platform compatibility improvements! 🚀
Noto Color Emoji didn't render symbols correctly. Revert to Noto Sans Symbols 2 (641KB) which has better symbol coverage.
Code Review - PR #114: Unfocused Window Overlay Indicator✅ Overall AssessmentThis is a well-implemented feature with good cross-platform considerations. The code quality is solid, follows the project conventions, and addresses a real UX need. I have a few suggestions for improvement. 🎯 Strengths
🔍 Code Quality ObservationsTabbedTerminal.kt (Lines 85-86)val isWindowFocusedState by remember { derivedStateOf { isWindowFocused() } }Concern: This creates a derived state that calls a function on every recomposition. Since Recommendation: Consider using // Option 1: Direct state usage (preferred if isWindowFocused can return State)
val isWindowFocusedState = isWindowFocused()
// Option 2: If isWindowFocused must remain a lambda, use LaunchedEffect
val isWindowFocusedState = remember { mutableStateOf(isWindowFocused()) }
LaunchedEffect(Unit) {
snapshotFlow { isWindowFocused() }.collect { isWindowFocusedState.value = it }
}TabbedTerminal.kt (Lines 368-373)if (!isWindowFocusedState && settings.showUnfocusedOverlay) {
Box(modifier = Modifier.fillMaxSize().background(Color.White.copy(alpha = 0.15f)))
}Observation: The overlay is positioned inside a Box wrapping the entire Column. This is correct for layering, but worth noting for future maintainers. Minor Suggestion: Consider extracting the overlay color to a constant or theme: private val UnfocusedOverlayColor = Color.White.copy(alpha = 0.15f)🐛 Potential Issues1. Font Loading Performance (FontUtils.kt:118-148)The
2. Binary Font File Licensing (NotoSansSymbols2-Regular.ttf)
3. Settings Migration (TerminalSettings.kt)
⚡ Performance Considerations1. Overlay Rendering (TabbedTerminal.kt:368-373)
2. Font Selection Per Character (TerminalCanvasRenderer.kt:820-839)
🔒 Security Considerations✅ No security concerns identified
🧪 Test CoverageMissing: No automated tests for new features
Test Plan: The PR's manual test plan is comprehensive:
Manual Testing Needed:
📝 DocumentationMissing: Documentation for new settings
🎨 Code Style & Best Practices✅ Follows project conventions:
import ai.rever.bossterm.compose.util.bundledSymbolFont
// Then use: bundledSymbolFont🚀 Recommendations SummaryMust Fix Before Merge:
Should Fix:
Nice to Have:
🎉 ConclusionThis is a solid PR that addresses real UX needs (focus indication) and cross-platform rendering issues (Linux symbol support). The implementation is clean and follows project conventions. With minor improvements (license attribution, state optimization), this is ready to merge. Rating: 8.5/10 Great work! 🚀 |
Platform-specific symbol font selection: - macOS: Always use FontFamily.Default (Apple Color Emoji) - Linux: Use bundled Noto Sans Symbols 2 (controlled by setting)
- null (default): macOS uses Apple Color Emoji, Linux uses bundled font - true: Force bundled Noto Sans Symbols 2 on all platforms - false: Force system default font on all platforms
Code ReviewThis PR adds two features: (1) unfocused window overlay and (2) platform-specific symbol font rendering. Overall the implementation is solid and follows project conventions, but I have some observations and suggestions. ✅ Strengths
🔍 Issues & Suggestions1. Performance: Overlay RecompositionLocation: val isWindowFocusedState by remember { derivedStateOf { isWindowFocused() } }Issue: val isWindowFocusedState by isWindowFocusedOr simply use it inline: if (!isWindowFocused.value && settings.showUnfocusedOverlay) {Impact: Unnecessary recomposition overhead on every frame when the value hasn't changed. 2. Font Loading: Resource Leak RiskLocation: Issue: If Suggestion: Use try-finally or Kotlin's private fun loadBundledSymbolFont(): FontFamily {
return try {
val fontStream = object {}.javaClass.classLoader
?.getResourceAsStream("fonts/NotoSansSymbols2-Regular.ttf")
?: return FontFamily.Default
val tempFile = java.io.File.createTempFile("NotoSansSymbols2", ".ttf")
try {
tempFile.deleteOnExit()
fontStream.use { input ->
tempFile.outputStream().use { output ->
input.copyTo(output)
}
}
FontFamily(
androidx.compose.ui.text.platform.Font(
file = tempFile,
weight = FontWeight.Normal
)
)
} catch (e: Exception) {
tempFile.delete() // Clean up immediately on failure
throw e
}
} catch (e: Exception) {
System.err.println("Failed to load bundled symbol font: ${e.message}")
FontFamily.Default
}
}3. Platform Detection: Runtime Check on Hot PathLocation: val isMacOS = System.getProperty("os.name")?.lowercase()?.contains("mac") == trueIssue: This system property lookup happens every time a symbol is rendered (potentially thousands of times per second during scrolling). Suggestion: Move to a top-level constant or lazy property: private val isMacOS: Boolean by lazy {
System.getProperty("os.name")?.lowercase()?.contains("mac") == true
}Or cache it in the rendering context. 4. Font Loading: Memory Leak PotentialLocation: val bundledSymbolFont: FontFamily by lazy {
loadBundledSymbolFont()
}Observation: The Minor suggestion: Add a comment explaining this is intentional: /**
* Lazily loaded bundled symbol font (Noto Sans Symbols 2).
* Loaded once and cached for application lifetime.
* The temp file is marked deleteOnExit() and will be cleaned up on JVM shutdown.
*/
val bundledSymbolFont: FontFamily by lazy {
loadBundledSymbolFont()
}5. Overlay Rendering: Z-Index AmbiguityLocation: Observation: The overlay is rendered after the terminal content in the composition tree. While this typically works correctly (later items render on top), it's not guaranteed in all Compose scenarios. Suggestion: Explicitly set z-index if ordering issues arise: .background(Color.White.copy(alpha = 0.15f))
.zIndex(10f) // Ensure overlay is on top6. Settings: Missing DocumentationLocation: Observation: The Suggestion: Consider adding a platform check in settings validation or UI (future enhancement): val preferTerminalFontForSymbols: Boolean =
if (System.getProperty("os.name")?.contains("mac", ignoreCase = true) == true) {
false // Always use system font on macOS
} else {
true // Default to bundled font on Linux
}7. Binary File in GitLocation: Observation: Adding a 641KB binary file to the repository. This is acceptable for a font resource, but:
Suggestion: Add attribution file: 🎯 Testing Recommendations
🔒 SecurityNo security concerns identified. Font loading uses standard Java resource APIs and temp file creation is properly scoped. 📊 Performance
🧪 Test CoverageMissing: No unit tests for:
Suggestion: Add tests for ✏️ Minor Nits
📝 Summary
Recommendation: ✅ Approve with minor fixes The implementation is production-ready, but I'd recommend addressing issues #1, #3, and #7 before merging. Issues #2, #4, #5, #6 can be follow-up improvements. Great work on the platform-specific font handling! The overlay feature will definitely improve UX for multi-window workflows. |
Add "Symbol Font" setting in Visual settings with options: - Platform Default (Apple on macOS, Bundled on Linux) - Bundled (Noto Sans Symbols 2) - System Default
Code Review - PR #114: Unfocused Window Overlay IndicatorSummaryThis PR adds two main features:
✅ Code Quality & Best PracticesStrengths:
Minor Concerns:
🐛 Potential BugsCritical:
Minor:
val useSystemFont = when (ctx.settings.preferTerminalFontForSymbols) {
null -> isMacOS // Platform default
true -> false // Bundled font
false -> true // System font
}⚡ Performance ConsiderationsGood:
Potential Issue:
🔒 Security ConcernsLow Risk:
🧪 Test CoverageMissing Tests:
📋 Additional Recommendations
🎯 Summary Rating
✏️ Action ItemsBefore Merge:
Post-Merge (Nice to Have): 🚀 ConclusionThis is a solid PR with useful features. The main blocker is the missing UI control for the overlay toggle. Once that's added and the z-order behavior is verified with dialogs, this should be safe to merge. The symbol font feature is well-implemented but consider splitting it into a separate PR for cleaner history. Great work overall! 🎉 |
Technical symbols (⏸ ⏵ etc) should use STIX Two Math → terminal font, NOT FontFamily.Default. This fixes the regression where these symbols were rendering as emoji on macOS. Logic now: - Emoji with variation selector → FontFamily.Default (color emoji) - Emoji/wide symbols → Platform-specific (macOS: system, Linux: bundled) - Technical/math symbols → STIX Two Math → terminal font (original behavior)
- Remove Co-Authored-By from commit template - Add Symbol Font Fallback documentation - Document platform-specific font selection logic - Document preferTerminalFontForSymbols setting
Code Review - PR #114: Unfocused Window Overlay & Symbol Font ImprovementsGreat work on this feature! The implementation is well-structured and follows the project's patterns. Here's my detailed feedback: ✅ Strengths
🐛 Issues & Concerns1. Performance: Platform Detection in Render Loop (HIGH PRIORITY)Location: val isMacOS = System.getProperty("os.name")?.lowercase()?.contains("mac") == true
val useSystemFontForEmoji = if (ctx.settings.preferTerminalFontForSymbols \!= null) {
\!ctx.settings.preferTerminalFontForSymbols\!\!
} else {
isMacOS
}Problem: This code runs inside Impact:
Solution: Move platform detection to initialization: // At class/object level or in RenderingContext
private val isMacOS = System.getProperty("os.name")?.lowercase()?.contains("mac") == true
// In renderCharacter(), simplified:
val useSystemFontForEmoji = when (ctx.settings.preferTerminalFontForSymbols) {
null -> isMacOS // Use cached platform check
true -> false
false -> true
}2. Missing Setting in Visual Settings SectionLocation: Problem: The PR adds
But there's no UI control in Solution: Add a toggle above or below the Symbol Font dropdown: SettingsToggle(
label = "Show Unfocused Overlay",
checked = settings.showUnfocusedOverlay,
onCheckedChange = { onSettingsChange(settings.copy(showUnfocusedOverlay = it)) },
description = "Semi-transparent white overlay when window loses focus"
)3. Redundant Null Safety OperatorLocation: val useSystemFontForEmoji = if (ctx.settings.preferTerminalFontForSymbols \!= null) {
\!ctx.settings.preferTerminalFontForSymbols\!\! // Double-bang after null check
} else {
isMacOS
}Issue: The 🔍 Potential Issues4. Font Classification Logic ComplexityLocation: The font selection logic is complex with overlapping conditions:
Questions:
Suggestion: Add comments explaining the classification hierarchy and rationale for each branch. 5. Font Loading Happens on First RenderLocation: Risk: If font loading throws an exception during first render, it could cause UI jank or delay. The lazy initialization is good, but consider:
6. Missing Font License DocumentationAdded File: Issue: No
Best Practice: Add 🧪 Testing Concerns
🎨 Code Style
🔒 SecurityNo security concerns identified. Font loading uses standard resource streams with proper error handling. 📊 Performance Summary
📝 RecommendationsBefore Merge:
Future Enhancements:
🎯 Overall AssessmentRating: 7.5/10 This is a solid feature with good architectural decisions, but has one critical performance issue (platform detection in render loop) and one usability issue (missing settings toggle). Once those are fixed, this will be production-ready. The commit history shows good iteration (10 commits exploring different font solutions) and the final implementation is well-reasoned. Great work! 🤖 Generated with Claude Code - Review based on CLAUDE.md conventions |
Code Review: Unfocused Window Overlay + Symbol Font ImprovementsOverviewThis PR introduces two main features:
✅ Strengths1. Clean Architecture
2. Platform-Aware Design
3. Font Loading Best Practices
4. Good Documentation
|
Pull Request Review - PR #114SummaryThis PR adds two main features:
Overall Assessment✅ Approved with minor suggestions The implementation is solid and follows the project's established patterns. The commit history shows thoughtful iteration and platform-specific considerations. Detailed Review🟢 Strengths
🟡 Suggestions for Improvement1. Missing License File for Bundled FontThe PR bundles Recommendation: Add Location: The code comment mentions "Apache 2.0 license" but the actual license text should be included in the repository. 2. Potential Window Focus Performance IssueIn val isWindowFocusedState by remember { derivedStateOf { isWindowFocused() } }Concern: If Recommendation: Consider whether // In Main.kt
val isWindowFocused = remember { mutableStateOf(true) }
windowFocusListener = object : WindowFocusListener {
override fun windowGainedFocus(e: WindowEvent) { isWindowFocused.value = true }
override fun windowLostFocus(e: WindowEvent) { isWindowFocused.value = false }
}
// In TabbedTerminal
fun TabbedTerminal(
isWindowFocused: State<Boolean>, // Changed from () -> Boolean
...
)This would eliminate the need for 3. Font Loading Error HandlingIn } catch (e: Exception) {
System.err.println("Failed to load bundled symbol font: " + e.message)
FontFamily.Default
}Recommendation: Consider whether this should log to the application's logging system (if one exists) rather than directly to stderr. Also, the error message could include the font file name for better debugging. 4. Settings UI - Missing "Unfocused Overlay" ToggleThe Recommendation: Add a toggle in the Visual Settings section: SettingsToggle(
label = "Show Unfocused Overlay",
checked = settings.showUnfocusedOverlay,
onCheckedChange = { onSettingsChange(settings.copy(showUnfocusedOverlay = it)) },
description = "Dim window with semi-transparent overlay when not focused"
)5. Overlay Color HardcodedThe overlay uses a hardcoded white color with 15% opacity: .background(Color.White.copy(alpha = 0.15f))Consideration: This works well for light text on dark backgrounds (typical terminal), but if a user has a light background theme, the white overlay might be too subtle. Consider:
This is a minor UX polish item, not a blocker. 🟢 Code Quality
🔍 Testing Recommendations
📝 Minor Nitpicks
ConclusionThis is a well-implemented PR that adds useful features with thoughtful platform-specific handling. The main suggestion is to add the font license file and consider exposing the overlay setting in the UI. Recommendation: Approve and merge after addressing the missing font license file (item #1). Items #2-5 can be addressed in follow-up PRs if desired. Great work! 🎉 |
Technical symbols (⏸ ⏵) now also respect platform defaults: - Linux: uses bundled Noto Sans Symbols 2 by default - macOS: uses STIX Two Math by default Previously only emoji/wide symbols had platform-specific defaults.
Code Review for PR #114: Add unfocused window overlay indicatorOverviewThis PR introduces two main features:
Positive Aspects ✅Architecture & Design
Code Quality
Documentation
Issues & Concerns 🔴1. CRITICAL: Missing UI Toggle for Unfocused OverlayThe Recommendation: Add a SettingsToggle(
label = "Show Unfocused Overlay",
checked = settings.showUnfocusedOverlay,
onCheckedChange = { onSettingsChange(settings.copy(showUnfocusedOverlay = it)) },
description = "Show semi-transparent overlay when window loses focus"
)2. Code Organization: Misplaced Closing BraceIn Current (confusing): )
}
} // ← Extra brace here
// Semi-transparent overlay...Should be: )
}
}
// Semi-transparent overlay...3. Performance Consideration: Platform Detection on Every RenderIn Recommendation: Move platform detection to a companion object or lazy val: object TerminalCanvasRenderer {
private val isMacOS by lazy {
System.getProperty("os.name")?.lowercase()?.contains("mac") == true
}
// ... then use isMacOS in drawChar()
}4. Nullable Logic ComplexityThe private fun shouldUseSystemFontForSymbols(settings: TerminalSettings): Boolean {
return when (settings.preferTerminalFontForSymbols) {
null -> isMacOS // Platform default
true -> false // Force bundled
false -> true // Force system
}
}5. Font Loading Error HandlingWhile 6. Test CoverageThe PR checklist shows unchecked test plan items. Consider testing:
7. Accessibility ConsiderationA 15% white overlay might not be sufficient for users with visual impairments or in bright environments. Consider:
Minor Suggestions 💡
sealed class SymbolFontStrategy {
object SystemDefault : SymbolFontStrategy()
object Bundled : SymbolFontStrategy()
object PlatformDefault : SymbolFontStrategy()
}
Security Considerations 🔒No security concerns identified. Font loading uses standard Java APIs with proper resource cleanup. Performance Considerations ⚡
RecommendationRequest Changes - The missing UI toggle for Overall, this is a well-structured PR with good documentation and solid implementation. The issues identified are mostly polish and optimization opportunities, with the exception of the missing UI toggle which should be addressed. 🤖 Generated with Claude Code |
Summary
showUnfocusedOverlay(default: true) to toggle the featureTest plan
🤖 Generated with Claude Code