Skip to content

feat(rules): add address-bar focus lock/switch rule#29

Merged
BlackHole1 merged 1 commit into
mainfrom
add-address-rules
Jun 22, 2026
Merged

feat(rules): add address-bar focus lock/switch rule#29
BlackHole1 merged 1 commit into
mainfrom
add-address-rules

Conversation

@BlackHole1

Copy link
Copy Markdown
Member

While a browser's address bar (omnibox) has keyboard focus, the user is typing a URL or a search, where a CJK input method is rarely wanted. This adds an optional rule that forces a chosen input source on address-bar focus, so typing web addresses lands in the right source.

Detection is event-driven via Accessibility: an AXObserver on the frontmost browser watches focusedUIElementChanged, and the system-wide focused element is classified by AddressBarHeuristic — a per-engine, non-localized identifier (Chromium OmniboxViewViews, Safari WEB_BROWSER_ADDRESS_AND_SEARCH_FIELD, Firefox urlbar-input) confirmed by a chrome-vs-AXWebArea structural check. It mirrors FloatingAppMonitor and reuses the existing Accessibility grant; the permission-free core lock is untouched.

Both a continuous "lock" and a one-shot "switch" are supported (some input methods cover both languages). There is no reverse restore: leaving the address bar re-resolves the standard URL/app/default rules rather than restoring a remembered source. When both the address bar and a URL rule apply, a user-configurable priority decides the winner, defaulting to address-bar-first.

The rule is off by default and is per-device runtime state, so it is excluded from config backup/import (and preserved across import). The option lives in the URL Rules settings pane as labeled pop-up menus matching the macOS Settings style.

Closed: #28

While a browser's address bar (omnibox) has keyboard focus, the user is
typing a URL or a search, where a CJK input method is rarely wanted.
This adds an optional rule that forces a chosen input source on
address-bar focus, so typing web addresses lands in the right source.

Detection is event-driven via Accessibility: an AXObserver on the
frontmost browser watches focusedUIElementChanged, and the system-wide
focused element is classified by AddressBarHeuristic — a per-engine,
non-localized identifier (Chromium OmniboxViewViews, Safari
WEB_BROWSER_ADDRESS_AND_SEARCH_FIELD, Firefox urlbar-input) confirmed
by a chrome-vs-AXWebArea structural check. It mirrors FloatingAppMonitor
and reuses the existing Accessibility grant; the permission-free core
lock is untouched.

Both a continuous "lock" and a one-shot "switch" are supported (some
input methods cover both languages). There is no reverse restore:
leaving the address bar re-resolves the standard URL/app/default rules
rather than restoring a remembered source. When both the address bar
and a URL rule apply, a user-configurable priority decides the winner,
defaulting to address-bar-first.

The rule is off by default and is per-device runtime state, so it is
excluded from config backup/import (and preserved across import). The
option lives in the URL Rules settings pane as labeled pop-up menus
matching the macOS Settings style.

Signed-off-by: Kevin Cui <bh@bugs.cc>
@coderabbitai

coderabbitai Bot commented Jun 22, 2026

Copy link
Copy Markdown

Review Change Stack

Summary by CodeRabbit

  • New Features
    • Added address-bar focus rule: automatically enforces a specified input source when focusing a browser's address bar, with configurable action (lock or switch), target source, and priority relative to URL rules.
    • Added settings panel for managing address-bar rules with accessibility support.
    • Enhanced activation log to display address-bar focus/blur events.

Walkthrough

A new "Address-bar focus rule" feature is added across LockIMEKit and the LockIME app. AddressBarHeuristic provides a pure, browser-agnostic predicate (Safari, Chromium, Firefox) that identifies whether a focused AX element is an address bar. AddressBarFocusMonitor implements the AddressBarFocusMonitoring protocol using AXObserver to track per-process browser address-bar focus. LockConfiguration gains four new fields controlling enablement, action, target source, and URL-rule precedence. RuleResolver.resolve is updated to accept addressBarFocused and compute a browser-scoped winner between address-bar and URL candidates. LockEngine injects the monitor, tracks addressBarFocused, and re-evaluates on all lifecycle transitions. AppState adds four mutation methods, and URLRulesSettingsPane adds an Accessibility-gated settings section.

Possibly Related PRs

  • oomol-lab/LockIME#13: Introduced the RuleSource/ActivationReason differentiation and ActivationLogPane label mapping infrastructure that this PR extends with addressBarRule, addressBarFocused, and addressBarBlurred.
  • oomol-lab/LockIME#22: Introduced RuleAction/.switchOnce one-shot semantics in LockEngine and RuleResolver that this PR threads through the new address-bar rule path.
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The pull request title follows the required format (): with 'feat(rules):' prefix and clearly describes the main change.
Description check ✅ Passed The pull request description is comprehensive and directly related to the changeset, providing context about the address-bar focus rule implementation.
Linked Issues check ✅ Passed The PR implementation fulfills the linked issue #28 requirement by providing a configurable address-bar focus rule that locks the input source to user-selected method while typing URLs.
Out of Scope Changes check ✅ Passed All changes are within scope: address-bar monitoring infrastructure, rule resolution logic, configuration persistence, UI integration, and comprehensive test coverage directly support the address-bar focus rule feature.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch add-address-rules

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
Sources/LockIMEKit/AppMonitor/AddressBarFocusMonitor.swift (1)

