From 618799ee703cc4addc7e50550ccf87b4b4cc7780 Mon Sep 17 00:00:00 2001 From: Tommaso Negri Date: Fri, 7 Aug 2026 23:52:00 +0200 Subject: make Cell a proper entity --- Sources/phbar/Models/Block.swift | 48 +++++++++++++++++++++++++++++++++++----- Sources/phbar/Models/Cell.swift | 46 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+), 5 deletions(-) create mode 100644 Sources/phbar/Models/Cell.swift (limited to 'Sources/phbar/Models') diff --git a/Sources/phbar/Models/Block.swift b/Sources/phbar/Models/Block.swift index 7eca7c9..95c4ada 100644 --- a/Sources/phbar/Models/Block.swift +++ b/Sources/phbar/Models/Block.swift @@ -1,15 +1,46 @@ 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? - @Published var cells: [String] = [] + /// 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? @@ -30,8 +61,8 @@ final class Block: ObservableObject, Identifiable { func runScript() { let command = self.command Task.detached(priority: .utility) { [weak self] in - let cells = Runner.run(command) - await self?.apply(cells) + let lines = Runner.run(command) + await self?.apply(lines) } } @@ -40,11 +71,18 @@ final class Block: ObservableObject, Identifiable { 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(_ cells: [String]) { - self.cells = cells + 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 diff --git a/Sources/phbar/Models/Cell.swift b/Sources/phbar/Models/Cell.swift new file mode 100644 index 0000000..789f615 --- /dev/null +++ b/Sources/phbar/Models/Cell.swift @@ -0,0 +1,46 @@ +import Foundation +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 +/// highlighted status 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 `!!`. +/// +/// E.g: `echo "!! $(date "+%a %d, %H:%M:%S") "` +/// +/// Highlight can have multiple use cases, but it was +/// imagined specifically for distinguishing a single cell +/// within a group. For example when you want to show which +/// workspace is currently active. +struct Cell: RawRepresentable, Identifiable { + /// Stable identity for SwiftUI diffing. + let id = UUID() + + /// The represented text content. + let rawValue: String + + /// Whether the cell is highlighted or not. + let highlighted: Bool + + /// The background color derived from the highlighted state. + /// + /// Returns `Config.highlight` when the cell is highlighted, + /// otherwise `.clear` so the underlying bar background + /// shows through. + var background: Color { + highlighted ? Config.highlight : .clear + } + + init(rawValue: String) { + self.highlighted = rawValue.hasPrefix("!!") + self.rawValue = highlighted ? String(rawValue.dropFirst(2)) : rawValue + } +} -- cgit v1.2.3