aboutsummaryrefslogtreecommitdiff
path: root/Sources/phbar
diff options
context:
space:
mode:
authorTommaso Negri <me@tommasongr.com>2026-08-09 23:58:42 +0200
committerTommaso Negri <me@tommasongr.com>2026-08-09 23:58:44 +0200
commitd59b22e557fa5d69ea6be725a7e5bcd829588225 (patch)
tree618e20ef2b648e46eac3d51e14b8e0fcc1faba3c /Sources/phbar
parent62ce97dcb63d61c8db739c1b32bd387042ca66a0 (diff)
downloadbar-d59b22e557fa5d69ea6be725a7e5bcd829588225.tar.gz
bar-d59b22e557fa5d69ea6be725a7e5bcd829588225.zip
rename project and add documentation guides
Diffstat (limited to 'Sources/phbar')
-rw-r--r--Sources/phbar/Config.swift143
-rw-r--r--Sources/phbar/Models/AppDelegate.swift47
-rw-r--r--Sources/phbar/Models/Block.swift107
-rw-r--r--Sources/phbar/Models/Cell.swift70
-rw-r--r--Sources/phbar/Models/HEX.swift114
-rw-r--r--Sources/phbar/Models/Notifier.swift90
-rw-r--r--Sources/phbar/Models/Runner.swift72
-rw-r--r--Sources/phbar/Models/Screen.swift38
-rw-r--r--Sources/phbar/Views/BarView.swift29
-rw-r--r--Sources/phbar/Views/BarWindow.swift32
-rw-r--r--Sources/phbar/Views/BlockView.swift60
-rw-r--r--Sources/phbar/Views/CellView.swift11
-rw-r--r--Sources/phbar/main.swift8
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()