89-94: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Align stop() behavior with the protocol’s documented callback contract.

AddressBarFocusMonitoring documents that stopping observation emits onChange(false) (see Sources/LockIMEKit/AppMonitor/AddressBarFocusMonitoring.swift, Line 7-10), but stop() currently resets state without notifying listeners. Either emit the false transition before clearing onChange, or update the protocol docs to match actual behavior.

Suggested patch
 public func stop() {
+        if current {
+            onChange?(false)
+        }
         detach()
         onChange = nil
         observedBundleID = nil
         current = false
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Sources/LockIMEKit/AppMonitor/AddressBarFocusMonitor.swift` around lines 89 -
94, The stop() method in AddressBarFocusMonitor currently resets state without
notifying listeners, but the AddressBarFocusMonitoring protocol documents that
stopping observation should emit onChange(false). Before clearing the onChange
property and other state in the stop() method, invoke onChange(false) to notify
observers that address bar focus monitoring has been halted, ensuring the
implementation aligns with the documented contract.
Sources/LockIME/AppState.swift (1)

399-400: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Correct the priority default in the doc comment.

The comment states false is the default, but addressBarOutranksURLRules defaults to true in LockConfiguration.

Proposed fix
-    /// `true` = address bar, `false` = URL rule (the default).
+    /// `true` = address bar (the default), `false` = URL rule.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Sources/LockIME/AppState.swift` around lines 399 - 400, The doc comment for
the priority selection between address-bar rule and URL rule contains incorrect
information about the default value. Update the comment to correctly state that
true (address bar priority) is the default, not false. The comment currently
says "false = URL rule (the default)" but should be corrected to accurately
reflect that the default in LockConfiguration sets address bar rules to take
priority over URL rules.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@Sources/LockIME/AppState.swift`:
- Around line 399-400: The doc comment for the priority selection between
address-bar rule and URL rule contains incorrect information about the default
value. Update the comment to correctly state that true (address bar priority) is
the default, not false. The comment currently says "false = URL rule (the
default)" but should be corrected to accurately reflect that the default in
LockConfiguration sets address bar rules to take priority over URL rules.

In `@Sources/LockIMEKit/AppMonitor/AddressBarFocusMonitor.swift`:
- Around line 89-94: The stop() method in AddressBarFocusMonitor currently
resets state without notifying listeners, but the AddressBarFocusMonitoring
protocol documents that stopping observation should emit onChange(false). Before
clearing the onChange property and other state in the stop() method, invoke
onChange(false) to notify observers that address bar focus monitoring has been
halted, ensuring the implementation aligns with the documented contract.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c2579244-8065-426e-9d14-522dbe5571c8

📥 Commits

Reviewing files that changed from the base of the PR and between ec25adc and 57ba586.

📒 Files selected for processing (17)
  • Sources/LockIME/AppState.swift
  • Sources/LockIME/Localizable.xcstrings
  • Sources/LockIME/UI/Settings/ActivationLogPane.swift
  • Sources/LockIME/UI/Settings/URLRulesSettingsPane.swift
  • Sources/LockIMEKit/AppMonitor/AddressBarFocusMonitor.swift
  • Sources/LockIMEKit/AppMonitor/AddressBarFocusMonitoring.swift
  • Sources/LockIMEKit/AppMonitor/AddressBarHeuristic.swift
  • Sources/LockIMEKit/LockEngine/InputSource.swift
  • Sources/LockIMEKit/LockEngine/LockEngine.swift
  • Sources/LockIMEKit/Rules/LockConfiguration.swift
  • Sources/LockIMEKit/Rules/RuleResolver.swift
  • Tests/LockIMEKitTests/AddressBarHeuristicTests.swift
  • Tests/LockIMEKitTests/ImportPlanTests.swift
  • Tests/LockIMEKitTests/LockConfigurationTests.swift
  • Tests/LockIMEKitTests/LockEngineAddressBarTests.swift
  • Tests/LockIMEKitTests/RuleResolverTests.swift
  • Tests/LockIMEKitTests/Support/MockAddressBarMonitor.swift

@BlackHole1 BlackHole1 merged commit 493eb7a into main Jun 22, 2026
3 checks passed
@BlackHole1 BlackHole1 deleted the add-address-rules branch June 22, 2026 05:11
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