├── .gitignore
├── Alan
├── Assets.xcassets
│ ├── Contents.json
│ ├── AppIcon.appiconset
│ │ ├── Icon.png
│ │ └── Contents.json
│ └── AccentColor.colorset
│ │ └── Contents.json
├── Constants.swift
├── PrefsWindowController.swift
├── HighlightWindow.swift
├── AppDelegate.swift
├── Extensions.swift
├── FocusHighlighter.swift
├── PrefsWindowController.xib
└── Base.lproj
│ └── MainMenu.xib
├── README.md
├── Alan.xcodeproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcuserdata
│ │ └── thall.xcuserdatad
│ │ ├── UserInterfaceState.xcuserstate
│ │ └── IDEFindNavigatorScopes.plist
├── xcuserdata
│ └── thall.xcuserdatad
│ │ └── xcschemes
│ │ └── xcschememanagement.plist
├── xcshareddata
│ └── xcschemes
│ │ └── Alan.xcscheme
└── project.pbxproj
└── LICENSE
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 |
--------------------------------------------------------------------------------
/Alan/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Alan/Assets.xcassets/AppIcon.appiconset/Icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tylerhall/Alan/main/Alan/Assets.xcassets/AppIcon.appiconset/Icon.png
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Alan
2 | Draws a border around the active window on macOS.
3 |
4 | 2025-12-03: Thanks to everyone filing bugs, but this app is more software satire than useful utility :)
5 |
--------------------------------------------------------------------------------
/Alan.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Alan.xcodeproj/project.xcworkspace/xcuserdata/thall.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tylerhall/Alan/main/Alan.xcodeproj/project.xcworkspace/xcuserdata/thall.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/Alan/Assets.xcassets/AccentColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "idiom" : "universal"
5 | }
6 | ],
7 | "info" : {
8 | "author" : "xcode",
9 | "version" : 1
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/Alan.xcodeproj/project.xcworkspace/xcuserdata/thall.xcuserdatad/IDEFindNavigatorScopes.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/Alan/Constants.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Constants.swift
3 | // Alan
4 | //
5 | // Created by Tyler Hall on 11/26/25.
6 | //
7 |
8 | import Cocoa
9 |
10 | struct Defaults {
11 | static let lightModeColor = NSColor.black
12 | static let darkModeColor = NSColor.white
13 | }
14 |
15 | struct Key {
16 | static let width = "width"
17 | static let inset = "inset"
18 | static let hideDock = "hideDock"
19 | static let lightMode = "lightMode"
20 | static let darkMode = "darkMode"
21 | }
22 |
--------------------------------------------------------------------------------
/Alan.xcodeproj/xcuserdata/thall.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | Alan.xcscheme_^#shared#^_
8 |
9 | orderHint
10 | 0
11 |
12 |
13 | SuppressBuildableAutocreation
14 |
15 | C61847E62ED753EE0093A1C9
16 |
17 | primary
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2025 Tyler Hall
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Alan/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "mac",
5 | "scale" : "1x",
6 | "size" : "16x16"
7 | },
8 | {
9 | "idiom" : "mac",
10 | "scale" : "2x",
11 | "size" : "16x16"
12 | },
13 | {
14 | "idiom" : "mac",
15 | "scale" : "1x",
16 | "size" : "32x32"
17 | },
18 | {
19 | "idiom" : "mac",
20 | "scale" : "2x",
21 | "size" : "32x32"
22 | },
23 | {
24 | "idiom" : "mac",
25 | "scale" : "1x",
26 | "size" : "128x128"
27 | },
28 | {
29 | "idiom" : "mac",
30 | "scale" : "2x",
31 | "size" : "128x128"
32 | },
33 | {
34 | "idiom" : "mac",
35 | "scale" : "1x",
36 | "size" : "256x256"
37 | },
38 | {
39 | "idiom" : "mac",
40 | "scale" : "2x",
41 | "size" : "256x256"
42 | },
43 | {
44 | "idiom" : "mac",
45 | "scale" : "1x",
46 | "size" : "512x512"
47 | },
48 | {
49 | "filename" : "Icon.png",
50 | "idiom" : "mac",
51 | "scale" : "2x",
52 | "size" : "512x512"
53 | }
54 | ],
55 | "info" : {
56 | "author" : "xcode",
57 | "version" : 1
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/Alan/PrefsWindowController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PrefsWindowController.swift
3 | // Alan
4 | //
5 | // Created by Tyler Hall on 11/26/25.
6 | //
7 |
8 | import AppKit
9 |
10 | class PrefsWindowController: NSWindowController {
11 |
12 | @IBOutlet weak var lightModeColorWell: NSColorWell!
13 | @IBOutlet weak var darkModeColorWell: NSColorWell!
14 |
15 | override func windowDidLoad() {
16 | super.windowDidLoad()
17 |
18 | lightModeColorWell.color = UserDefaults.standard.color(forKey: Key.lightMode) ?? Defaults.lightModeColor
19 | darkModeColorWell.color = UserDefaults.standard.color(forKey: Key.darkMode) ?? Defaults.darkModeColor
20 |
21 | NotificationCenter.default.addObserver(self, selector: #selector(PrefsWindowController.userDefaultsChanged), name: UserDefaults.didChangeNotification, object: nil)
22 | }
23 |
24 | @IBAction func lightModeChanged(_ sender: NSColorWell) {
25 | UserDefaults.standard.setColor(sender.color, forKey: Key.lightMode)
26 | }
27 |
28 | @IBAction func darkModeChanged(_ sender: NSColorWell) {
29 | UserDefaults.standard.setColor(sender.color, forKey: Key.darkMode)
30 | }
31 |
32 | @objc func userDefaultsChanged() {
33 | FocusHighlighter.shared.forceUpdate()
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/Alan/HighlightWindow.swift:
--------------------------------------------------------------------------------
1 | //
2 | // HighlightWindow.swift
3 | // Alan
4 | //
5 | // Created by Tyler Hall on 11/26/25.
6 | //
7 |
8 | import AppKit
9 |
10 | class HighlightWindow: NSWindow {
11 |
12 | init() {
13 | super.init(
14 | contentRect: .zero,
15 | styleMask: .borderless,
16 | backing: .buffered,
17 | defer: false
18 | )
19 |
20 | self.isOpaque = false
21 | self.hasShadow = false
22 | self.backgroundColor = .clear
23 | self.ignoresMouseEvents = true
24 | self.level = .statusBar
25 | self.collectionBehavior = [.canJoinAllSpaces, .ignoresCycle]
26 | self.isReleasedWhenClosed = false
27 |
28 | self.contentView = HighlightView(frame: .zero)
29 | }
30 |
31 | func updateFrame(to rect: CGRect) {
32 | let newRect = rect.insetBy(dx: -2, dy: -2)
33 | setFrame(newRect, display: true)
34 | self.contentView?.setNeedsDisplay(.infinite)
35 | orderFrontRegardless()
36 | }
37 | }
38 |
39 | class HighlightView: NSView {
40 | override var isFlipped: Bool { true }
41 |
42 | override func draw(_ dirtyRect: NSRect) {
43 | super.draw(dirtyRect)
44 |
45 | NSGraphicsContext.current?.saveGraphicsState()
46 | defer { NSGraphicsContext.current?.restoreGraphicsState() }
47 |
48 | var inset = UserDefaults.standard.integer(forKey: Key.inset)
49 | inset = max(1, min(20, inset))
50 |
51 | var width = UserDefaults.standard.integer(forKey: Key.width)
52 | width = max(1, min(20, width))
53 |
54 | let path = NSBezierPath(rect: bounds.insetBy(dx: CGFloat(inset), dy: CGFloat(inset)))
55 | path.lineWidth = CGFloat(width)
56 |
57 | let color: NSColor
58 | if NSAppearance.isLightMode {
59 | color = UserDefaults.standard.color(forKey: Key.lightMode) ?? Defaults.lightModeColor
60 | } else {
61 | color = UserDefaults.standard.color(forKey: Key.darkMode) ?? Defaults.darkModeColor
62 | }
63 | color.setStroke()
64 |
65 | path.stroke()
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/Alan/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // Alan
4 | //
5 | // Created by Tyler Hall on 11/26/25.
6 | //
7 |
8 | import Cocoa
9 | import ApplicationServices
10 |
11 | @main
12 | class AppDelegate: NSObject, NSApplicationDelegate {
13 |
14 | let prefsWindowController: PrefsWindowController = {
15 | return PrefsWindowController(windowNibName: String(describing: PrefsWindowController.self))
16 | }()
17 |
18 | func applicationDidFinishLaunching(_ aNotification: Notification) {
19 |
20 | UserDefaults.standard.register(defaults: [
21 | Key.width: 5,
22 | Key.inset: 4,
23 | Key.hideDock: false,
24 | Key.lightMode: Defaults.lightModeColor,
25 | Key.darkMode: Defaults.darkModeColor
26 | ])
27 |
28 | if UserDefaults.standard.bool(forKey: Key.hideDock) == true {
29 | NSApp.setActivationPolicy(.accessory)
30 | }
31 |
32 | requestAccessibilityPermissionIfNeeded()
33 |
34 | FocusHighlighter.shared.start()
35 | }
36 |
37 | func requestAccessibilityPermissionIfNeeded() {
38 | let options: CFDictionary = [
39 | kAXTrustedCheckOptionPrompt.takeUnretainedValue() as String: true
40 | ] as CFDictionary
41 |
42 | let trusted = AXIsProcessTrustedWithOptions(options)
43 |
44 | guard trusted else {
45 | if let url = URL(string: "x-apple.systempreferences:com.apple.preference.universalaccess") {
46 | NSWorkspace.shared.open(url)
47 | }
48 |
49 | // Give the user a clear message and quit so they can enable it
50 | let alert = NSAlert()
51 | alert.messageText = "Accessibility Permission Required"
52 | alert.informativeText = """
53 | Alan needs Accessibility permission to highlight the focused window.
54 |
55 | Please open System Settings → Privacy & Security → Accessibility
56 | and enable “Alan”.
57 |
58 | Then relaunch Alan.
59 | """
60 | alert.addButton(withTitle: "Quit")
61 | alert.runModal()
62 |
63 | NSApp.terminate(nil)
64 | return
65 | }
66 | }
67 |
68 | @IBAction func showPrefs(_ sender: AnyObject?) {
69 | prefsWindowController.showWindow(nil)
70 | prefsWindowController.window?.makeKeyAndOrderFront(nil)
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/Alan/Extensions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Extensions.swift
3 | // Alan
4 | //
5 | // Created by Tyler Hall on 11/26/25.
6 | //
7 |
8 | import AppKit
9 |
10 | // Source - https://stackoverflow.com/a
11 | // Posted by workingdog support Ukraine, modified by community. See post 'Timeline' for change history
12 | // Retrieved 2025-11-26, License - CC BY-SA 4.0
13 |
14 | extension NSColor {
15 |
16 | convenience init(hex: String) {
17 | let trimHex = hex.trimmingCharacters(in: .whitespacesAndNewlines)
18 | let dropHash = String(trimHex.dropFirst()).trimmingCharacters(in: .whitespacesAndNewlines)
19 | let hexString = trimHex.starts(with: "#") ? dropHash : trimHex
20 | let ui64 = UInt64(hexString, radix: 16)
21 | let value = ui64 != nil ? Int(ui64!) : 0
22 | // #RRGGBB
23 | var components = (
24 | R: CGFloat((value >> 16) & 0xff) / 255,
25 | G: CGFloat((value >> 08) & 0xff) / 255,
26 | B: CGFloat((value >> 00) & 0xff) / 255,
27 | a: CGFloat(1)
28 | )
29 | if String(hexString).count == 8 {
30 | // #RRGGBBAA
31 | components = (
32 | R: CGFloat((value >> 24) & 0xff) / 255,
33 | G: CGFloat((value >> 16) & 0xff) / 255,
34 | B: CGFloat((value >> 08) & 0xff) / 255,
35 | a: CGFloat((value >> 00) & 0xff) / 255
36 | )
37 | }
38 | self.init(red: components.R, green: components.G, blue: components.B, alpha: components.a)
39 | }
40 |
41 | func toHex(alpha: Bool = false) -> String? {
42 | guard let components = cgColor.components, components.count >= 3 else {
43 | return nil
44 | }
45 |
46 | let r = Float(components[0])
47 | let g = Float(components[1])
48 | let b = Float(components[2])
49 | var a = Float(1.0)
50 |
51 | if components.count >= 4 {
52 | a = Float(components[3])
53 | }
54 |
55 | if alpha {
56 | return String(format: "%02lX%02lX%02lX%02lX", lroundf(r * 255), lroundf(g * 255), lroundf(b * 255), lroundf(a * 255))
57 | } else {
58 | return String(format: "%02lX%02lX%02lX", lroundf(r * 255), lroundf(g * 255), lroundf(b * 255))
59 | }
60 | }
61 | }
62 |
63 | extension NSAppearance {
64 | static var isdarkMode: Bool {
65 | switch NSApp.effectiveAppearance.name {
66 | case .darkAqua, .vibrantDark, .accessibilityHighContrastDarkAqua, .accessibilityHighContrastVibrantDark:
67 | return true
68 | default:
69 | return false
70 | }
71 | }
72 |
73 | static var isLightMode: Bool {
74 | return !isdarkMode
75 | }
76 | }
77 |
78 | extension UserDefaults {
79 | func setColor(_ color: NSColor, forKey key: String) {
80 | let data = NSKeyedArchiver.archivedData(withRootObject: color)
81 | self.set(data, forKey: key)
82 | }
83 |
84 | func color(forKey key: String) -> NSColor? {
85 | guard let data = self.data(forKey: key) else { return nil }
86 | return NSKeyedUnarchiver.unarchiveObject(with: data) as? NSColor
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/Alan.xcodeproj/xcshareddata/xcschemes/Alan.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
9 |
10 |
16 |
22 |
23 |
24 |
25 |
26 |
32 |
33 |
43 |
45 |
51 |
52 |
53 |
54 |
60 |
62 |
68 |
69 |
70 |
71 |
73 |
74 |
77 |
78 |
79 |
--------------------------------------------------------------------------------
/Alan/FocusHighlighter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FocusHighlighter.swift
3 | // Alan
4 | //
5 | // Created by Tyler Hall on 11/26/25.
6 | //
7 |
8 | import AppKit
9 | import ApplicationServices
10 |
11 | class FocusHighlighter {
12 |
13 | static let shared = FocusHighlighter()
14 |
15 | private let systemWideElement = AXUIElementCreateSystemWide()
16 | private let highlightWindow = HighlightWindow()
17 | private var timer: Timer?
18 | private var lastFrame: CGRect?
19 |
20 | func start() {
21 | handleFocusChange()
22 |
23 | timer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { [weak self] _ in
24 | self?.handleFocusChange()
25 | }
26 |
27 | if let timer = timer {
28 | RunLoop.current.add(timer, forMode: .common)
29 | }
30 | }
31 |
32 | func forceUpdate() {
33 | guard let lastFrame else { return }
34 | highlightWindow.updateFrame(to: lastFrame)
35 | }
36 |
37 | private func handleFocusChange() {
38 | guard let axFrame = currentFocusedWindowFrame() else {
39 | if highlightWindow.isVisible {
40 | highlightWindow.orderOut(nil)
41 | lastFrame = nil
42 | }
43 | return
44 | }
45 |
46 | let cocoaFrame = cocoaRect(fromAXRect: axFrame)
47 |
48 | if lastFrame != cocoaFrame {
49 | lastFrame = cocoaFrame
50 | highlightWindow.updateFrame(to: cocoaFrame)
51 | }
52 | }
53 |
54 | // Hello, darkness, my old friend. I'm still really bad at this API.
55 | private func currentFocusedWindowFrame() -> CGRect? {
56 | var focusedElement: CFTypeRef?
57 | let err = AXUIElementCopyAttributeValue(
58 | systemWideElement,
59 | kAXFocusedUIElementAttribute as CFString,
60 | &focusedElement
61 | )
62 |
63 | guard err == .success, let element = focusedElement as! AXUIElement? else {
64 | return nil
65 | }
66 |
67 | // If focus is a child, ask for its window
68 | var windowElement: CFTypeRef?
69 | let windowErr = AXUIElementCopyAttributeValue(
70 | element,
71 | kAXWindowAttribute as CFString,
72 | &windowElement
73 | )
74 |
75 | let targetElement: AXUIElement
76 | if windowErr == .success, let w = windowElement as! AXUIElement? {
77 | targetElement = w
78 | } else {
79 | targetElement = element
80 | }
81 |
82 | var frameValue: CFTypeRef?
83 | let frameErr = AXUIElementCopyAttributeValue(
84 | targetElement,
85 | "AXFrame" as CFString,
86 | &frameValue
87 | )
88 |
89 | guard frameErr == .success,
90 | let cfValue = frameValue,
91 | CFGetTypeID(cfValue) == AXValueGetTypeID()
92 | else {
93 | return nil
94 | }
95 |
96 | var rect = CGRect.zero
97 | if AXValueGetType(cfValue as! AXValue) == .cgRect {
98 | AXValueGetValue(cfValue as! AXValue, .cgRect, &rect)
99 | return rect
100 | }
101 |
102 | return nil
103 | }
104 | }
105 |
106 | private func cocoaRect(fromAXRect axRect: CGRect) -> CGRect {
107 | // Find the maximum Y coordinate across all screens in Cocoa space
108 | // This represents the total height of the entire screen arrangement
109 | // AX coordinates start from y=0 at the top of the topmost screen
110 | // Cocoa coordinates start from y=0 at the bottom of the bottommost screen
111 | // So we need the total height to properly flip the Y coordinate
112 | let maxY = NSScreen.screens.map { $0.frame.maxY }.max() ?? 0
113 |
114 | var rect = axRect
115 | rect.origin.y = maxY - (axRect.origin.y + axRect.height)
116 |
117 | return rect
118 | }
119 |
--------------------------------------------------------------------------------
/Alan.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 77;
7 | objects = {
8 |
9 | /* Begin PBXFileReference section */
10 | C61847E72ED753EE0093A1C9 /* Alan.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Alan.app; sourceTree = BUILT_PRODUCTS_DIR; };
11 | /* End PBXFileReference section */
12 |
13 | /* Begin PBXFileSystemSynchronizedRootGroup section */
14 | C61847E92ED753EE0093A1C9 /* Alan */ = {
15 | isa = PBXFileSystemSynchronizedRootGroup;
16 | path = Alan;
17 | sourceTree = "";
18 | };
19 | /* End PBXFileSystemSynchronizedRootGroup section */
20 |
21 | /* Begin PBXFrameworksBuildPhase section */
22 | C61847E42ED753EE0093A1C9 /* Frameworks */ = {
23 | isa = PBXFrameworksBuildPhase;
24 | buildActionMask = 2147483647;
25 | files = (
26 | );
27 | runOnlyForDeploymentPostprocessing = 0;
28 | };
29 | /* End PBXFrameworksBuildPhase section */
30 |
31 | /* Begin PBXGroup section */
32 | C61847DE2ED753EE0093A1C9 = {
33 | isa = PBXGroup;
34 | children = (
35 | C61847E92ED753EE0093A1C9 /* Alan */,
36 | C61847E82ED753EE0093A1C9 /* Products */,
37 | );
38 | sourceTree = "";
39 | };
40 | C61847E82ED753EE0093A1C9 /* Products */ = {
41 | isa = PBXGroup;
42 | children = (
43 | C61847E72ED753EE0093A1C9 /* Alan.app */,
44 | );
45 | name = Products;
46 | sourceTree = "";
47 | };
48 | /* End PBXGroup section */
49 |
50 | /* Begin PBXNativeTarget section */
51 | C61847E62ED753EE0093A1C9 /* Alan */ = {
52 | isa = PBXNativeTarget;
53 | buildConfigurationList = C61847F32ED753EF0093A1C9 /* Build configuration list for PBXNativeTarget "Alan" */;
54 | buildPhases = (
55 | C61847E32ED753EE0093A1C9 /* Sources */,
56 | C61847E42ED753EE0093A1C9 /* Frameworks */,
57 | C61847E52ED753EE0093A1C9 /* Resources */,
58 | );
59 | buildRules = (
60 | );
61 | dependencies = (
62 | );
63 | fileSystemSynchronizedGroups = (
64 | C61847E92ED753EE0093A1C9 /* Alan */,
65 | );
66 | name = Alan;
67 | packageProductDependencies = (
68 | );
69 | productName = Focused;
70 | productReference = C61847E72ED753EE0093A1C9 /* Alan.app */;
71 | productType = "com.apple.product-type.application";
72 | };
73 | /* End PBXNativeTarget section */
74 |
75 | /* Begin PBXProject section */
76 | C61847DF2ED753EE0093A1C9 /* Project object */ = {
77 | isa = PBXProject;
78 | attributes = {
79 | BuildIndependentTargetsInParallel = 1;
80 | LastSwiftUpdateCheck = 2600;
81 | LastUpgradeCheck = 2600;
82 | TargetAttributes = {
83 | C61847E62ED753EE0093A1C9 = {
84 | CreatedOnToolsVersion = 26.0.1;
85 | };
86 | };
87 | };
88 | buildConfigurationList = C61847E22ED753EE0093A1C9 /* Build configuration list for PBXProject "Alan" */;
89 | developmentRegion = en;
90 | hasScannedForEncodings = 0;
91 | knownRegions = (
92 | en,
93 | Base,
94 | );
95 | mainGroup = C61847DE2ED753EE0093A1C9;
96 | minimizedProjectReferenceProxies = 1;
97 | preferredProjectObjectVersion = 77;
98 | productRefGroup = C61847E82ED753EE0093A1C9 /* Products */;
99 | projectDirPath = "";
100 | projectRoot = "";
101 | targets = (
102 | C61847E62ED753EE0093A1C9 /* Alan */,
103 | );
104 | };
105 | /* End PBXProject section */
106 |
107 | /* Begin PBXResourcesBuildPhase section */
108 | C61847E52ED753EE0093A1C9 /* Resources */ = {
109 | isa = PBXResourcesBuildPhase;
110 | buildActionMask = 2147483647;
111 | files = (
112 | );
113 | runOnlyForDeploymentPostprocessing = 0;
114 | };
115 | /* End PBXResourcesBuildPhase section */
116 |
117 | /* Begin PBXSourcesBuildPhase section */
118 | C61847E32ED753EE0093A1C9 /* Sources */ = {
119 | isa = PBXSourcesBuildPhase;
120 | buildActionMask = 2147483647;
121 | files = (
122 | );
123 | runOnlyForDeploymentPostprocessing = 0;
124 | };
125 | /* End PBXSourcesBuildPhase section */
126 |
127 | /* Begin XCBuildConfiguration section */
128 | C61847F12ED753EF0093A1C9 /* Debug */ = {
129 | isa = XCBuildConfiguration;
130 | buildSettings = {
131 | ALWAYS_SEARCH_USER_PATHS = NO;
132 | ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
133 | CLANG_ANALYZER_NONNULL = YES;
134 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
135 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
136 | CLANG_ENABLE_MODULES = YES;
137 | CLANG_ENABLE_OBJC_ARC = YES;
138 | CLANG_ENABLE_OBJC_WEAK = YES;
139 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
140 | CLANG_WARN_BOOL_CONVERSION = YES;
141 | CLANG_WARN_COMMA = YES;
142 | CLANG_WARN_CONSTANT_CONVERSION = YES;
143 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
144 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
145 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
146 | CLANG_WARN_EMPTY_BODY = YES;
147 | CLANG_WARN_ENUM_CONVERSION = YES;
148 | CLANG_WARN_INFINITE_RECURSION = YES;
149 | CLANG_WARN_INT_CONVERSION = YES;
150 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
151 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
152 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
153 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
154 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
155 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
156 | CLANG_WARN_STRICT_PROTOTYPES = YES;
157 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
158 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
159 | CLANG_WARN_UNREACHABLE_CODE = YES;
160 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
161 | COPY_PHASE_STRIP = NO;
162 | DEBUG_INFORMATION_FORMAT = dwarf;
163 | DEVELOPMENT_TEAM = 3A6K89K388;
164 | ENABLE_STRICT_OBJC_MSGSEND = YES;
165 | ENABLE_TESTABILITY = YES;
166 | ENABLE_USER_SCRIPT_SANDBOXING = YES;
167 | GCC_C_LANGUAGE_STANDARD = gnu17;
168 | GCC_DYNAMIC_NO_PIC = NO;
169 | GCC_NO_COMMON_BLOCKS = YES;
170 | GCC_OPTIMIZATION_LEVEL = 0;
171 | GCC_PREPROCESSOR_DEFINITIONS = (
172 | "DEBUG=1",
173 | "$(inherited)",
174 | );
175 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
176 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
177 | GCC_WARN_UNDECLARED_SELECTOR = YES;
178 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
179 | GCC_WARN_UNUSED_FUNCTION = YES;
180 | GCC_WARN_UNUSED_VARIABLE = YES;
181 | LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
182 | MACOSX_DEPLOYMENT_TARGET = 15.7;
183 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
184 | MTL_FAST_MATH = YES;
185 | ONLY_ACTIVE_ARCH = YES;
186 | SDKROOT = macosx;
187 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
188 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
189 | };
190 | name = Debug;
191 | };
192 | C61847F22ED753EF0093A1C9 /* Release */ = {
193 | isa = XCBuildConfiguration;
194 | buildSettings = {
195 | ALWAYS_SEARCH_USER_PATHS = NO;
196 | ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
197 | CLANG_ANALYZER_NONNULL = YES;
198 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
199 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
200 | CLANG_ENABLE_MODULES = YES;
201 | CLANG_ENABLE_OBJC_ARC = YES;
202 | CLANG_ENABLE_OBJC_WEAK = YES;
203 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
204 | CLANG_WARN_BOOL_CONVERSION = YES;
205 | CLANG_WARN_COMMA = YES;
206 | CLANG_WARN_CONSTANT_CONVERSION = YES;
207 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
208 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
209 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
210 | CLANG_WARN_EMPTY_BODY = YES;
211 | CLANG_WARN_ENUM_CONVERSION = YES;
212 | CLANG_WARN_INFINITE_RECURSION = YES;
213 | CLANG_WARN_INT_CONVERSION = YES;
214 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
215 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
216 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
217 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
218 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
219 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
220 | CLANG_WARN_STRICT_PROTOTYPES = YES;
221 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
222 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
223 | CLANG_WARN_UNREACHABLE_CODE = YES;
224 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
225 | COPY_PHASE_STRIP = NO;
226 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
227 | DEVELOPMENT_TEAM = 3A6K89K388;
228 | ENABLE_NS_ASSERTIONS = NO;
229 | ENABLE_STRICT_OBJC_MSGSEND = YES;
230 | ENABLE_USER_SCRIPT_SANDBOXING = YES;
231 | GCC_C_LANGUAGE_STANDARD = gnu17;
232 | GCC_NO_COMMON_BLOCKS = YES;
233 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
234 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
235 | GCC_WARN_UNDECLARED_SELECTOR = YES;
236 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
237 | GCC_WARN_UNUSED_FUNCTION = YES;
238 | GCC_WARN_UNUSED_VARIABLE = YES;
239 | LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
240 | MACOSX_DEPLOYMENT_TARGET = 15.7;
241 | MTL_ENABLE_DEBUG_INFO = NO;
242 | MTL_FAST_MATH = YES;
243 | SDKROOT = macosx;
244 | SWIFT_COMPILATION_MODE = wholemodule;
245 | };
246 | name = Release;
247 | };
248 | C61847F42ED753EF0093A1C9 /* Debug */ = {
249 | isa = XCBuildConfiguration;
250 | buildSettings = {
251 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
252 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
253 | CODE_SIGN_STYLE = Automatic;
254 | COMBINE_HIDPI_IMAGES = YES;
255 | CURRENT_PROJECT_VERSION = 1;
256 | DEVELOPMENT_TEAM = 3A6K89K388;
257 | ENABLE_APP_SANDBOX = NO;
258 | ENABLE_HARDENED_RUNTIME = YES;
259 | GENERATE_INFOPLIST_FILE = YES;
260 | INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities";
261 | INFOPLIST_KEY_NSHumanReadableCopyright = "";
262 | INFOPLIST_KEY_NSMainNibFile = MainMenu;
263 | INFOPLIST_KEY_NSPrincipalClass = NSApplication;
264 | LD_RUNPATH_SEARCH_PATHS = (
265 | "$(inherited)",
266 | "@executable_path/../Frameworks",
267 | );
268 | MARKETING_VERSION = 1.0;
269 | PRODUCT_BUNDLE_IDENTIFIER = studio.retina.Alan;
270 | PRODUCT_NAME = "$(TARGET_NAME)";
271 | REGISTER_APP_GROUPS = YES;
272 | STRING_CATALOG_GENERATE_SYMBOLS = YES;
273 | SWIFT_APPROACHABLE_CONCURRENCY = YES;
274 | SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor;
275 | SWIFT_EMIT_LOC_STRINGS = YES;
276 | SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
277 | SWIFT_VERSION = 5.0;
278 | };
279 | name = Debug;
280 | };
281 | C61847F52ED753EF0093A1C9 /* Release */ = {
282 | isa = XCBuildConfiguration;
283 | buildSettings = {
284 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
285 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
286 | CODE_SIGN_STYLE = Automatic;
287 | COMBINE_HIDPI_IMAGES = YES;
288 | CURRENT_PROJECT_VERSION = 1;
289 | DEVELOPMENT_TEAM = 3A6K89K388;
290 | ENABLE_APP_SANDBOX = NO;
291 | ENABLE_HARDENED_RUNTIME = YES;
292 | GENERATE_INFOPLIST_FILE = YES;
293 | INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities";
294 | INFOPLIST_KEY_NSHumanReadableCopyright = "";
295 | INFOPLIST_KEY_NSMainNibFile = MainMenu;
296 | INFOPLIST_KEY_NSPrincipalClass = NSApplication;
297 | LD_RUNPATH_SEARCH_PATHS = (
298 | "$(inherited)",
299 | "@executable_path/../Frameworks",
300 | );
301 | MARKETING_VERSION = 1.0;
302 | PRODUCT_BUNDLE_IDENTIFIER = studio.retina.Alan;
303 | PRODUCT_NAME = "$(TARGET_NAME)";
304 | REGISTER_APP_GROUPS = YES;
305 | STRING_CATALOG_GENERATE_SYMBOLS = YES;
306 | SWIFT_APPROACHABLE_CONCURRENCY = YES;
307 | SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor;
308 | SWIFT_EMIT_LOC_STRINGS = YES;
309 | SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
310 | SWIFT_VERSION = 5.0;
311 | };
312 | name = Release;
313 | };
314 | /* End XCBuildConfiguration section */
315 |
316 | /* Begin XCConfigurationList section */
317 | C61847E22ED753EE0093A1C9 /* Build configuration list for PBXProject "Alan" */ = {
318 | isa = XCConfigurationList;
319 | buildConfigurations = (
320 | C61847F12ED753EF0093A1C9 /* Debug */,
321 | C61847F22ED753EF0093A1C9 /* Release */,
322 | );
323 | defaultConfigurationIsVisible = 0;
324 | defaultConfigurationName = Release;
325 | };
326 | C61847F32ED753EF0093A1C9 /* Build configuration list for PBXNativeTarget "Alan" */ = {
327 | isa = XCConfigurationList;
328 | buildConfigurations = (
329 | C61847F42ED753EF0093A1C9 /* Debug */,
330 | C61847F52ED753EF0093A1C9 /* Release */,
331 | );
332 | defaultConfigurationIsVisible = 0;
333 | defaultConfigurationName = Release;
334 | };
335 | /* End XCConfigurationList section */
336 | };
337 | rootObject = C61847DF2ED753EE0093A1C9 /* Project object */;
338 | }
339 |
--------------------------------------------------------------------------------
/Alan/PrefsWindowController.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
--------------------------------------------------------------------------------
/Alan/Base.lproj/MainMenu.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
682 |
683 |
684 |
685 |
--------------------------------------------------------------------------------