Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: CI

on:
push:
branches: ["main", "master"]
pull_request:

jobs:
test-and-build:
runs-on: macos-14

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Show Swift version
run: swift --version

- name: Run unit tests
run: |
cd Lucid
swift test

- name: Verify app bundle script
run: |
cd Lucid
./build-app.sh debug
12 changes: 7 additions & 5 deletions Lucid/Lucid/Services/ProcessMonitor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ final class ProcessMonitor {
private var previousCPUTimes: [pid_t: UInt64] = [:]
private var previousCPUHistory: [Double] = []
private var previousMemoryHistory: [Double] = []
private let pollInterval: TimeInterval = 3.0
private let pollInterval: TimeInterval = 2.0
private let logger = Logger(subsystem: "com.tan.lucid", category: "ProcessMonitor")
private let timerQueue = DispatchQueue(label: "com.tan.lucid.timer", qos: .userInitiated)

Expand Down Expand Up @@ -87,6 +87,9 @@ final class ProcessMonitor {
guard let self else { return }
guard !self.isRefreshing else { return }
self.isRefreshing = true
defer {
self.isRefreshing = false
}

// Refresh NSWorkspace app names every other cycle to reduce MainActor blocking
self.shouldRefreshAppNames.toggle()
Expand Down Expand Up @@ -202,9 +205,9 @@ final class ProcessMonitor {
let finalCPUTimes = currentCPUTimes
let counts = FilterCounts(
total: newProcesses.count,
system: newProcesses.count(where: { $0.safety == .system }),
user: newProcesses.count(where: { $0.safety == .user }),
unknown: newProcesses.count(where: { $0.safety == .unknown })
system: newProcesses.filter { $0.safety == .system }.count,
user: newProcesses.filter { $0.safety == .user }.count,
unknown: newProcesses.filter { $0.safety == .unknown }.count
)
let ports = Array(Set(newProcesses.flatMap(\.ports))).sorted()

Expand All @@ -215,7 +218,6 @@ final class ProcessMonitor {
self.filterCounts = counts
self.activePorts = ports
self.updateSystemStats()
self.isRefreshing = false
}
}
}
Expand Down
25 changes: 16 additions & 9 deletions Lucid/Makefile
Original file line number Diff line number Diff line change
@@ -1,19 +1,26 @@
.PHONY: all build clean app run
.PHONY: all build test app run clean install

CONFIG ?= debug

all: app

build:
swift build
swift build --configuration $(CONFIG)

app: build
./build-app.sh
test:
swift test

clean:
swift package clean
rm -rf .build
app: build
./build-app.sh $(CONFIG)

run: app
open .build/arm64-apple-macosx/debug/Lucid.app
@BIN_PATH=$$(swift build --configuration $(CONFIG) --show-bin-path); \
open "$$BIN_PATH/Lucid.app"

install: app
cp -R .build/arm64-apple-macosx/debug/Lucid.app /Applications/
BIN_PATH="$$(swift build --configuration $(CONFIG) --show-bin-path)" && \
cp -R "$$BIN_PATH/Lucid.app" /Applications/

clean:
swift package clean
rm -rf .build
4 changes: 4 additions & 0 deletions Lucid/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ let package = Package(
name: "Lucid",
dependencies: [],
path: "Lucid",
exclude: [
"Info.plist",
"Lucid.entitlements"
],
resources: [
.copy("Assets.xcassets")
]
Expand Down
153 changes: 42 additions & 111 deletions Lucid/build-app.sh
Original file line number Diff line number Diff line change
@@ -1,125 +1,56 @@
#!/bin/bash
# Build script to create a proper macOS app bundle with icon
#!/usr/bin/env bash
set -euo pipefail

set -e

# Configuration
APP_NAME="Lucid"
BUILD_DIR=".build/arm64-apple-macosx/debug"
APP_BUNDLE="$BUILD_DIR/$APP_NAME.app"
CONFIGURATION="${1:-debug}"

if [[ "$(uname -s)" != "Darwin" ]]; then
echo "Error: $APP_NAME.app bundles can only be built on macOS." >&2
exit 1
fi

if [[ "$CONFIGURATION" != "debug" && "$CONFIGURATION" != "release" ]]; then
echo "Usage: $0 [debug|release]" >&2
exit 1
fi

ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$ROOT_DIR"

echo "Building $APP_NAME ($CONFIGURATION)..."
swift build --configuration "$CONFIGURATION"

BIN_PATH="$(swift build --configuration "$CONFIGURATION" --show-bin-path)"
EXECUTABLE="$BIN_PATH/$APP_NAME"
APP_BUNDLE="$BIN_PATH/$APP_NAME.app"
CONTENTS="$APP_BUNDLE/Contents"
MACOS="$CONTENTS/MacOS"
RESOURCES="$CONTENTS/Resources"
EXECUTABLE="$BUILD_DIR/$APP_NAME"
ICONSET_SOURCE="Lucid/Assets.xcassets/AppIcon.appiconset"
ICONSET="$RESOURCES/AppIcon.iconset"

echo "Building $APP_NAME.app bundle..."
if [[ ! -f "$EXECUTABLE" ]]; then
echo "Error: compiled executable not found at $EXECUTABLE" >&2
exit 1
fi

# Clean previous build
echo "Creating app bundle at: $APP_BUNDLE"
rm -rf "$APP_BUNDLE"
mkdir -p "$MACOS" "$RESOURCES"

# Create app bundle structure
mkdir -p "$MACOS"
mkdir -p "$RESOURCES"

# Copy executable
cp "$EXECUTABLE" "$MACOS/"

# Copy Info.plist
cp "$EXECUTABLE" "$MACOS/$APP_NAME"
chmod +x "$MACOS/$APP_NAME"
cp "Lucid/Info.plist" "$CONTENTS/Info.plist"

# Copy Assets.xcassets to Resources
cp -R "Lucid/Assets.xcassets" "$RESOURCES/"

# Create icon set from Assets.xcassets for macOS to recognize
ICONSET="$RESOURCES/AppIcon.iconset"
mkdir -p "$ICONSET"

# Copy icon files from Assets.xcassets to a location macOS recognizes
cp Lucid/Assets.xcassets/AppIcon.appiconset/icon_16x16.png "$ICONSET/icon_16x16.png"
cp Lucid/Assets.xcassets/AppIcon.appiconset/icon_16x16@2x.png "$ICONSET/icon_16x16@2x.png"
cp Lucid/Assets.xcassets/AppIcon.appiconset/icon_32x32.png "$ICONSET/icon_32x32.png"
cp Lucid/Assets.xcassets/AppIcon.appiconset/icon_32x32@2x.png "$ICONSET/icon_32x32@2x.png"
cp Lucid/Assets.xcassets/AppIcon.appiconset/icon_128x128.png "$ICONSET/icon_128x128.png"
cp Lucid/Assets.xcassets/AppIcon.appiconset/icon_256x256.png "$ICONSET/icon_256x256.png"
cp Lucid/Assets.xcassets/AppIcon.appiconset/icon_256x256.png "$ICONSET/icon_128x128@2x.png"
cp Lucid/Assets.xcassets/AppIcon.appiconset/icon_512x512.png "$ICONSET/icon_256x256@2x.png"
cp Lucid/Assets.xcassets/AppIcon.appiconset/icon_512x512.png "$ICONSET/icon_512x512.png"
cp Lucid/Assets.xcassets/AppIcon.appiconset/icon_1024x1024.png "$ICONSET/icon_512x512@2x.png"
if [[ -d "$ICONSET_SOURCE" ]]; then
cp -R "$ICONSET_SOURCE" "$ICONSET"

# Create iconset Contents.json
cat > "$ICONSET/Contents.json" << 'EOF'
{
"images" : [
{
"filename" : "icon_16x16.png",
"idiom" : "mac",
"scale" : "1x",
"size" : "16x16"
},
{
"filename" : "icon_16x16@2x.png",
"idiom" : "mac",
"scale" : "2x",
"size" : "16x16"
},
{
"filename" : "icon_32x32.png",
"idiom" : "mac",
"scale" : "1x",
"size" : "32x32"
},
{
"filename" : "icon_32x32@2x.png",
"idiom" : "mac",
"scale" : "2x",
"size" : "32x32"
},
{
"filename" : "icon_128x128.png",
"idiom" : "mac",
"scale" : "1x",
"size" : "128x128"
},
{
"filename" : "icon_256x256.png",
"idiom" : "mac",
"scale" : "2x",
"size" : "128x128"
},
{
"filename" : "icon_256x256.png",
"idiom" : "mac",
"scale" : "1x",
"size" : "256x256"
},
{
"filename" : "icon_512x512.png",
"idiom" : "mac",
"scale" : "2x",
"size" : "256x256"
},
{
"filename" : "icon_512x512.png",
"idiom" : "mac",
"scale" : "1x",
"size" : "512x512"
},
{
"filename" : "icon_1024x1024.png",
"idiom" : "mac",
"scale" : "2x",
"size" : "512x512"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
EOF
if command -v iconutil >/dev/null 2>&1; then
iconutil -c icns "$ICONSET" -o "$RESOURCES/AppIcon.icns"
fi

# Update Info.plist to reference the icon
/usr/libexec/PlistBuddy -c "Add :CFBundleIconFile string AppIcon" "$CONTENTS/Info.plist" 2>/dev/null || true
/usr/libexec/PlistBuddy -c "Set :CFBundleIconFile AppIcon" "$CONTENTS/Info.plist" 2>/dev/null \
|| /usr/libexec/PlistBuddy -c "Add :CFBundleIconFile string AppIcon" "$CONTENTS/Info.plist"
fi

echo "βœ“ $APP_NAME.app bundle created successfully"
echo " Location: $APP_BUNDLE"
echo "βœ… Created $APP_BUNDLE"
26 changes: 3 additions & 23 deletions Lucid/create-app-bundle.sh
Original file line number Diff line number Diff line change
@@ -1,24 +1,4 @@
#!/bin/bash
set -e
#!/usr/bin/env bash
set -euo pipefail

APP_NAME="Lucid"
BUILD_DIR=".build/arm64-apple-macosx/debug"
BUNDLE_DIR="$BUILD_DIR/$APP_NAME.app"

# Clean old bundle
rm -rf "$BUNDLE_DIR"

# Create app bundle structure
mkdir -p "$BUNDLE_DIR/Contents/MacOS"
mkdir -p "$BUNDLE_DIR/Contents/Resources"

# Copy executable
cp "$BUILD_DIR/$APP_NAME" "$BUNDLE_DIR/Contents/MacOS/$APP_NAME"

# Copy Info.plist
cp "$APP_NAME/Info.plist" "$BUNDLE_DIR/Contents/Info.plist"

# Set executable permissions
chmod +x "$BUNDLE_DIR/Contents/MacOS/$APP_NAME"

echo "App bundle created at: $BUNDLE_DIR"
"$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/build-app.sh" "${1:-debug}"
51 changes: 0 additions & 51 deletions Lucid/run-lucid.sh

This file was deleted.

Loading