Skip to content

Reimplement field-edge tabby icon indicator as default#123

Merged
FuJacob merged 4 commits into
mainfrom
fix/default-indicator-fieldedge
May 21, 2026
Merged

Reimplement field-edge tabby icon indicator as default#123
FuJacob merged 4 commits into
mainfrom
fix/default-indicator-fieldedge

Conversation

@FuJacob

@FuJacob FuJacob commented May 21, 2026

Copy link
Copy Markdown
Owner

Summary

  • Reverts the indicator simplification from Simplify activation indicator + add local DMG build script #117 that removed the field-edge icon
  • Restores fieldEdgeIcon mode: tabby's app icon placed outside the text area's left edge
  • Default indicator is now fieldEdgeIcon instead of caretAnchor
  • Both MenuBarView and SettingsView use a mode picker (None / Caret / Tabby Icon)
  • Fix pre-existing SwiftLint line_length violation in SettingsView

Test plan

  • Build succeeds
  • Build-for-testing succeeds
  • Tabby icon appears at the left edge of focused text fields
  • Indicator mode picker works in both Settings and menu bar panel
  • "None" hides indicator, "Caret" shows triangle, "Tabby Icon" shows app icon
  • Fresh install defaults to Tabby Icon mode

🤖 Generated with Claude Code

Greptile Summary

This PR restores the fieldEdgeIcon activation indicator (tabby's paw-print icon anchored outside the text field's left edge) that was removed in #117, making it the new default mode. Both SettingsView and MenuBarView replace the old on/off toggle with a three-way mode picker (None / Caret / Tabby Icon), and migration logic correctly upgrades legacy users to the new default.

  • ActivationIndicatorController gains a fieldEdgeIconOrigin helper and a new FieldEdgeIconIndicatorView, tracking last-shown mode instead of a boolean to skip redundant frame updates.
  • SuggestionSettingsModel updates the migration path so fresh installs default to .fieldEdgeIcon; the legacy key is still written for backward compatibility with older builds.
  • LlamaVisualContextSummarizer adds a deduplicateConsecutiveLines pre-pass and bumps repetitionPenalty from 1.1 → 1.4 for the summarizer to combat hallucination loops from OCR'd repeated text.

Confidence Score: 4/5

Safe to merge with one known outstanding issue: the caretRect.isEmpty guard silently hides the fieldEdgeIcon indicator in apps that report an empty caret rect but a valid input frame rect.

The fieldEdgeIconOrigin function has fallback logic to use inputFrameRect when caretRect is empty, but the guard !caretRect.isEmpty on line 60 exits before that logic can run. This means the fieldEdgeIcon indicator — the new default mode — will not appear in apps like Electron editors or browser text inputs that expose a valid input frame rect but report an empty caret rect. The rest of the changes (migration logic, UI pickers, deduplication, penalty bump) are well-scoped and correct.

tabby/Services/UI/ActivationIndicatorController.swift — the guard !caretRect.isEmpty path needs to be conditioned on mode so fieldEdgeIcon can fall through to its inputFrameRect-based positioning.

Important Files Changed

Filename Overview
tabby/Services/UI/ActivationIndicatorController.swift Core of this PR — adds fieldEdgeIconOrigin, FieldEdgeIconIndicatorView, and mode-tracking; a pre-existing guard !caretRect.isEmpty still exits early for .fieldEdgeIcon even when a valid inputFrameRect is available (flagged in a prior outside-diff comment).
tabby/Models/SuggestionSettingsModel.swift Migration path updated to default to .fieldEdgeIcon; backward-compat write to the legacy key is preserved; no regressions found.
tabby/App/Core/AppDelegate.swift Call site updated to pass mode and inputFrameRect to the new show() signature; straightforward and correct.
tabby/UI/MenuBarView.swift Toggle replaced with a compact Picker using compactLabel; binding updated to selectIndicatorMode; no issues found.
tabby/UI/SettingsView.swift Toggle replaced with a Picker using displayLabel; pre-existing SwiftLint line_length suppression added for the long confirmation string; no issues found.
tabby/Services/Visual/LlamaVisualContextSummarizer.swift Adds deduplicateConsecutiveLines pre-pass to strip OCR-induced repetition before summarization; behavior with blank-line-separated duplicate sections noted in a prior thread comment.
tabby/Models/LlamaRuntimeModels.swift repetitionPenalty in the summary() factory bumped from 1.1 → 1.4 with an explanatory comment; scoped to the summarizer path, does not affect autocomplete options.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["show(mode, caretRect, inputFrameRect)"] --> B{mode == .hidden?}
    B -->|Yes| C[hide]
    B -->|No| D{caretRect.isEmpty?}
    D -->|Yes| C
    D -->|No| E[set contentView rootView via view-for-mode]
    E --> F{switch mode}
    F -->|.caretAnchor| G[caretAnchorOrigin]
    F -->|.fieldEdgeIcon| H[fieldEdgeIconOrigin\nprefers inputFrameRect left edge\nfallback to right side]
    G --> I[compute integral frame]
    H --> I
    I --> J{lastMode == mode\nframe unchanged\npanel visible?}
    J -->|Yes| K[no-op]
    J -->|No| L[setFrame + orderFrontRegardless\nupdate lastMode]
