Compare commits

...

4 Commits

Author SHA1 Message Date
tommaso 3da4c8e466 make styles optional in theme 2026-07-12 11:36:27 +02:00
tommaso 70a170f544 move text definition to theme and make it dynamic 2026-07-12 11:34:52 +02:00
tommaso 7c0551c662 make colors and padding optional 2026-07-12 11:16:54 +02:00
tommaso 0edc4ca47a add Makefile 2026-07-12 11:14:49 +02:00
16 changed files with 148 additions and 51 deletions
+59
View File
@@ -0,0 +1,59 @@
# Makefile for phbar
#
# Usage: make <target>
BIN_DIR := $(HOME)/.local/bin
BUILD_DIR := .build/release
BINARIES := phbar
.PHONY: all build install clean test run
all: build
# Build all targets in release mode
build:
swift build -c release
# Build and install binaries to ~/.local/bin
install: build
@mkdir -p $(BIN_DIR)
@for bin in $(BINARIES); do \
cp -f $(BUILD_DIR)/$$bin $(BIN_DIR)/; \
done
@echo "✅ Installed $(BINARIES) to $(BIN_DIR)"
# Clean build artifacts
clean:
swift package clean
# Run tests
test:
swift test
# Quick test: build, install, and verify
run: install
@echo "Verifying installation..."
@for bin in $(BINARIES); do \
which $$bin > /dev/null && echo "$$bin: $(shell which $$bin)" || echo "$$bin: not found"; \
done
# Uninstall binaries from ~/.local/bin
uninstall:
@for bin in $(BINARIES); do \
rm -f $(BIN_DIR)/$$bin; \
done
@echo "✅ Uninstalled $(BINARIES) from $(BIN_DIR)"
# Rebuild and reinstall (clean + install)
reinstall: clean install
# Show help
help:
@echo "Available targets:"
@echo " make build - Build in release mode"
@echo " make install - Build and install to ~/.local/bin"
@echo " make clean - Clean build artifacts"
@echo " make test - Run tests"
@echo " make run - Build, install, and verify"
@echo " make uninstall - Remove binaries from ~/.local/bin"
@echo " make reinstall - Clean and reinstall"
+3
View File
@@ -6,6 +6,8 @@ final class PHBlock: ObservableObject, Decodable, Identifiable {
let command: String
let name: String?
let textName: String?
@Published var text: PHThemeText = .default
let styleName: String?
@Published var style: PHThemeStyle = .default
let refresh: Double?
@@ -38,6 +40,7 @@ final class PHBlock: ObservableObject, Decodable, Identifiable {
private enum CodingKeys: String, CodingKey {
case command, name
case textName = "text"
case styleName = "style"
case refresh, events, centered
}
+1 -1
View File
@@ -1,3 +1,3 @@
struct PHConfig: Decodable {
let text: PHConfigText
let env: [String: String]?
}
@@ -1,9 +0,0 @@
enum PHConfigTextStyle: String, Decodable {
case normal, italic
}
extension PHConfigText {
var italic: Bool {
style == .italic
}
}
+3 -6
View File
@@ -1,6 +1,3 @@
[text]
font = "Comic Code"
size = 14
weight = "regular"
style = "normal"
offset = -0.5
height = 30
[window]
+3 -1
View File
@@ -1,9 +1,11 @@
struct PHTheme: Decodable {
let window: PHThemeWindow
let styles: [PHThemeStyle]
let texts: [PHThemeText]?
let styles: [PHThemeStyle]?
private enum CodingKeys: String, CodingKey {
case window
case texts = "text"
case styles = "style"
}
}
@@ -1,10 +1,9 @@
struct PHThemePadding: Decodable {
let leading: Double
let trailing: Double
let trailing: Double?
let leading: Double?
var total: Double { leading + trailing }
static var zero: PHThemePadding {
Self.init(leading: 0, trailing: 0)
private enum CodingKeys: String, CodingKey {
case trailing = "right"
case leading = "left"
}
}
@@ -1,15 +1,15 @@
struct PHThemeStyle: Decodable {
let name: String
let foreground: PHThemeColor
let background: PHThemeColor
let padding: PHThemePadding
let foreground: PHThemeColor?
let background: PHThemeColor?
let padding: PHThemePadding?
static let `default`: Self = {
PHThemeStyle(
name: "_default",
foreground: .init(color: "#000000", alpha: nil),
background: .init(color: "#ffffff", alpha: nil),
padding: .init(leading: 10, trailing: 10)
padding: .init(trailing: 12, leading: 12)
)
}()
}
@@ -1,22 +1,35 @@
import AppKit
import SwiftUI
struct PHConfigText: Decodable {
struct PHThemeText: Decodable {
let name: String
let fontFamily: String
let size: Double
let weight: PHConfigTextWeight
let style: PHConfigTextStyle
let weight: PHThemeTextWeight
let style: PHThemeTextStyle
let offset: Double?
private enum CodingKeys: String, CodingKey {
case name
case fontFamily = "font"
case size, weight, style, offset
}
static let `default`: Self = {
PHThemeText(
name: "_default",
fontFamily: "monospace",
size: 14.0,
weight: .regular,
style: .normal,
offset: nil
)
}()
}
// Design
extension PHConfigText {
extension PHThemeText {
var design: SwiftUI.Font.Design? {
switch fontFamily {
case "sans":
@@ -46,7 +59,7 @@ extension PHConfigText {
// Font
extension PHConfigText {
extension PHThemeText {
/// Construct an `NSFont` from the typeface settings.
///
/// Falls back to the system font if parsing fails.
@@ -0,0 +1,9 @@
enum PHThemeTextStyle: String, Decodable {
case normal, italic
}
extension PHThemeText {
var italic: Bool {
style == .italic
}
}
@@ -1,7 +1,7 @@
import AppKit
import SwiftUI
enum PHConfigTextWeight: String, Decodable {
enum PHThemeTextWeight: String, Decodable {
case thin
case ultraLight = "ultralight"
case light
+17 -4
View File
@@ -6,20 +6,33 @@
shadow = false
blur = 0.0
[[text]]
name = "default"
font = "Comic Code"
size = 14
weight = "regular"
style = "normal"
offset = -0.5
[[text]]
name = "italic"
font = "Comic Code"
size = 14
weight = "regular"
style = "italic"
offset = -0.5
[[style]]
name = "default"
foreground = { color = "#aed3f3" }
background = { color = "#010408", alpha = 0.825 }
padding = { leading = 0.0, trailing = 0.0 }
[[style]]
name = "floating"
foreground = { color = "#aed3f3" }
background = { color = "#000000", alpha = 0 }
padding = { leading = 0.0, trailing = 0.0 }
[[style]]
name = "tinted"
foreground = { color = "#aed3f3" }
background = { color = "#0f304a" }
padding = { leading = 12.0, trailing = 12.0 }
padding = { left = 12.0, right = 12.0 }
+14 -3
View File
@@ -29,17 +29,28 @@ final class BarController: ObservableObject {
self.blocks = blocks
for block in blocks {
block.text = text(for: block)
block.style = style(for: block)
}
}
}
// Style
// Theming
extension BarController {
func text(for block: PHBlock) -> PHThemeText {
guard let text = theme.texts?.first(where: { $0.name == block.textName }) else {
guard let defaultText = theme.texts?.first(where: { $0.name == "default" }) else {
return PHThemeText.default
}
return defaultText
}
return text
}
func style(for block: PHBlock) -> PHThemeStyle {
guard let style = theme.styles.first(where: { $0.name == block.styleName }) else {
guard let defaultStyle = theme.styles.first(where: { $0.name == "default" }) else {
guard let style = theme.styles?.first(where: { $0.name == block.styleName }) else {
guard let defaultStyle = theme.styles?.first(where: { $0.name == "default" }) else {
return PHThemeStyle.default
}
return defaultStyle
-2
View File
@@ -31,8 +31,6 @@ struct BarView: View {
}
}
}
.font(bar.config.text.font)
.italic(bar.config.text.italic)
.frame(height: 30)
.ignoresSafeArea()
.environmentObject(bar)
+1 -1
View File
@@ -11,7 +11,7 @@ struct SpaceBlockView: View {
var body: some View {
Rectangle()
.fill(block.style.background)
.fill(block.style.background?.uiColor ?? .clear)
.frame(width: width)
}
}
+7 -5
View File
@@ -7,7 +7,7 @@ struct TextBlockView: View {
@State var hovering = false
private var adjustOffset: CGAffineTransform {
guard let offset = bar.config.text.offset else { return .init() }
guard let offset = block.text.offset else { return .init() }
return .init(translationX: 0, y: offset)
}
@@ -15,16 +15,18 @@ struct TextBlockView: View {
if let label = block.label, !label.isEmpty {
ZStack(alignment: .center) {
RoundedRectangle(cornerRadius: 0)
.fill(block.style.background)
.foregroundStyle(block.style.background?.uiColor ?? .clear)
Group {
Text(label)
.foregroundStyle(block.style.foreground)
.font(block.text.font)
.italic(block.text.italic)
.foregroundStyle(block.style.foreground?.uiColor ?? .clear)
.transformEffect(adjustOffset)
.border(bar.debug ? .blue : .clear)
}
.padding(.leading, block.style.padding.leading)
.padding(.trailing, block.style.padding.trailing)
.padding(.trailing, block.style.padding?.trailing ?? 0)
.padding(.leading, block.style.padding?.leading ?? 0)
}
.fixedSize(horizontal: true, vertical: false)
.border(bar.debug ? .red : .clear)