Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 21 additions & 8 deletions Cotabby/Services/UI/OverlayController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ final class OverlayController: SuggestionOverlayControlling {
private enum Layout {
static let minimumGhostFontSize: CGFloat = 14
static let maximumGhostFontSize: CGFloat = 24
/// Derived caret rects come from line-box geometry rather than measured text bounds, so the
/// reported height often inflates against the host's real font size (Slack, several web
/// editors). Keep the ceiling well below the exact cap so a wrong derived reading cannot
/// render oversized ghost text — the observed failure mode is too-large, not too-small.
static let maximumDerivedGhostFontSize: CGFloat = 17
static let maximumEstimatedGhostFontSize: CGFloat = 16
Comment on lines 14 to 21
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Now that all three caps are per-quality constants, maximumGhostFontSize is the odd one out — it's the only name that doesn't signal which quality tier it governs. Renaming it to match the pattern of its siblings makes the Layout enum self-documenting and avoids any future confusion about whether the generic name applies to all qualities.

Suggested change
static let minimumGhostFontSize: CGFloat = 14
static let maximumGhostFontSize: CGFloat = 24
/// Derived caret rects come from line-box geometry rather than measured text bounds, so the
/// reported height often inflates against the host's real font size (Slack, several web
/// editors). Keep the ceiling well below the exact cap so a wrong derived reading cannot
/// render oversized ghost text — the observed failure mode is too-large, not too-small.
static let maximumDerivedGhostFontSize: CGFloat = 17
static let maximumEstimatedGhostFontSize: CGFloat = 16
static let minimumGhostFontSize: CGFloat = 14
/// Measured text bounds from the accessibility API; trusted to scale up in larger editors.
static let maximumExactGhostFontSize: CGFloat = 24
/// Derived caret rects come from line-box geometry rather than measured text bounds, so the
/// reported height often inflates against the host's real font size (Slack, several web
/// editors). Keep the ceiling well below the exact cap so a wrong derived reading cannot
/// render oversized ghost text — the observed failure mode is too-large, not too-small.
static let maximumDerivedGhostFontSize: CGFloat = 17
static let maximumEstimatedGhostFontSize: CGFloat = 16

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Fix in Codex Fix in Claude Code

static let fontToLineHeightRatio: CGFloat = 0.78
}
Expand Down Expand Up @@ -240,11 +245,13 @@ final class OverlayController: SuggestionOverlayControlling {
panel.orderFrontRegardless()
}

/// Exact and derived caret rects usually reflect the real text line height, so they may scale
/// up in larger editors. Estimated rects are much less trustworthy because some apps only
/// expose the full field frame; the extra ceiling prevents one bad estimate from rendering
/// comically oversized ghost text. `caretHeight` is already floored to the per-session minimum
/// by `ghostFontStabilizer`, so this only applies the static floor and quality ceilings.
/// Caps the proposed font size by how much we trust the caret geometry:
/// - `.exact`: measured text bounds, trusted up to the full ceiling so larger editors scale.
/// - `.derived`: line-box geometry that commonly inflates against the real font, tightly
/// capped to avoid the "insanely big derived" failure mode observed in Slack-like hosts.
/// - `.estimated`: full-field-frame fallback, even less trustworthy, tightest ceiling.
/// `caretHeight` is already floored to the per-session minimum by `ghostFontStabilizer`, so this
/// only applies the static floor and the per-quality ceiling.
private func resolvedGhostFontSize(
forCaretHeight caretHeight: CGFloat,
caretQuality: CaretGeometryQuality
Expand All @@ -253,9 +260,15 @@ final class OverlayController: SuggestionOverlayControlling {
Layout.minimumGhostFontSize,
caretHeight * Layout.fontToLineHeightRatio
)
let qualityCap = caretQuality == .estimated
? Layout.maximumEstimatedGhostFontSize
: Layout.maximumGhostFontSize
let qualityCap: CGFloat
switch caretQuality {
case .exact:
qualityCap = Layout.maximumGhostFontSize
case .derived:
qualityCap = Layout.maximumDerivedGhostFontSize
case .estimated:
qualityCap = Layout.maximumEstimatedGhostFontSize
}

return min(proposedSize, qualityCap)
}
Expand Down