Skip to content
Merged
Show file tree
Hide file tree
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ shape matters:

## Unreleased

- Added: A Rules submenu in the menu bar lists your rules in match order. Click one to turn it off or on without opening Settings, or choose Edit Rules… to jump to the Rules pane.
- Added: The browser picker can lay out as a horizontal row instead of a vertical list, show icons without names, and use a larger or smaller icon size. Set it under Settings → Browsers → Picker appearance. In the row layout, ←/→ move between choices and ↑/↓ jump a row. The picker names the highlighted choice above the icons, so profiles of the same browser stay distinguishable.
- Fixed: When macOS blocks Junction from reading your browsers' profile lists, Junction now says so instead of showing no profiles. Settings → Browsers, the rule editor and the welcome tour explain that Junction needs Full Disk Access, with a button that opens the right page in System Settings and a Check Again button for after you turn it on.
- Changed: Google Chrome for Testing, the automation build that Playwright and Puppeteer install, no longer shows up as a browser in rules, the picker, or Settings → Browsers.
Expand Down
6 changes: 6 additions & 0 deletions Sources/JunctionApp/Settings/SettingsWindowController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ final class SettingsWindowController: NSObject {
name: .junctionPrefillRule,
object: nil
)
NotificationCenter.default.addObserver(
self,
selector: #selector(jumpToRules),
name: .junctionShowRules,
object: nil
)
}

func show() {
Expand Down
43 changes: 43 additions & 0 deletions Sources/JunctionApp/StatusItemController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,30 @@ final class StatusItemController: NSObject, NSMenuDelegate {
}
recent.submenu = recentMenu
menu.addItem(recent)

// Rules, in match order, each a checkmark toggle for `enabled`.
let rules = NSMenuItem(title: "Rules", action: nil, keyEquivalent: "")
.withSymbol("list.bullet")
let rulesMenu = NSMenu()
if state.config.rules.isEmpty {
let empty = NSMenuItem(title: "No rules yet", action: nil, keyEquivalent: "")
empty.isEnabled = false
rulesMenu.addItem(empty)
}
for (index, rule) in state.config.rules.enumerated() {
let item = NSMenuItem(title: rule.name, action: #selector(toggleRule(_:)), keyEquivalent: "")
item.target = self
item.state = rule.enabled ? .on : .off
item.representedObject = RuleRef(index: index, name: rule.name)
item.toolTip = rule.enabled ? "Click to turn this rule off" : "Click to turn this rule on"
rulesMenu.addItem(item)
}
rulesMenu.addItem(.separator())
let editRules = NSMenuItem(title: "Edit Rules…", action: #selector(showRules), keyEquivalent: "")
editRules.target = self
rulesMenu.addItem(editRules)
rules.submenu = rulesMenu
menu.addItem(rules)
menu.addItem(.separator())

let settings = NSMenuItem(title: "Settings…", action: #selector(openSettings), keyEquivalent: ",")
Expand All @@ -210,6 +234,24 @@ final class StatusItemController: NSObject, NSMenuDelegate {

@objc private func togglePause() { state.routingPaused.toggle() }

/// Position plus name: rule ids aren't persisted, and the file can change while the menu
/// is open, so only toggle if the rule at that position is still the one that was shown.
private struct RuleRef {
let index: Int
let name: String
}

@objc private func toggleRule(_ sender: NSMenuItem) {
guard let ref = sender.representedObject as? RuleRef,
state.config.rules.indices.contains(ref.index),
state.config.rules[ref.index].name == ref.name else { return }
state.updateConfig { $0.rules[ref.index].enabled.toggle() }
}

@objc private func showRules() {
NotificationCenter.default.post(name: .junctionShowRules, object: nil)
}

/// LSUIElement apps aren't frontmost, so Sparkle's window opens behind everything.
@objc private func checkForUpdates() {
NSApp.activate(ignoringOtherApps: true)
Expand Down Expand Up @@ -246,5 +288,6 @@ final class StatusItemController: NSObject, NSMenuDelegate {

extension Notification.Name {
static let junctionPrefillRule = Notification.Name("junctionPrefillRule")
static let junctionShowRules = Notification.Name("junctionShowRules")
}
#endif
4 changes: 4 additions & 0 deletions web/src/data/docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,10 @@
"label": "Recent links",
"body": "the last 10 links Junction routed, in a submenu. In memory only — never written to disk."
},
{
"label": "Rules",
"body": "every rule in match order, checked when it's on. Click one to turn it off or on without opening Settings; <b>Edit Rules…</b> opens the Rules pane."
},
{
"label": "Settings",
"body": "fallback (a browser, or the ask-every-time picker), rules, browsers and profiles — including which appear in the picker and how it looks (a vertical list or a horizontal row, with or without names, any icon size) — deep links, updates."
Expand Down
Loading