Compare commits
4 Commits
eebbe3cbb8
...
3da4c8e466
| Author | SHA1 | Date | |
|---|---|---|---|
|
3da4c8e466
|
|||
|
70a170f544
|
|||
|
7c0551c662
|
|||
|
0edc4ca47a
|
@@ -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"
|
||||||
@@ -6,6 +6,8 @@ final class PHBlock: ObservableObject, Decodable, Identifiable {
|
|||||||
|
|
||||||
let command: String
|
let command: String
|
||||||
let name: String?
|
let name: String?
|
||||||
|
let textName: String?
|
||||||
|
@Published var text: PHThemeText = .default
|
||||||
let styleName: String?
|
let styleName: String?
|
||||||
@Published var style: PHThemeStyle = .default
|
@Published var style: PHThemeStyle = .default
|
||||||
let refresh: Double?
|
let refresh: Double?
|
||||||
@@ -38,6 +40,7 @@ final class PHBlock: ObservableObject, Decodable, Identifiable {
|
|||||||
|
|
||||||
private enum CodingKeys: String, CodingKey {
|
private enum CodingKeys: String, CodingKey {
|
||||||
case command, name
|
case command, name
|
||||||
|
case textName = "text"
|
||||||
case styleName = "style"
|
case styleName = "style"
|
||||||
case refresh, events, centered
|
case refresh, events, centered
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
struct PHConfig: Decodable {
|
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,3 @@
|
|||||||
[text]
|
height = 30
|
||||||
font = "Comic Code"
|
|
||||||
size = 14
|
[window]
|
||||||
weight = "regular"
|
|
||||||
style = "normal"
|
|
||||||
offset = -0.5
|
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
struct PHTheme: Decodable {
|
struct PHTheme: Decodable {
|
||||||
let window: PHThemeWindow
|
let window: PHThemeWindow
|
||||||
let styles: [PHThemeStyle]
|
let texts: [PHThemeText]?
|
||||||
|
let styles: [PHThemeStyle]?
|
||||||
|
|
||||||
private enum CodingKeys: String, CodingKey {
|
private enum CodingKeys: String, CodingKey {
|
||||||
case window
|
case window
|
||||||
|
case texts = "text"
|
||||||
case styles = "style"
|
case styles = "style"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
struct PHThemePadding: Decodable {
|
struct PHThemePadding: Decodable {
|
||||||
let leading: Double
|
let trailing: Double?
|
||||||
let trailing: Double
|
let leading: Double?
|
||||||
|
|
||||||
var total: Double { leading + trailing }
|
private enum CodingKeys: String, CodingKey {
|
||||||
|
case trailing = "right"
|
||||||
static var zero: PHThemePadding {
|
case leading = "left"
|
||||||
Self.init(leading: 0, trailing: 0)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
struct PHThemeStyle: Decodable {
|
struct PHThemeStyle: Decodable {
|
||||||
let name: String
|
let name: String
|
||||||
let foreground: PHThemeColor
|
let foreground: PHThemeColor?
|
||||||
let background: PHThemeColor
|
let background: PHThemeColor?
|
||||||
let padding: PHThemePadding
|
let padding: PHThemePadding?
|
||||||
|
|
||||||
static let `default`: Self = {
|
static let `default`: Self = {
|
||||||
PHThemeStyle(
|
PHThemeStyle(
|
||||||
name: "_default",
|
name: "_default",
|
||||||
foreground: .init(color: "#000000", alpha: nil),
|
foreground: .init(color: "#000000", alpha: nil),
|
||||||
background: .init(color: "#ffffff", alpha: nil),
|
background: .init(color: "#ffffff", alpha: nil),
|
||||||
padding: .init(leading: 10, trailing: 10)
|
padding: .init(trailing: 12, leading: 12)
|
||||||
)
|
)
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|||||||
+18
-5
@@ -1,22 +1,35 @@
|
|||||||
import AppKit
|
import AppKit
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
struct PHConfigText: Decodable {
|
struct PHThemeText: Decodable {
|
||||||
|
let name: String
|
||||||
let fontFamily: String
|
let fontFamily: String
|
||||||
let size: Double
|
let size: Double
|
||||||
let weight: PHConfigTextWeight
|
let weight: PHThemeTextWeight
|
||||||
let style: PHConfigTextStyle
|
let style: PHThemeTextStyle
|
||||||
let offset: Double?
|
let offset: Double?
|
||||||
|
|
||||||
private enum CodingKeys: String, CodingKey {
|
private enum CodingKeys: String, CodingKey {
|
||||||
|
case name
|
||||||
case fontFamily = "font"
|
case fontFamily = "font"
|
||||||
case size, weight, style, offset
|
case size, weight, style, offset
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static let `default`: Self = {
|
||||||
|
PHThemeText(
|
||||||
|
name: "_default",
|
||||||
|
fontFamily: "monospace",
|
||||||
|
size: 14.0,
|
||||||
|
weight: .regular,
|
||||||
|
style: .normal,
|
||||||
|
offset: nil
|
||||||
|
)
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Design
|
// Design
|
||||||
|
|
||||||
extension PHConfigText {
|
extension PHThemeText {
|
||||||
var design: SwiftUI.Font.Design? {
|
var design: SwiftUI.Font.Design? {
|
||||||
switch fontFamily {
|
switch fontFamily {
|
||||||
case "sans":
|
case "sans":
|
||||||
@@ -46,7 +59,7 @@ extension PHConfigText {
|
|||||||
|
|
||||||
// Font
|
// Font
|
||||||
|
|
||||||
extension PHConfigText {
|
extension PHThemeText {
|
||||||
/// Construct an `NSFont` from the typeface settings.
|
/// Construct an `NSFont` from the typeface settings.
|
||||||
///
|
///
|
||||||
/// Falls back to the system font if parsing fails.
|
/// 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
-1
@@ -1,7 +1,7 @@
|
|||||||
import AppKit
|
import AppKit
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
enum PHConfigTextWeight: String, Decodable {
|
enum PHThemeTextWeight: String, Decodable {
|
||||||
case thin
|
case thin
|
||||||
case ultraLight = "ultralight"
|
case ultraLight = "ultralight"
|
||||||
case light
|
case light
|
||||||
@@ -6,20 +6,33 @@
|
|||||||
shadow = false
|
shadow = false
|
||||||
blur = 0.0
|
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]]
|
[[style]]
|
||||||
name = "default"
|
name = "default"
|
||||||
foreground = { color = "#aed3f3" }
|
foreground = { color = "#aed3f3" }
|
||||||
background = { color = "#010408", alpha = 0.825 }
|
background = { color = "#010408", alpha = 0.825 }
|
||||||
padding = { leading = 0.0, trailing = 0.0 }
|
|
||||||
|
|
||||||
[[style]]
|
[[style]]
|
||||||
name = "floating"
|
name = "floating"
|
||||||
foreground = { color = "#aed3f3" }
|
foreground = { color = "#aed3f3" }
|
||||||
background = { color = "#000000", alpha = 0 }
|
|
||||||
padding = { leading = 0.0, trailing = 0.0 }
|
|
||||||
|
|
||||||
[[style]]
|
[[style]]
|
||||||
name = "tinted"
|
name = "tinted"
|
||||||
foreground = { color = "#aed3f3" }
|
foreground = { color = "#aed3f3" }
|
||||||
background = { color = "#0f304a" }
|
background = { color = "#0f304a" }
|
||||||
padding = { leading = 12.0, trailing = 12.0 }
|
padding = { left = 12.0, right = 12.0 }
|
||||||
|
|||||||
@@ -29,17 +29,28 @@ final class BarController: ObservableObject {
|
|||||||
self.blocks = blocks
|
self.blocks = blocks
|
||||||
|
|
||||||
for block in blocks {
|
for block in blocks {
|
||||||
|
block.text = text(for: block)
|
||||||
block.style = style(for: block)
|
block.style = style(for: block)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Style
|
// Theming
|
||||||
|
|
||||||
extension BarController {
|
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 {
|
func style(for block: PHBlock) -> PHThemeStyle {
|
||||||
guard let style = theme.styles.first(where: { $0.name == block.styleName }) else {
|
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 defaultStyle = theme.styles?.first(where: { $0.name == "default" }) else {
|
||||||
return PHThemeStyle.default
|
return PHThemeStyle.default
|
||||||
}
|
}
|
||||||
return defaultStyle
|
return defaultStyle
|
||||||
|
|||||||
@@ -31,8 +31,6 @@ struct BarView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.font(bar.config.text.font)
|
|
||||||
.italic(bar.config.text.italic)
|
|
||||||
.frame(height: 30)
|
.frame(height: 30)
|
||||||
.ignoresSafeArea()
|
.ignoresSafeArea()
|
||||||
.environmentObject(bar)
|
.environmentObject(bar)
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ struct SpaceBlockView: View {
|
|||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Rectangle()
|
Rectangle()
|
||||||
.fill(block.style.background)
|
.fill(block.style.background?.uiColor ?? .clear)
|
||||||
.frame(width: width)
|
.frame(width: width)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ struct TextBlockView: View {
|
|||||||
@State var hovering = false
|
@State var hovering = false
|
||||||
|
|
||||||
private var adjustOffset: CGAffineTransform {
|
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)
|
return .init(translationX: 0, y: offset)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -15,16 +15,18 @@ struct TextBlockView: View {
|
|||||||
if let label = block.label, !label.isEmpty {
|
if let label = block.label, !label.isEmpty {
|
||||||
ZStack(alignment: .center) {
|
ZStack(alignment: .center) {
|
||||||
RoundedRectangle(cornerRadius: 0)
|
RoundedRectangle(cornerRadius: 0)
|
||||||
.fill(block.style.background)
|
.foregroundStyle(block.style.background?.uiColor ?? .clear)
|
||||||
|
|
||||||
Group {
|
Group {
|
||||||
Text(label)
|
Text(label)
|
||||||
.foregroundStyle(block.style.foreground)
|
.font(block.text.font)
|
||||||
|
.italic(block.text.italic)
|
||||||
|
.foregroundStyle(block.style.foreground?.uiColor ?? .clear)
|
||||||
.transformEffect(adjustOffset)
|
.transformEffect(adjustOffset)
|
||||||
.border(bar.debug ? .blue : .clear)
|
.border(bar.debug ? .blue : .clear)
|
||||||
}
|
}
|
||||||
.padding(.leading, block.style.padding.leading)
|
.padding(.trailing, block.style.padding?.trailing ?? 0)
|
||||||
.padding(.trailing, block.style.padding.trailing)
|
.padding(.leading, block.style.padding?.leading ?? 0)
|
||||||
}
|
}
|
||||||
.fixedSize(horizontal: true, vertical: false)
|
.fixedSize(horizontal: true, vertical: false)
|
||||||
.border(bar.debug ? .red : .clear)
|
.border(bar.debug ? .red : .clear)
|
||||||
|
|||||||
Reference in New Issue
Block a user