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 | |
| parent | 62ce97dcb63d61c8db739c1b32bd387042ca66a0 (diff) | |
| download | bar-d59b22e557fa5d69ea6be725a7e5bcd829588225.tar.gz bar-d59b22e557fa5d69ea6be725a7e5bcd829588225.zip | |
rename project and add documentation guides
Diffstat (limited to 'Sources')
| -rw-r--r-- | Sources/bar/Config.swift (renamed from Sources/phbar/Config.swift) | 21 | ||||
| -rw-r--r-- | Sources/bar/Models/AppDelegate.swift (renamed from Sources/phbar/Models/AppDelegate.swift) | 0 | ||||
| -rw-r--r-- | Sources/bar/Models/Block.swift (renamed from Sources/phbar/Models/Block.swift) | 4 | ||||
| -rw-r--r-- | Sources/bar/Models/Cell.swift (renamed from Sources/phbar/Models/Cell.swift) | 17 | ||||
| -rw-r--r-- | Sources/bar/Models/HEX.swift (renamed from Sources/phbar/Models/HEX.swift) | 0 | ||||
| -rw-r--r-- | Sources/bar/Models/Notifier.swift (renamed from Sources/phbar/Models/Notifier.swift) | 10 | ||||
| -rw-r--r-- | Sources/bar/Models/Runner.swift (renamed from Sources/phbar/Models/Runner.swift) | 0 | ||||
| -rw-r--r-- | Sources/bar/Models/Screen.swift (renamed from Sources/phbar/Models/Screen.swift) | 8 | ||||
| -rw-r--r-- | Sources/bar/Views/BarView.swift (renamed from Sources/phbar/Views/BarView.swift) | 0 | ||||
| -rw-r--r-- | Sources/bar/Views/BarWindow.swift (renamed from Sources/phbar/Views/BarWindow.swift) | 0 | ||||
| -rw-r--r-- | Sources/bar/Views/BlockView.swift (renamed from Sources/phbar/Views/BlockView.swift) | 0 | ||||
| -rw-r--r-- | Sources/bar/Views/CellView.swift (renamed from Sources/phbar/Views/CellView.swift) | 0 | ||||
| -rw-r--r-- | Sources/bar/bar.docc/bar.md | 44 | ||||
| -rw-r--r-- | Sources/bar/bar.docc/commands.md | 50 | ||||
| -rw-r--r-- | Sources/bar/bar.docc/customisation.md | 45 | ||||
| -rw-r--r-- | Sources/bar/bar.docc/installation.md | 95 | ||||
| -rw-r--r-- | Sources/bar/bar.docc/manual-updates.md | 28 | ||||
| -rw-r--r-- | Sources/bar/bar.docc/philosophy.md | 34 | ||||
| -rw-r--r-- | Sources/bar/main.swift (renamed from Sources/phbar/main.swift) | 4 |
19 files changed, 328 insertions, 32 deletions
diff --git a/Sources/phbar/Config.swift b/Sources/bar/Config.swift index e948b30..2d9859b 100644 --- a/Sources/phbar/Config.swift +++ b/Sources/bar/Config.swift @@ -3,13 +3,12 @@ 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. +/// This is the primary file a user edits to customise *bar*. 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 + /// > Tip: Set this value to 30 to match the default macOS menu bar height. + static let height: Double = 30 /// The bar position on the screen. static let position: Position = .top @@ -52,9 +51,9 @@ enum Config { /// 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: "version", placement: .left, command: "echo '&!1 bar 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: "memory", placement: .right, command: "echo \" Hi $USER, welcome to bar! | \""), Block(name: "clock", placement: .right, command: "echo \" $(date '+%a %d, %H:%M:%S') \"", interval: 1), ] } @@ -74,7 +73,7 @@ extension Config { /// 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 + /// produces output on an optional timer. See ``bar/Block`` for the /// runtime counterpart that manages execution and cell publishing. struct Block: Sendable { /// The block identifier used for manual updates. @@ -83,10 +82,10 @@ extension Config { /// 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 + /// notification that tells *bar* to trigger an update /// for the block identified by the name. /// - /// E.g: `notifyutil -p house.panini.phbar.touch.<name>` + /// E.g: `notifyutil -p bar.touch.<name>` /// /// Be aware that nothing stops you from having multiple /// blocks with the same name. In such case, triggering @@ -104,8 +103,8 @@ extension Config { /// 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 + /// of the process that executed `bar` in the first place. + /// This means that running `bar` from your regular shell /// or from launchctl might result in different behaviours. /// Make sure to test both scenarios properly. let command: String diff --git a/Sources/phbar/Models/AppDelegate.swift b/Sources/bar/Models/AppDelegate.swift index a7ecb5a..a7ecb5a 100644 --- a/Sources/phbar/Models/AppDelegate.swift +++ b/Sources/bar/Models/AppDelegate.swift diff --git a/Sources/phbar/Models/Block.swift b/Sources/bar/Models/Block.swift index fe575e4..cb09a4b 100644 --- a/Sources/phbar/Models/Block.swift +++ b/Sources/bar/Models/Block.swift @@ -27,7 +27,7 @@ final class Block: ObservableObject, Identifiable { /// 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. + /// script. Inherits the environment of the `bar` process. let command: String /// Refresh interval in seconds, or `nil` for a one-shot block. @@ -101,7 +101,7 @@ final class Block: ObservableObject, Identifiable { extension Config.Block { @MainActor - var resolved: phbar.Block { + var resolved: bar.Block { .init(name: name, placement: placement, command: command, interval: interval) } } diff --git a/Sources/phbar/Models/Cell.swift b/Sources/bar/Models/Cell.swift index 0ad8e51..9e0d46f 100644 --- a/Sources/phbar/Models/Cell.swift +++ b/Sources/bar/Models/Cell.swift @@ -5,19 +5,24 @@ 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. +/// 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. /// +/// ### Highlight /// 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`. +/// special background color defined in ``Config``. /// 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). +/// index to pick an highlight. /// -/// E.g: `echo "&!1 $(date "+%a %d, %H:%M:%S") "` +/// ```shell +/// echo "&!1 $(date "+%a %d, %H:%M:%S") " +/// ``` +/// +/// > Note: Count starts from 1. /// /// Highlight colors can have multiple use cases, but they /// were imagined specifically for distinguishing cells diff --git a/Sources/phbar/Models/HEX.swift b/Sources/bar/Models/HEX.swift index a262630..a262630 100644 --- a/Sources/phbar/Models/HEX.swift +++ b/Sources/bar/Models/HEX.swift diff --git a/Sources/phbar/Models/Notifier.swift b/Sources/bar/Models/Notifier.swift index a428c49..cdfe19e 100644 --- a/Sources/phbar/Models/Notifier.swift +++ b/Sources/bar/Models/Notifier.swift @@ -20,12 +20,12 @@ private final class NotifyBox { /// notifyutil -p <prefix><name> /// ``` /// -/// Each category of events uses a different ``prefix`` +/// 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> +/// notifyutil -p bar.touch.<name> /// ``` /// /// > Note: `notify.h` isn't part of Swift's Darwin module, @@ -37,9 +37,9 @@ private final class NotifyBox { enum Notifier { /// Prefix for block refresh notifications. /// - /// Use `notifyutil -p house.panini.phbar.touch.<name>` to trigger + /// Use `notifyutil -p bar.touch.<name>` to trigger /// a block update from the shell. - static let touchPrefix = "house.panini.phbar.touch." + static let touchPrefix = "bar.touch." /// Register `handler` to fire whenever the notification is posted. /// @@ -80,7 +80,7 @@ enum Notifier { /// Remove and release a registration. /// - /// Pass the token returned by ``Notifier/register(_:handler:)``. + /// Pass the token returned by ``Notifier/register(_:prefix:handler:)``. /// Optional: process exit clears all pending registrations anyway. static func cancel(_ token: UnsafeMutableRawPointer) { let center = CFNotificationCenterGetDarwinNotifyCenter() diff --git a/Sources/phbar/Models/Runner.swift b/Sources/bar/Models/Runner.swift index be4dbff..be4dbff 100644 --- a/Sources/phbar/Models/Runner.swift +++ b/Sources/bar/Models/Runner.swift diff --git a/Sources/phbar/Models/Screen.swift b/Sources/bar/Models/Screen.swift index 870d3b6..8029db4 100644 --- a/Sources/phbar/Models/Screen.swift +++ b/Sources/bar/Models/Screen.swift @@ -1,10 +1,6 @@ import AppKit +import Foundation -/// 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. /// @@ -14,7 +10,7 @@ enum Screen { /// of the screen. /// - height: The bar height in points (see ``Config/height``). /// - /// - Returns: An ``NSRect`` in screen coordinates. The bar spans + /// - 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 { diff --git a/Sources/phbar/Views/BarView.swift b/Sources/bar/Views/BarView.swift index 7ad7c4c..7ad7c4c 100644 --- a/Sources/phbar/Views/BarView.swift +++ b/Sources/bar/Views/BarView.swift diff --git a/Sources/phbar/Views/BarWindow.swift b/Sources/bar/Views/BarWindow.swift index ce65432..ce65432 100644 --- a/Sources/phbar/Views/BarWindow.swift +++ b/Sources/bar/Views/BarWindow.swift diff --git a/Sources/phbar/Views/BlockView.swift b/Sources/bar/Views/BlockView.swift index 85d6e6e..85d6e6e 100644 --- a/Sources/phbar/Views/BlockView.swift +++ b/Sources/bar/Views/BlockView.swift diff --git a/Sources/phbar/Views/CellView.swift b/Sources/bar/Views/CellView.swift index 6b39eb1..6b39eb1 100644 --- a/Sources/phbar/Views/CellView.swift +++ b/Sources/bar/Views/CellView.swift diff --git a/Sources/bar/bar.docc/bar.md b/Sources/bar/bar.docc/bar.md new file mode 100644 index 0000000..3d8b2ee --- /dev/null +++ b/Sources/bar/bar.docc/bar.md @@ -0,0 +1,44 @@ +# ``bar`` + +Modular status bar for macOS written in Swift. + +## Overview + +*bar* draws a status bar on each screen connected at runtime. +The bar can be configured to have a specific ``Config-swift.enum/height``, ``Config-swift.enum/position``, and ``Config-swift.enum/background`` color. +You can also customize the ``Config-swift.enum/font`` and ``Config-swift.enum/foreground`` color used by all the texts. + +Inside the bar, you can display one or more ``Block``, each defined by a name, placement, shell command, and optional refresh interval. +At runtime each block runs the command, reads from stdout, and builds one or more ``Cell`` from the output. +If you specify the interval, the block will re-run the command with such frequency. + +## Topics + +### Getting started + +- <doc:installation> +- <doc:customisation> +- ``Config`` +- ``HEX`` + +### Blocks + +- <doc:commands> +- <doc:manual-updates> +- ``Block`` +- ``Cell`` +- ``Runner`` +- ``Notifier`` + +### Design + +- <doc:philosophy> + +### Views + +- ``AppDelegate`` +- ``Screen`` +- ``BarWindow`` +- ``BarView`` +- ``BlockView`` +- ``CellView`` diff --git a/Sources/bar/bar.docc/commands.md b/Sources/bar/bar.docc/commands.md new file mode 100644 index 0000000..4c00bfe --- /dev/null +++ b/Sources/bar/bar.docc/commands.md @@ -0,0 +1,50 @@ +# Commands + +How a block runs its command and what you can do with it. + +Each block runs its ``Config-swift.enum/Block/command`` as a shell script via `/bin/sh -c`. +The standard output is captured and **each line becomes one ``Cell``** in the bar. +Cells are laid out horizontally in the order they appear. + +```shell +# A single-line command produces one cell +echo "Hello, world" + +# Multi-line output produces multiple cells +echo "first cell" +echo "second cell" +``` + +## Highlighting cells + +Prefix a line with `&!<level>` to apply a highlight background color. +The highlight level is a positive integer that indexes into ``Config-swift.enum/highlights`` (1-based). + +```shell +echo "&!1 workspace 1" # highlighted with Config.highlights[0] +echo "&!2 workspace 2" # highlighted with Config.highlights[1] +echo "workspace 3" # no highlight +``` + +Highlight colors were imagined specifically for distinguishing cells within a group. E.g: showing window manager workspaces and which one is active. + +## Environment variables + +Each command inherits the `bar` process environment, plus these extras: + +| Variable | What holds | When set | Example value | +|---|---|---|---| +| `BLOCK` | The name of the block | Always | `"clock"` | +| `BUTTON` | The mouse button number | On mouse click inside the block | `"0"` (left), `"1"` (right), `"2"` (middle) | +| `SCROLL` | The scroll direction | On scroll over the block | `"UP"`, `"DOWN"`, `"LEFT"`, `"RIGHT"` | + +This lets a single script adapt its behaviour based on which block called it or what interaction just happened: + +```shell +#!/bin/sh +if [ "$BLOCK" = "clock" ]; then + date '+%a %d, %H:%M:%S' +elif [ -n "$BUTTON" ]; then + echo "clicked button $BUTTON" +fi +``` diff --git a/Sources/bar/bar.docc/customisation.md b/Sources/bar/bar.docc/customisation.md new file mode 100644 index 0000000..cc692ce --- /dev/null +++ b/Sources/bar/bar.docc/customisation.md @@ -0,0 +1,45 @@ +# Customisation + +How to customise the program. + +The program can be customised by editing the static properties inside the ``Config`` enum. + +```swift +// Sources/bar/Config.swift + +enum Config { + static let height: Double = 30 + static let position: Position = .top + + static let font: Font = .custom("My Awesome Font", fixedSize: 14) + + static let foreground: Color = .hex("#FFFFFF") + static let background: Color = .hex("#000000").opacity(0.75) + static let highlights: [Color] = [.blue, .purple] + + static let blocks: [Block] = [ + Block(name: "workspaces", placement: .left, command: "~/scripts/workspaces"), + Block(name: "clock", placement: .right, command: "date '+%a %d, %H:%M:%S'", interval: 1), + ] +} +``` + +> Tip: While testing the configuration you might want to use the `make run` target. +> It will build and execute the program right away, without copying the binaries. + +## HEX Colors + +You can use any standard SwiftUI color. For convenience, we also provide the ``HEX`` type for defining colors by hex code directly in source: + +```swift +static let foreground: Color = .hex("#CDD6F4") +``` + +Supported formats (the leading `#` is optional): `#RGB`, `#RGBA`, `#RRGGBB`, `#RRGGBBAA`. + +## Spacing + +*bar* does not offer an option to customise the spacing between blocks. +We personally think that adding a space character at the end or beginning of a block is plenty enough and eliminates a bunch of possible edge cases. + +> Tip: If you don't like the default space of your font you can try one of the [alternatives from the Unicode specification](https://spaces.mau.fi/). diff --git a/Sources/bar/bar.docc/installation.md b/Sources/bar/bar.docc/installation.md new file mode 100644 index 0000000..a96f79f --- /dev/null +++ b/Sources/bar/bar.docc/installation.md @@ -0,0 +1,95 @@ +# Installation + +How to install and build the program. + +**Building the program from source is an integral part of the philosophy and design of the software**. +For this reason we do not ship pre-built binaries nor you'll be able to find it on Homebrew or any other package manager. + +## Requirements + +- **macOS 26** or later +- **Swift 6.3** (bundled with Xcode or installed via [swift.org](https://swift.org)) + +## Download + +Clone the official repository somewhere on you system: + +```shell +git clone https://github.com/paninihouse/bar.git +``` + +## Build + +From within the *bar* directory, run the install target: + +```shell +make install +``` + +> Tip: By default, the install target builds a release binary and copies it to `/usr/local/bin/bar`. +> You can override the install location by setting `PREFIX`: +> +> ```shell +> make install PREFIX=~/.local/bin +> ``` + +### Without make + +If you don't have `make`, build directly with the Swift Package Manager: + +```shell +swift build -c release +cp .build/release/bar /usr/local/bin/ +``` + +## Run at login + +It is fine to run `bar` manually while you're editing and customising the source code. +Although, as soon as you're happy with your build is a good idea to create a launch agent that can start the program for you. + +Copy the example down below in a `plist` file inside the `~/Library/LaunchAgents/` directory. + +> Tip: It is best practice to use [reverse domain name notation](https://en.wikipedia.org/wiki/Reverse_domain_name_notation) for the filename. + +Adjust the example as you see fit. +For the full file specification run: + +```shell +man launchd.plist +``` + +You can than register the launch agent like so: + +```shell +launchctl load ~/Library/LaunchAgents/house.panini.bar.plist +``` + +### Example launch agent + +```plist +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>Label</key> + <string>house.panini.bar</string> + + <key>ProgramArguments</key> + <array> + <string>/usr/local/bin/bar</string> + </array> + + <key>RunAtLoad</key> + <true/> + + <key>KeepAlive</key> + <true/> + + <key>StandardOutPath</key> + <string>/tmp/bar/bar.log</string> + + <key>StandardErrorPath</key> + <string>/tmp/bar/bar.err</string> +</dict> +</plist> +``` diff --git a/Sources/bar/bar.docc/manual-updates.md b/Sources/bar/bar.docc/manual-updates.md new file mode 100644 index 0000000..60520ca --- /dev/null +++ b/Sources/bar/bar.docc/manual-updates.md @@ -0,0 +1,28 @@ +# Manual refreshes + +How to trigger a block update on-demand. + +*bar* implements a very basic way to manually signal a block to refresh its content. +This is done by posting a [Darwin notification](https://developer.apple.com/documentation/notifications) from the shell. + +## Triggering a refresh + +Use `notifyutil` (bundled with macOS) to post a notification: + +```shell +notifyutil -p bar.touch.<name> +``` + +Replace `<name>` with the block's name (e.g. `clock`, `battery`). +If multiple blocks share the same name, a single notification refreshes them all. + +## Use cases + +Manual updates are useful for blocks that react to external events: + +- **Window managers** - refresh when the workspace or focused app changes. +- **Volume indicator** — refresh when the sound output changes. +- **Network monitor** — poll only when the interface state flips. +- **Now playing** — update when the current track changes. + +Combine with a launch agent or a shell hook that posts the notification whenever the relevant system state changes. diff --git a/Sources/bar/bar.docc/philosophy.md b/Sources/bar/bar.docc/philosophy.md new file mode 100644 index 0000000..6c9764b --- /dev/null +++ b/Sources/bar/bar.docc/philosophy.md @@ -0,0 +1,34 @@ +# Design philosophy + +*bar* design is heavily inspired by the suite of [suckless tools](https://suckless.org/) for Linux. +These tools are very different from what you typically have on macOS. +They do not have configuration files, extraneous features, or a plugin/extension system. +Instead, they are focused on simplicity, frugality, and a DIY approach. + +We deliberately designed *bar* to be extremely limited, specific, and easy to understand, like a suckless tool. +This is not software you install, run, and forget about. +You're supposed to fully understand how it works, make it your own, and ultimately give back your enhancements to the community. + +## How customization works + +*bar* does not read a configuration file somewhere on your system. +Instead, configuration happens directly in the source code by editing ``Config`` and rebuilding the software. +This approach allows us to keep the software extremely minimal and reliable, since all the possible errors are caught at build time. + +## No pre-built binaries + +Since the configuration requires you to modify the source code, it doesn't make sense to ship pre-built binaries of *bar*. +Therefore, you won't find it in Homebrew or in any other package manager. +The way to install *bar* is simply to download or clone the repository on your system and run <doc:installation>. + +## How to extend the functionality + +*bar* already does a lot out of the box, but it is deliberately more limited compared to other solutions like [SketchyBar](https://felixkratz.github.io/SketchyBar/). +What if you need a specific feature that is not available? +Well, you can build it or patch it yourself. + +*bar* aims to stay focused and minimal, so it can be read through and understood in roughly half an hour. +You're more than encouraged to apply patches to extend the software, write your own features, and ultimately publish them for the entire community. + +This approach is far more powerful than a plugin/extension ecosystem because you're not limited to what we, as the main developers, decide you can customize or change. +You own the source code, therefore you are free to do whatever you want. diff --git a/Sources/phbar/main.swift b/Sources/bar/main.swift index 6322d69..e965fa5 100644 --- a/Sources/phbar/main.swift +++ b/Sources/bar/main.swift @@ -1,8 +1,8 @@ import AppKit // Entry point. Top-level code runs on the main actor. -let app = NSApplication.shared -let delegate = AppDelegate() +private let app = NSApplication.shared +private let delegate = AppDelegate() app.delegate = delegate app.setActivationPolicy(.accessory) // no Dock icon, no menu app.run() |