Loading

Comments Outside Diff (1)

  1. tabby/Services/UI/ActivationIndicatorController.swift, line 60-63 (link)

    P1 The caretRect.isEmpty guard exits early for all modes, including .fieldEdgeIcon, whose entire purpose is to anchor to the input frame even when caret geometry is unreliable. If an app (e.g., an Electron or browser editor) reports an empty caret rect but a valid inputFrameRect, the indicator is hidden despite fieldEdgeIconOrigin being fully capable of positioning itself from inputFrameRect alone. The guard should pass through when the mode is .fieldEdgeIcon and a non-empty inputFrameRect is available.

    Fix in Codex Fix in Claude Code

Reviews (3): Last reviewed commit: "Replace cropped app icon with SwiftUI-bu..." | Re-trigger Greptile

FuJacob and others added 2 commits May 20, 2026 21:08
- Change default indicator mode from caretAnchor to fieldEdgeIcon
- Implement FieldEdgeIconView rendering a small pawprint below the caret
- Update ActivationIndicatorController.show() to accept mode enum instead of boolean
- Update AppDelegate to pass selectedIndicatorMode directly
- Toggle shim now maps on → fieldEdgeIcon instead of caretAnchor
- Fix SwiftLint line_length violation in SettingsView.swift

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When the screen contains chatbot output with repeated phrases like
"Final Answer\nFinal Answer\n...", the model learns the pattern and
loops it in the summary output. Two fixes:

1. Deduplicate consecutive identical lines from OCR text before
   building the prompt, removing the repeating signal at the source.
2. Raise the summary repetition penalty from 1.1 to 1.4 so the
   sampler actively discourages looping even if non-consecutive
   repetition slips through.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Comment thread tabby/Services/UI/ActivationIndicatorController.swift Outdated
Comment on lines +57 to +72
/// Collapses runs of identical trimmed lines to a single occurrence.
/// Preserves blank lines and non-duplicate content unchanged.
private func deduplicateConsecutiveLines(_ text: String) -> String {
var result: [String] = []
var previous: String?
for line in text.components(separatedBy: "\n") {
let trimmed = line.trimmingCharacters(in: .whitespaces)
if trimmed.isEmpty || trimmed != previous {
result.append(line)
if !trimmed.isEmpty {
previous = trimmed
}
}
}
return result.joined(separator: "\n")
}

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 previous persists across blank lines, deduplicating non-consecutive repetitions

previous is only updated for non-empty trimmed lines, but it is never reset when a blank line is encountered. A sequence like "Section A\n\nSection A\nContent" will silently drop the second "Section A" even though the two occurrences appear in different document regions. The docstring says "Collapses runs of identical trimmed lines", but the implementation deduplicates any identical line that shares its most-recent non-empty predecessor, which is broader than a consecutive run. Resetting previous = nil when a blank line is encountered would align behavior with the documented intent.

Fix in Codex Fix in Claude Code

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

better to be more safe and remove as much duplicate as possible.

Reverts the indicator simplification from #117 and restores the
field-edge icon mode that places tabby's app icon outside the text
area's left edge.

- Restore ActivationIndicatorController mode-based API with fieldEdgeIcon support
- Restore FieldEdgeIconIndicatorView using NSApp.applicationIconImage
- Restore fieldEdgeIconOrigin positioning logic (left-edge with screen fallback)
- AppDelegate passes selectedIndicatorMode + inputFrameRect
- MenuBarView and SettingsView use mode picker instead of boolean toggle
- Default migration maps legacy toggle-on to fieldEdgeIcon instead of caretAnchor
- Fix pre-existing SwiftLint line_length violation in SettingsView

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@FuJacob FuJacob changed the title Default indicator to tabby icon, disable inline caret Reimplement field-edge tabby icon indicator as default May 21, 2026
@greptile-apps

greptile-apps Bot commented May 21, 2026

Copy link
Copy Markdown
Contributor

Want your agent to iterate on Greptile's feedback? Try greploops.

Build the field-edge indicator from SwiftUI elements instead of
cropping NSApp.applicationIconImage: dark rounded rect background
with a pawprint.fill SF Symbol on top. Adapts to light/dark mode.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@FuJacob FuJacob merged commit 8b133be into main May 21, 2026
2 of 3 checks passed
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