diff options
| author | Tommaso Negri <me@tommasongr.com> | 2026-08-09 23:58:42 +0200 |
|---|---|---|
| committer | Tommaso Negri <me@tommasongr.com> | 2026-08-09 23:58:44 +0200 |
| commit | d59b22e557fa5d69ea6be725a7e5bcd829588225 (patch) | |
| tree | 618e20ef2b648e46eac3d51e14b8e0fcc1faba3c /Sources/phbar | |
| parent | 62ce97dcb63d61c8db739c1b32bd387042ca66a0 (diff) | |
| download | bar-d59b22e557fa5d69ea6be725a7e5bcd829588225.tar.gz bar-d59b22e557fa5d69ea6be725a7e5bcd829588225.zip | |
rename project and add documentation guides
Diffstat (limited to 'Sources/phbar')
| -rw-r--r-- | Sources/phbar/Config.swift | 143 | ||||
| -rw-r--r-- | Sources/phbar/Models/AppDelegate.swift | 47 | ||||
| -rw-r--r-- | Sources/phbar/Models/Block.swift | 107 | ||||
| -rw-r--r-- | Sources/phbar/Models/Cell.swift | 70 | ||||
| -rw-r--r-- | Sources/phbar/Models/HEX.swift | 114 | ||||
| -rw-r--r-- | Sources/phbar/Models/Notifier.swift | 90 | ||||
| -rw-r--r-- | Sources/phbar/Models/Runner.swift | 72 | ||||
| -rw-r--r-- | Sources/phbar/Models/Screen.swift | 38 | ||||
| -rw-r--r-- | Sources/phbar/Views/BarView.swift | 29 | ||||
| -rw-r--r-- | Sources/phbar/Views/BarWindow.swift | 32 | ||||
| -rw-r--r-- | Sources/phbar/Views/BlockView.swift | 60 | ||||
| -rw-r--r-- | Sources/phbar/Views/CellView.swift | 11 | ||||
| -rw-r--r-- | Sources/phbar/main.swift | 8 |
13 files changed, 0 insertions, 821 deletions
diff --git a/Sources/phbar/Config.swift b/Sources/phbar/Config.swift deleted file mode 100644 index e948b30..0000000 --- a/Sources/phbar/Config.swift +++ /dev/null @@ -1,143 +0,0 @@ -import SwiftUI - -/// The top-level configuration for the bar. -/// -/// Set appearance, define blocks, and choose the bar position here. -/// This is the primary file a user edits to customise phbar. -enum Config { - /// The bar height in points (pt). - /// - /// Tip: Set this value to 30 to match the default macOS - /// menu bar height. - static let height: CGFloat = 30 - - /// The bar position on the screen. - static let position: Position = .top - - // NOTE: Customizing font - // - // You can use system default fonts thanks to the standard SwiftUI - // methods and access properties. However, you can also use your - // preferred custom font. - // - // E.g: `.custom("Comic Code", fixedSize: 14)` - // - // You may want to use a patched Nerd Font (https://www.nerdfonts.com) - // so you can have icons in your bar. Alternatively, you can display - // SF Symbols by copy and pasting them as glyphs. - - /// The font used for all text and icons in the bar. - static let font: Font = .system(size: 14, weight: .regular, design: .monospaced) - - // NOTE: Customizing colors - // - // You can use system default colors and the standard SwiftUI - // methods to define them. However, for convenience we added - // support for hex colors too (see ``HEX``). - // - // E.g: `.hex("#000000")` - - /// The color used for texts and icons. - static let foreground: Color = .hex("#FFFFFF") - - /// The color used for the bar background. - static let background: Color = .hex("#000000").opacity(0.75) - - /// The colors used to highlight a cell at different levels (see ``Cell``). - static let highlights: [Color] = [.blue, .purple] - - /// The blocks that make up the bar, listed in display order. - /// - /// Each block is positioned on the left or right side of the bar - /// and runs a shell command, optionally on a repeating interval. - /// See ``Config-swift.enum/Block`` for the full configuration options. - static let blocks: [Block] = [ - Block(name: "version", placement: .left, command: "echo '&!1 phbar v1.0.0 '"), - Block(name: "ph", placement: .left, command: "echo ' by Panini House'"), - Block(name: "memory", placement: .right, command: "echo \" Hi $USER, welcome to phbar! | \""), - Block(name: "clock", placement: .right, command: "echo \" $(date '+%a %d, %H:%M:%S') \"", interval: 1), - ] -} - -// MARK: Types reference - -extension Config { - /// The bar position on the screen. - enum Position: Sendable { - /// Anchor the bar at the top of the screen. - case top - - /// Anchor the bar at the bottom of the screen. - case bottom - } - - /// A block defined in the configuration. - /// - /// Represents one unit of the bar. Each block is a named command that - /// produces output on an optional timer. See ``phbar/Block`` for the - /// runtime counterpart that manages execution and cell publishing. - struct Block: Sendable { - /// The block identifier used for manual updates. - /// - /// Some blocks do not need to be updated on an defined - /// interval. You might want instead to react to specific - /// events or "manually" update them. - /// In such cases, you can send a standard system - /// notification that tells phbar to trigger an update - /// for the block identified by the name. - /// - /// E.g: `notifyutil -p house.panini.phbar.touch.<name>` - /// - /// Be aware that nothing stops you from having multiple - /// blocks with the same name. In such case, triggering - /// a notification will cause all blocks with that name - /// to be re-evaluated. - let name: String - - /// Whether the block anchors to the left or right side of the bar. - let placement: Placement - - /// The shell command used for updating the content. - /// - /// You can write an expression directly in the block - /// definition or you can point to a proper executable - /// script somewhere on the system. - /// - /// The command process will inherit the environment - /// of the process that executed phbar in the first place. - /// This means that running phbar from your regular shell - /// or from launchctl might result in different behaviours. - /// Make sure to test both scenarios properly. - let command: String - - /// The interval, in seconds, at which the command is executed. - /// - /// If set to `nil`, the block will be evaluated just once - /// at launch. You can of course trigger a manual update via - /// a notification later on. - let interval: Double? - - /// Define a block of the bar. - /// - /// - Parameters: - /// - name: The block identifier used for manual updates. - /// - placement: Left or right side of the bar. - /// - command: The shell command used for updating the content. - /// - interval: The interval, in seconds, at which the command is executed. - /// - init(name: String, placement: Placement, command: String, interval: Double? = nil) { - self.name = name - self.placement = placement - self.command = command - self.interval = interval - } - - /// Where the block is anchored inside the bar. - enum Placement: Sendable { - /// Anchored to the left side of the bar. - case left - /// Anchored to the right side of the bar. - case right - } - } -} diff --git a/Sources/phbar/Models/AppDelegate.swift b/Sources/phbar/Models/AppDelegate.swift deleted file mode 100644 index a7ecb5a..0000000 --- a/Sources/phbar/Models/AppDelegate.swift +++ /dev/null @@ -1,47 +0,0 @@ -import AppKit -import SwiftUI - -@MainActor -final class AppDelegate: NSObject, NSApplicationDelegate { - private var windows: [BarWindow] = [] - private var blocks: [Block] = Config.blocks.map(\.resolved) - private var tokens: [UnsafeMutableRawPointer] = [] - - func applicationDidFinishLaunching(_ notification: Notification) { - for screen in NSScreen.screens { - let frame = Screen.barFrame(screen, position: Config.position, height: Config.height) - let window = BarWindow(contentRect: frame) - window.contentView = NSHostingView(rootView: BarView(blocks: blocks)) - window.orderFrontRegardless() - windows.append(window) - } - - // Register one listener per unique name; a single post fans out to every - // block with that name (see Notifier). - for name in Set(blocks.map(\.name)) { - let token = Notifier.register(name) { [weak self] in - self?.handleRefresh(name) - } - tokens.append(token) - } - - for block in blocks { block.start() } - } - - func applicationWillTerminate(_ notification: Notification) { - for block in blocks { block.stop() } - for token in tokens { Notifier.cancel(token) } - } - - /// Called from a Darwin notification handler. Marked `nonisolated` because - /// libnotify's delivery contract isn't statically visible to Swift, so we - /// hop back to the main actor explicitly. - nonisolated func handleRefresh(_ name: String) { - Task { @MainActor [weak self] in - guard let self else { return } - for block in self.blocks where block.name == name { - block.runScript() - } - } - } -} diff --git a/Sources/phbar/Models/Block.swift b/Sources/phbar/Models/Block.swift deleted file mode 100644 index fe575e4..0000000 --- a/Sources/phbar/Models/Block.swift +++ /dev/null @@ -1,107 +0,0 @@ -import Foundation - -/// A named, self-updating unit of the bar. -/// -/// Each block runs a shell command on an optional timer, -/// splits its stdout into ``Cell`` values, and publishes -/// them for rendering. Blocks are identified by ``name`` -/// so they can be refreshed on demand via Darwin -/// notifications (see ``Notifier``). -@MainActor -final class Block: ObservableObject, Identifiable { - /// Stable identity for SwiftUI diffing. - let id = UUID() - - /// Identifier used for manual updates via Darwin - /// notifications. - /// - /// Multiple blocks can share the same name. - /// In such case, a single notification will refresh - /// all of them. - let name: String - - /// Whether the block is anchored to the left or right - /// side of the bar. - let placement: Config.Block.Placement - - /// The shell command to execute for producing the cells. - /// - /// Can be an inline expression or a path to an executable - /// script. Inherits the environment of the phbar process. - let command: String - - /// Refresh interval in seconds, or `nil` for a one-shot block. - /// - /// A one-shot block can still be manually refreshed via - /// a Darwin notification (see ``Notifier``). - let interval: Double? - - /// The current cells produced by the last command execution. - /// - /// SwiftUI observes this `@Published` property and re-renders - /// the view tree automatically. - @Published var cells: [Cell] = [] - - private var tick: Task<Void, Never>? - - init(name: String, placement: Config.Block.Placement, command: String, interval: Double? = nil) { - self.name = name - self.placement = placement - self.command = command - self.interval = interval - } - - /// Initial paint + interval timer. - func start() { - runScript() - startTick() - } - - /// Re-run the command and update the cells on the main actor. - func runScript(env: [String: String] = [:]) { - let command = self.command - - var env = env - env.updateValue(name, forKey: "BLOCK") - - Task.detached(priority: .utility) { [weak self] in - let lines = Runner.run(command, env: env) - await self?.apply(lines) - } - } - - /// Stop the interval timer. - func stop() { - tick?.cancel() - } - - /// Replace the current cells with freshly parsed lines. - /// - /// Each line becomes a ``Cell``. This is always called - /// on the main actor so SwiftUI sees the update immediately. - @MainActor - private func apply(_ lines: [String]) { - self.cells = lines.map { Cell(rawValue: $0) } - } - - /// Start the interval timer that re-runs the command periodically. - /// - /// Does nothing when ``interval`` is `nil` or ≤ 0. - private func startTick() { - guard let interval, interval > 0 else { return } - tick = Task { [weak self] in - while !Task.isCancelled { - try? await Task.sleep(for: .seconds(interval)) - if Task.isCancelled { break } - self?.runScript() - } - } - } -} - -extension Config.Block { - @MainActor - var resolved: phbar.Block { - .init(name: name, placement: placement, command: command, interval: interval) - } -} diff --git a/Sources/phbar/Models/Cell.swift b/Sources/phbar/Models/Cell.swift deleted file mode 100644 index 0ad8e51..0000000 --- a/Sources/phbar/Models/Cell.swift +++ /dev/null @@ -1,70 +0,0 @@ -import Foundation -import RegexBuilder -import SwiftUI - -/// A thin wrapper around a single text line captured -/// by a block. -/// -/// When a block executes the command it captures stdout -/// and splits each line into a cell. Each cell is then -/// laid out horizontally respecting the order. -/// -/// Besides the underlying text value, a cell holds an -/// highlight level (as Int) that, if true, applies the -/// special background color defined in `Config.swift`. -/// You can set the highlight at runtime by prefixing the -/// text line with the special notation `&!<level>`, where -/// `<level>` is a positive integer that can be used as an -/// index to pick an highlight (count starts from 1). -/// -/// E.g: `echo "&!1 $(date "+%a %d, %H:%M:%S") "` -/// -/// Highlight colors can have multiple use cases, but they -/// were imagined specifically for distinguishing cells -/// within a group. For example when you want to show -/// window manager workspaces and which one is active. -struct Cell: RawRepresentable, Identifiable { - /// Stable identity for SwiftUI diffing. - let id = UUID() - - /// The represented text content. - let rawValue: String - - /// The highlight level of the cell. - let highlight: Int - - /// The background color derived from the highlight level. - /// - /// Returns the `Config.highlights` color at the same index - /// if present, otherwise `.clear` so the underlying bar - /// background shows through. - var background: Color { - guard highlight != 0 && highlight <= Config.highlights.count else { return .clear } - - return Config.highlights[highlight - 1] - } - - init(rawValue: String) { - let pattern = Regex { - Optionally { - "&!" - Capture { OneOrMore(.digit) } - } - Capture { OneOrMore(.any) } - } - - guard let match = rawValue.firstMatch(of: pattern) else { - self.rawValue = rawValue - self.highlight = 0 - return - } - - if let rawHighlight = match.output.1, let highlight = Int(String(rawHighlight)) { - self.highlight = highlight - } else { - self.highlight = 0 - } - - self.rawValue = String(match.output.2) - } -} diff --git a/Sources/phbar/Models/HEX.swift b/Sources/phbar/Models/HEX.swift deleted file mode 100644 index a262630..0000000 --- a/Sources/phbar/Models/HEX.swift +++ /dev/null @@ -1,114 +0,0 @@ -import SwiftUI - -/// A color described by a hexadecimal color code. -/// -/// `HEX` is a lightweight, string-backed representation: the code is stored -/// verbatim and parsed only when converted to a SwiftUI color via `Color(hex:)` -/// or `Color.hex(_:)`, which resolve it in the sRGB color space. -/// -/// Supported formats (the leading `#` is optional): -/// - `#RGB` -/// - `#RGBA` -/// - `#RRGGBB` -/// - `#RRGGBBAA` -/// -/// Single-digit components are expanded by repeating the digit; for example, -/// `#3aF` is read as `#33AAFF`. When present, the trailing digits encode alpha -/// (opacity); otherwise opacity defaults to `1`. -struct HEX { - /// The raw hex code, exactly as supplied. It is not validated or normalized; - /// parsing is performed lazily by `Color.hex(_:)`. - var code: String - - /// Creates a hex color from the given code. - /// - /// - Parameter code: A hex color code. Stored verbatim; see ``HEX`` for the - /// accepted formats. - init(code: String) { - self.code = code - } -} - -extension SwiftUI.Color { - /// Creates a color from a ``HEX`` value. - init(hex: HEX) { - self = .hex(hex.code) - } - - /// Creates a color from a hex code, resolving it in the sRGB color space. - /// - /// Supported formats (the leading `#` is optional): `#RGB`, `#RGBA`, - /// `#RRGGBB`, and `#RRGGBBAA`. Single-digit components are expanded by - /// repeating the digit. When present, the trailing digits encode alpha - /// (opacity); otherwise opacity defaults to `1`. Malformed input resolves - /// to opaque black. - /// - /// - Parameter code: A hex color code. - static func hex(_ code: String) -> SwiftUI.Color { - var sanitized = code.trimmingCharacters(in: .whitespacesAndNewlines) - if sanitized.hasPrefix("#") { - sanitized.removeFirst() - } - - var rgba: UInt64 = 0 - Scanner(string: sanitized).scanHexInt64(&rgba) - - let red: Double - let green: Double - let blue: Double - let opacity: Double - - switch sanitized.count { - case 8: // RRGGBBAA - red = Double((rgba >> 24) & 0xFF) / 255.0 - green = Double((rgba >> 16) & 0xFF) / 255.0 - blue = Double((rgba >> 8) & 0xFF) / 255.0 - opacity = Double(rgba & 0xFF) / 255.0 - case 6: // RRGGBB - red = Double((rgba >> 16) & 0xFF) / 255.0 - green = Double((rgba >> 8) & 0xFF) / 255.0 - blue = Double(rgba & 0xFF) / 255.0 - opacity = 1.0 - case 4: // RGBA - red = Double(((rgba >> 12) & 0xF) * 0x11) / 255.0 - green = Double(((rgba >> 8) & 0xF) * 0x11) / 255.0 - blue = Double(((rgba >> 4) & 0xF) * 0x11) / 255.0 - opacity = Double((rgba & 0xF) * 0x11) / 255.0 - case 3: // RGB - red = Double(((rgba >> 8) & 0xF) * 0x11) / 255.0 - green = Double(((rgba >> 4) & 0xF) * 0x11) / 255.0 - blue = Double((rgba & 0xF) * 0x11) / 255.0 - opacity = 1.0 - default: - red = 0.0 - green = 0.0 - blue = 0.0 - opacity = 1.0 - } - - return Color(.sRGB, red: red, green: green, blue: blue, opacity: opacity) - } -} - -extension SwiftUI.Color.Resolved { - /// The hex code for this resolved color. - /// - /// Components are clamped to `0...1` and quantized to 8 bits. - /// The result is a 6-digit `#RRGGBB` code, or an 8-digit `#RRGGBBAA` code - /// when the color is not fully opaque. - var hex: HEX { - let r = Int((min(max(Double(red), 0), 1) * 255).rounded()) - let g = Int((min(max(Double(green), 0), 1) * 255).rounded()) - let b = Int((min(max(Double(blue), 0), 1) * 255).rounded()) - - let code: String - if opacity < 1 { - let a = Int((min(max(Double(opacity), 0), 1) * 255).rounded()) - code = String(format: "#%02X%02X%02X%02X", r, g, b, a) - } else { - code = String(format: "#%02X%02X%02X", r, g, b) - } - - return HEX(code: code) - } -} diff --git a/Sources/phbar/Models/Notifier.swift b/Sources/phbar/Models/Notifier.swift deleted file mode 100644 index a428c49..0000000 --- a/Sources/phbar/Models/Notifier.swift +++ /dev/null @@ -1,90 +0,0 @@ -import Foundation - -/// Retains the handler for the lifetime of the registration. -/// -/// The raw pointer returned by ``Notifier/register(_:handler:)`` -/// is a retained reference to this box; it is released when -/// the registration is cancelled. -private final class NotifyBox { - let handler: @Sendable () -> Void - init(_ handler: @escaping @Sendable () -> Void) { self.handler = handler } -} - -/// Delivers refresh triggers via Darwin notifications. -/// -/// Wraps CoreFoundation notifications — the macOS-native, -/// signal-like way to trigger behaviour from the shell with -/// no client binary, using `notifyutil`: -/// -/// ``` -/// notifyutil -p <prefix><name> -/// ``` -/// -/// Each category of events uses a different ``prefix`` -/// to avoid collisions. For example, block refresh -/// notifications use ``touchPrefix``: -/// -/// ``` -/// notifyutil -p house.panini.phbar.touch.<name> -/// ``` -/// -/// > Note: `notify.h` isn't part of Swift's Darwin module, -/// > so we go through `CFNotificationCenter`, which talks -/// > to the same subsystem. -/// > -/// > The callback fires on an arbitrary thread; handlers -/// > must hop to the main actor themselves (see ``AppDelegate``). -enum Notifier { - /// Prefix for block refresh notifications. - /// - /// Use `notifyutil -p house.panini.phbar.touch.<name>` to trigger - /// a block update from the shell. - static let touchPrefix = "house.panini.phbar.touch." - - /// Register `handler` to fire whenever the notification is posted. - /// - /// - Parameters: - /// - name: The notification name suffix. - /// - prefix: The full notification name will be `prefix + name`. - /// Defaults to ``touchPrefix``. - /// - handler: Closure to invoke when the notification fires. - /// - /// The returned token is a retained pointer used to keep the handler - /// alive and to cancel the registration later. The handler runs on an - /// arbitrary thread, so hop to the main actor inside it if you touch - /// UI or published state. - @discardableResult - static func register( - _ name: String, - prefix: String = Notifier.touchPrefix, - handler: @escaping @Sendable () -> Void - ) -> UnsafeMutableRawPointer { - let fullName = prefix + name - let ctx = Unmanaged.passRetained(NotifyBox(handler)).toOpaque() - - CFNotificationCenterAddObserver( - CFNotificationCenterGetDarwinNotifyCenter(), - ctx, - { _, observer, _, _, _ in - guard let observer else { return } - let box = Unmanaged<NotifyBox>.fromOpaque(observer).takeUnretainedValue() - box.handler() - }, - fullName as CFString, - nil, - .deliverImmediately - ) - - return ctx - } - - /// Remove and release a registration. - /// - /// Pass the token returned by ``Notifier/register(_:handler:)``. - /// Optional: process exit clears all pending registrations anyway. - static func cancel(_ token: UnsafeMutableRawPointer) { - let center = CFNotificationCenterGetDarwinNotifyCenter() - CFNotificationCenterRemoveEveryObserver(center, token) - Unmanaged<NotifyBox>.fromOpaque(token).release() - } -} diff --git a/Sources/phbar/Models/Runner.swift b/Sources/phbar/Models/Runner.swift deleted file mode 100644 index be4dbff..0000000 --- a/Sources/phbar/Models/Runner.swift +++ /dev/null @@ -1,72 +0,0 @@ -import Foundation - -/// Runs a shell command and returns its stdout as separate lines. -/// -/// Blocking — always call from a background context (e.g. `Task.detached`). -enum Runner { - /// Run a command and returns stdout as an array of separated lines. - /// - Parameters: - /// - command: The command to run. - /// - env: An optional dictionary of strings to merge into - /// the process environment. - /// - /// - Returns: An array of the text lines printed to stdout. - static func run(_ command: String, env: [String: String] = [:]) -> [String] { - let process = Process() - process.executableURL = URL(fileURLWithPath: "/bin/sh") - process.arguments = ["-c", command] - - var environment = ProcessInfo.processInfo.environment - environment.merge(env) { _, new in new } - process.environment = environment - - let stdout = Pipe() - let stderr = Pipe() - process.standardOutput = stdout - process.standardError = stderr - - guard (try? process.run()) != nil else { - return ["ERROR"] - } - - let data = stdout.fileHandleForReading.readDataToEndOfFile() - let errData = stderr.fileHandleForReading.readDataToEndOfFile() - process.waitUntilExit() - - guard process.terminationStatus == 0 else { - write(errData, to: .standardError) - return ["ERROR"] - } - - guard let string = String(data: data, encoding: .utf8) else { - return [""] - } - - return parse(string) - } - - /// Split output into lines. A single trailing newline - /// (from a final `\n`) is dropped; blank lines elsewhere - /// are kept so a script can emit intentional gaps. - static func parse(_ text: String) -> [String] { - var lines = text.components(separatedBy: "\n") - if lines.last == "" { lines.removeLast() } - return lines - } - - /// Write data to the provided handle. - /// - Parameters: - /// - data: The data to write. - /// - handle: The handle to which data should be written. - static func write(_ data: Data, to handle: FileHandle) { - handle.write(data) - } - - /// Write string to the provided handle. - /// - Parameters: - /// - string: The string to write. - /// - handle: The handle to which data should be written. - static func write(_ string: String, to handle: FileHandle) { - write(Data(string.utf8), to: handle) - } -} diff --git a/Sources/phbar/Models/Screen.swift b/Sources/phbar/Models/Screen.swift deleted file mode 100644 index 870d3b6..0000000 --- a/Sources/phbar/Models/Screen.swift +++ /dev/null @@ -1,38 +0,0 @@ -import AppKit - -/// Helpers for multi-screen geometry. -/// -/// Maps a bar configuration (position, height) into concrete -/// screen coordinates so the window can be placed correctly -/// on each display. -enum Screen { - /// Frame of the bar within a screen, in global display coordinates. - /// - /// - Parameters: - /// - screen: The screen to calculate the bar frame for. - /// - position: Whether the bar sits at the top or bottom - /// of the screen. - /// - height: The bar height in points (see ``Config/height``). - /// - /// - Returns: An ``NSRect`` in screen coordinates. The bar spans - /// the full width of the screen and is anchored at the top - /// or bottom edge. - static func barFrame(_ screen: NSScreen, position: Config.Position, height: CGFloat) -> NSRect { - switch position { - case .top: - return NSRect( - x: screen.frame.minX, - y: screen.frame.maxY - height, - width: screen.frame.width, - height: height - ) - case .bottom: - return NSRect( - x: screen.frame.minX, - y: screen.frame.minY, - width: screen.frame.width, - height: height - ) - } - } -} diff --git a/Sources/phbar/Views/BarView.swift b/Sources/phbar/Views/BarView.swift deleted file mode 100644 index 7ad7c4c..0000000 --- a/Sources/phbar/Views/BarView.swift +++ /dev/null @@ -1,29 +0,0 @@ -import SwiftUI - -struct BarView: View { - private let left: [Block] - private let right: [Block] - - init(blocks: [Block]) { - self.left = blocks.filter { $0.placement == .left } - self.right = blocks.filter { $0.placement == .right } - } - - var body: some View { - HStack(spacing: 0) { - ForEach(left) { block in - BlockView(block: block) - } - - Spacer() - - ForEach(right) { block in - BlockView(block: block) - } - } - .font(Config.font) - .foregroundStyle(Config.foreground) - .frame(maxWidth: .infinity, maxHeight: Config.height) - .background(Config.background) - } -} diff --git a/Sources/phbar/Views/BarWindow.swift b/Sources/phbar/Views/BarWindow.swift deleted file mode 100644 index ce65432..0000000 --- a/Sources/phbar/Views/BarWindow.swift +++ /dev/null @@ -1,32 +0,0 @@ -import AppKit - -final class BarWindow: NSPanel { - init(contentRect: NSRect) { - super.init( - contentRect: contentRect, - styleMask: [.nonactivatingPanel, .borderless], - backing: .buffered, - defer: false - ) - - animationBehavior = .none - backgroundColor = .clear - becomesKeyOnlyIfNeeded = true - collectionBehavior = [ - .canJoinAllSpaces, - .fullScreenAuxiliary, - .fullScreenNone, - .stationary, - ] - hasShadow = false - hidesOnDeactivate = false - isFloatingPanel = true - isMovable = false - isOpaque = false - isReleasedWhenClosed = false - level = .floating - } - - override var canBecomeMain: Bool { false } - override var canBecomeKey: Bool { true } -} diff --git a/Sources/phbar/Views/BlockView.swift b/Sources/phbar/Views/BlockView.swift deleted file mode 100644 index 85d6e6e..0000000 --- a/Sources/phbar/Views/BlockView.swift +++ /dev/null @@ -1,60 +0,0 @@ -import SwiftUI - -struct BlockView: View { - @ObservedObject var block: Block - - @State private var hovering = false - - var body: some View { - HStack(spacing: 0) { - ForEach(block.cells) { cell in - CellView(cell: cell) - } - } - .onHover { hovering = $0 } - .onAppear { - NSEvent.addLocalMonitorForEvents(matching: [ - .leftMouseDown, - .rightMouseDown, - .otherMouseDown, - .scrollWheel, - ]) { event in - handle(event: event) - } - } - } - - private func handle(event: NSEvent) -> NSEvent { - guard hovering else { return event } - - switch event.type { - case .leftMouseDown, .rightMouseDown, .otherMouseDown: - block.runScript(env: ["BUTTON": String(event.buttonNumber)]) - break - case .scrollWheel: - if event.scrollingDeltaY > 0 { - block.runScript(env: ["SCROLL": "UP"]) - break - } - - if event.scrollingDeltaY < 0 { - block.runScript(env: ["SCROLL": "DOWN"]) - break - } - - if event.scrollingDeltaX > 0 { - block.runScript(env: ["SCROLL": "RIGHT"]) - break - } - - if event.scrollingDeltaX < 0 { - block.runScript(env: ["SCROLL": "LEFT"]) - break - } - default: - break - } - - return event - } -} diff --git a/Sources/phbar/Views/CellView.swift b/Sources/phbar/Views/CellView.swift deleted file mode 100644 index 6b39eb1..0000000 --- a/Sources/phbar/Views/CellView.swift +++ /dev/null @@ -1,11 +0,0 @@ -import SwiftUI - -struct CellView: View { - var cell: Cell - - var body: some View { - Text(cell.rawValue) - .frame(height: Config.height) - .background(cell.background) - } -} diff --git a/Sources/phbar/main.swift b/Sources/phbar/main.swift deleted file mode 100644 index 6322d69..0000000 --- a/Sources/phbar/main.swift +++ /dev/null @@ -1,8 +0,0 @@ -import AppKit - -// Entry point. Top-level code runs on the main actor. -let app = NSApplication.shared -let delegate = AppDelegate() -app.delegate = delegate -app.setActivationPolicy(.accessory) // no Dock icon, no menu -app.run() |
