Use lowercase puter branding
This commit is contained in:
+7
-7
@@ -2,20 +2,20 @@
|
|||||||
import PackageDescription
|
import PackageDescription
|
||||||
|
|
||||||
let package = Package(
|
let package = Package(
|
||||||
name: "Puter",
|
name: "puter",
|
||||||
platforms: [.macOS(.v14)],
|
platforms: [.macOS(.v14)],
|
||||||
products: [
|
products: [
|
||||||
.executable(name: "Puter", targets: ["Puter"])
|
.executable(name: "puter", targets: ["puter"])
|
||||||
],
|
],
|
||||||
targets: [
|
targets: [
|
||||||
.executableTarget(
|
.executableTarget(
|
||||||
name: "Puter",
|
name: "puter",
|
||||||
path: "Sources/Puter"
|
path: "Sources/puter"
|
||||||
),
|
),
|
||||||
.testTarget(
|
.testTarget(
|
||||||
name: "PuterTests",
|
name: "puterTests",
|
||||||
dependencies: ["Puter"],
|
dependencies: ["puter"],
|
||||||
path: "Tests/PuterTests"
|
path: "Tests/puterTests"
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# Puter
|
# puter
|
||||||
|
|
||||||
A native macOS system monitor written in SwiftUI and inspired by Windows 11 Task Manager.
|
A native macOS system monitor written in SwiftUI and inspired by Windows 11 Task Manager.
|
||||||
|
|
||||||
@@ -57,7 +57,7 @@ To create a standard double-clickable app bundle:
|
|||||||
|
|
||||||
```sh
|
```sh
|
||||||
./scripts/build-app.sh
|
./scripts/build-app.sh
|
||||||
open "dist/Puter.app"
|
open "dist/puter.app"
|
||||||
```
|
```
|
||||||
|
|
||||||
To create a drag-to-install disk image containing the app and an Applications shortcut:
|
To create a drag-to-install disk image containing the app and an Applications shortcut:
|
||||||
|
|||||||
@@ -5,17 +5,17 @@
|
|||||||
<key>CFBundleDevelopmentRegion</key>
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
<string>en</string>
|
<string>en</string>
|
||||||
<key>CFBundleDisplayName</key>
|
<key>CFBundleDisplayName</key>
|
||||||
<string>Puter</string>
|
<string>puter</string>
|
||||||
<key>CFBundleExecutable</key>
|
<key>CFBundleExecutable</key>
|
||||||
<string>Puter</string>
|
<string>puter</string>
|
||||||
<key>CFBundleIdentifier</key>
|
<key>CFBundleIdentifier</key>
|
||||||
<string>local.mactaskmanager.app</string>
|
<string>dev.soconnor.puter</string>
|
||||||
<key>CFBundleIconName</key>
|
<key>CFBundleIconName</key>
|
||||||
<string>Puter</string>
|
<string>puter</string>
|
||||||
<key>CFBundleInfoDictionaryVersion</key>
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
<string>6.0</string>
|
<string>6.0</string>
|
||||||
<key>CFBundleName</key>
|
<key>CFBundleName</key>
|
||||||
<string>Puter</string>
|
<string>puter</string>
|
||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>APPL</string>
|
<string>APPL</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ struct SettingsView: View {
|
|||||||
var body: some View {
|
var body: some View {
|
||||||
@Bindable var monitor = monitor
|
@Bindable var monitor = monitor
|
||||||
VStack(spacing: 0) {
|
VStack(spacing: 0) {
|
||||||
PageHeader(title: "Settings", subtitle: "Customize Puter")
|
PageHeader(title: "Settings", subtitle: "Customize puter")
|
||||||
Form {
|
Form {
|
||||||
Section("General") {
|
Section("General") {
|
||||||
Picker("Default start page", selection: $defaultStartPage) {
|
Picker("Default start page", selection: $defaultStartPage) {
|
||||||
@@ -125,7 +125,7 @@ struct SettingsView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Section("About") {
|
Section("About") {
|
||||||
LabeledContent("Application", value: "Puter")
|
LabeledContent("Application", value: "puter")
|
||||||
LabeledContent("Version", value: appVersion)
|
LabeledContent("Version", value: appVersion)
|
||||||
LabeledContent("Framework", value: "Native SwiftUI")
|
LabeledContent("Framework", value: "Native SwiftUI")
|
||||||
}
|
}
|
||||||
@@ -2,7 +2,7 @@ import AppKit
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
extension Notification.Name {
|
extension Notification.Name {
|
||||||
static let runNewTask = Notification.Name("Puter.runNewTask")
|
static let runNewTask = Notification.Name("puter.runNewTask")
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ContentView: View {
|
struct ContentView: View {
|
||||||
@@ -70,14 +70,14 @@ struct ContentView: View {
|
|||||||
TaskToolbarContent(showingNewTask: $showingNewTask)
|
TaskToolbarContent(showingNewTask: $showingNewTask)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.navigationTitle("Puter")
|
.navigationTitle("puter")
|
||||||
.animation(.snappy(duration: 0.22), value: sidebarCompact)
|
.animation(.snappy(duration: 0.22), value: sidebarCompact)
|
||||||
.onChange(of: selection) { _, _ in searchText = "" }
|
.onChange(of: selection) { _, _ in searchText = "" }
|
||||||
.onReceive(NotificationCenter.default.publisher(for: .runNewTask)) { _ in showingNewTask = true }
|
.onReceive(NotificationCenter.default.publisher(for: .runNewTask)) { _ in showingNewTask = true }
|
||||||
.sheet(isPresented: $showingNewTask) {
|
.sheet(isPresented: $showingNewTask) {
|
||||||
NewTaskView(isPresented: $showingNewTask)
|
NewTaskView(isPresented: $showingNewTask)
|
||||||
}
|
}
|
||||||
.alert("Puter", isPresented: Binding(
|
.alert("puter", isPresented: Binding(
|
||||||
get: { monitor.errorMessage != nil },
|
get: { monitor.errorMessage != nil },
|
||||||
set: { if !$0 { monitor.errorMessage = nil } }
|
set: { if !$0 { monitor.errorMessage = nil } }
|
||||||
)) {
|
)) {
|
||||||
@@ -294,7 +294,7 @@ struct HardwareView: View {
|
|||||||
metric("Control backend", FanControlService.isAvailable ? "Ready" : "Unavailable")
|
metric("Control backend", FanControlService.isAvailable ? "Ready" : "Unavailable")
|
||||||
Spacer()
|
Spacer()
|
||||||
}
|
}
|
||||||
Text("Puter reads fan sensors directly through the Mac's SMC interface. Manual targets are clamped to each fan's reported safe range; Automatic returns control to macOS.")
|
Text("puter reads fan sensors directly through the Mac's SMC interface. Manual targets are clamped to each fan's reported safe range; Automatic returns control to macOS.")
|
||||||
.font(.caption).foregroundStyle(.secondary)
|
.font(.caption).foregroundStyle(.secondary)
|
||||||
HStack {
|
HStack {
|
||||||
Button("Apply targets", systemImage: "speedometer") {
|
Button("Apply targets", systemImage: "speedometer") {
|
||||||
@@ -126,7 +126,7 @@ enum ProcessDiagnosticReporter {
|
|||||||
|
|
||||||
let panel = NSSavePanel()
|
let panel = NSSavePanel()
|
||||||
panel.title = "Create diagnostic report"
|
panel.title = "Create diagnostic report"
|
||||||
panel.message = "Puter will sample \(process.displayName) for \(duration) seconds and save a text report."
|
panel.message = "puter will sample \(process.displayName) for \(duration) seconds and save a text report."
|
||||||
panel.prompt = "Create Report"
|
panel.prompt = "Create Report"
|
||||||
panel.nameFieldStringValue = safeFilename("\(process.displayName)-\(process.pid)-sample.txt")
|
panel.nameFieldStringValue = safeFilename("\(process.displayName)-\(process.pid)-sample.txt")
|
||||||
panel.allowedContentTypes = [.plainText]
|
panel.allowedContentTypes = [.plainText]
|
||||||
@@ -6,7 +6,7 @@ struct PuterApp: App {
|
|||||||
@AppStorage("sidebarCompact") private var sidebarCompact = false
|
@AppStorage("sidebarCompact") private var sidebarCompact = false
|
||||||
|
|
||||||
var body: some Scene {
|
var body: some Scene {
|
||||||
WindowGroup("Puter") {
|
WindowGroup("puter") {
|
||||||
ContentView()
|
ContentView()
|
||||||
.environment(monitor)
|
.environment(monitor)
|
||||||
.frame(minWidth: 820, minHeight: 560)
|
.frame(minWidth: 820, minHeight: 560)
|
||||||
@@ -66,7 +66,7 @@ struct AppHistoryView: View {
|
|||||||
Button("Cancel", role: .cancel) {}
|
Button("Cancel", role: .cancel) {}
|
||||||
Button("Reset", role: .destructive) { monitor.resetAppHistory() }
|
Button("Reset", role: .destructive) { monitor.resetAppHistory() }
|
||||||
} message: {
|
} message: {
|
||||||
Text("CPU time and network totals collected by Puter will be permanently cleared.")
|
Text("CPU time and network totals collected by puter will be permanently cleared.")
|
||||||
}
|
}
|
||||||
.sheet(item: $inspectedUsage) { AppUsagePropertiesView(usage: $0) }
|
.sheet(item: $inspectedUsage) { AppUsagePropertiesView(usage: $0) }
|
||||||
}
|
}
|
||||||
@@ -147,7 +147,7 @@ final class SystemMonitor {
|
|||||||
|
|
||||||
func terminate(_ process: ProcessRecord, force: Bool = false) {
|
func terminate(_ process: ProcessRecord, force: Bool = false) {
|
||||||
guard process.pid != getpid() else {
|
guard process.pid != getpid() else {
|
||||||
errorMessage = "Puter cannot end itself."
|
errorMessage = "puter cannot end itself."
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let signal = force ? SIGKILL : SIGTERM
|
let signal = force ? SIGKILL : SIGTERM
|
||||||
@@ -170,7 +170,7 @@ final class SystemMonitor {
|
|||||||
|
|
||||||
func terminateGroup(_ group: [ProcessRecord]) {
|
func terminateGroup(_ group: [ProcessRecord]) {
|
||||||
guard !group.contains(where: { $0.pid == getpid() }) else {
|
guard !group.contains(where: { $0.pid == getpid() }) else {
|
||||||
errorMessage = "Puter cannot end an application group that contains itself."
|
errorMessage = "puter cannot end an application group that contains itself."
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let pids = Set(group.map(\.pid))
|
let pids = Set(group.map(\.pid))
|
||||||
@@ -188,7 +188,7 @@ final class SystemMonitor {
|
|||||||
|
|
||||||
func sendSignal(_ signal: Int32, to process: ProcessRecord, successMessage: String? = nil) {
|
func sendSignal(_ signal: Int32, to process: ProcessRecord, successMessage: String? = nil) {
|
||||||
guard process.pid != getpid() else {
|
guard process.pid != getpid() else {
|
||||||
errorMessage = "Puter cannot control itself."
|
errorMessage = "puter cannot control itself."
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if kill(process.pid, signal) != 0 {
|
if kill(process.pid, signal) != 0 {
|
||||||
@@ -415,7 +415,7 @@ private enum AppHistoryPersistence {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static let directory = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)[0]
|
static let directory = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)[0]
|
||||||
.appendingPathComponent("MacTaskManager", isDirectory: true)
|
.appendingPathComponent("puter", isDirectory: true)
|
||||||
static let url = directory.appendingPathComponent("app-history.json")
|
static let url = directory.appendingPathComponent("app-history.json")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import XCTest
|
import XCTest
|
||||||
@testable import Puter
|
@testable import puter
|
||||||
|
|
||||||
final class ModelTests: XCTestCase {
|
final class ModelTests: XCTestCase {
|
||||||
func testAppleSystemServicePublisherAndDomainIdentity() {
|
func testAppleSystemServicePublisherAndDomainIdentity() {
|
||||||
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
@@ -2,10 +2,10 @@
|
|||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
PROJECT_DIR="${0:A:h:h}"
|
PROJECT_DIR="${0:A:h:h}"
|
||||||
APP_DIR="$PROJECT_DIR/dist/Puter.app"
|
APP_DIR="$PROJECT_DIR/dist/puter.app"
|
||||||
ICON_SOURCE="${PUTER_ICON_SOURCE:-$PROJECT_DIR/Puter.icon}"
|
ICON_SOURCE="${PUTER_ICON_SOURCE:-$PROJECT_DIR/puter.icon}"
|
||||||
STAGING_ROOT="$(mktemp -d /tmp/puter-build.XXXXXX)"
|
STAGING_ROOT="$(mktemp -d /tmp/puter-build.XXXXXX)"
|
||||||
STAGED_APP="$STAGING_ROOT/Puter.app"
|
STAGED_APP="$STAGING_ROOT/puter.app"
|
||||||
CONTENTS_DIR="$STAGED_APP/Contents"
|
CONTENTS_DIR="$STAGED_APP/Contents"
|
||||||
|
|
||||||
trap '/bin/rm -rf -- "$STAGING_ROOT"' EXIT
|
trap '/bin/rm -rf -- "$STAGING_ROOT"' EXIT
|
||||||
@@ -14,9 +14,9 @@ cd "$PROJECT_DIR"
|
|||||||
swift build -c release
|
swift build -c release
|
||||||
|
|
||||||
mkdir -p "$CONTENTS_DIR/MacOS" "$CONTENTS_DIR/Resources"
|
mkdir -p "$CONTENTS_DIR/MacOS" "$CONTENTS_DIR/Resources"
|
||||||
cp "$PROJECT_DIR/.build/release/Puter" "$CONTENTS_DIR/MacOS/Puter"
|
cp "$PROJECT_DIR/.build/release/puter" "$CONTENTS_DIR/MacOS/puter"
|
||||||
cp "$PROJECT_DIR/Resources/Info.plist" "$CONTENTS_DIR/Info.plist"
|
cp "$PROJECT_DIR/Resources/Info.plist" "$CONTENTS_DIR/Info.plist"
|
||||||
chmod +x "$CONTENTS_DIR/MacOS/Puter"
|
chmod +x "$CONTENTS_DIR/MacOS/puter"
|
||||||
|
|
||||||
SMC_SOURCE="/Applications/Stats.app/Contents/Resources/smc"
|
SMC_SOURCE="/Applications/Stats.app/Contents/Resources/smc"
|
||||||
if [[ -x "$SMC_SOURCE" ]]; then
|
if [[ -x "$SMC_SOURCE" ]]; then
|
||||||
@@ -31,14 +31,14 @@ if [[ -d "$ICON_SOURCE" ]]; then
|
|||||||
--compile "$CONTENTS_DIR/Resources" \
|
--compile "$CONTENTS_DIR/Resources" \
|
||||||
--platform macosx \
|
--platform macosx \
|
||||||
--minimum-deployment-target 14.0 \
|
--minimum-deployment-target 14.0 \
|
||||||
--app-icon Puter \
|
--app-icon puter \
|
||||||
--output-partial-info-plist "$CONTENTS_DIR/Resources/IconInfo.plist" >/dev/null
|
--output-partial-info-plist "$CONTENTS_DIR/Resources/IconInfo.plist" >/dev/null
|
||||||
|
|
||||||
[[ -s "$CONTENTS_DIR/Resources/Assets.car" ]] || {
|
[[ -s "$CONTENTS_DIR/Resources/Assets.car" ]] || {
|
||||||
print -u2 "Icon Composer did not produce an Assets.car file"
|
print -u2 "Icon Composer did not produce an Assets.car file"
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
[[ "$(/usr/libexec/PlistBuddy -c 'Print :CFBundleIconName' "$CONTENTS_DIR/Info.plist")" == "Puter" ]] || {
|
[[ "$(/usr/libexec/PlistBuddy -c 'Print :CFBundleIconName' "$CONTENTS_DIR/Info.plist")" == "puter" ]] || {
|
||||||
print -u2 "CFBundleIconName must reference the Icon Composer asset"
|
print -u2 "CFBundleIconName must reference the Icon Composer asset"
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
PROJECT_DIR="${0:A:h:h}"
|
PROJECT_DIR="${0:A:h:h}"
|
||||||
APP_PATH="$PROJECT_DIR/dist/Puter.app"
|
APP_PATH="$PROJECT_DIR/dist/puter.app"
|
||||||
DMG_PATH="$PROJECT_DIR/dist/Puter-macOS.dmg"
|
DMG_PATH="$PROJECT_DIR/dist/puter-macOS.dmg"
|
||||||
STAGING_DIR="$(mktemp -d /tmp/puter-dmg.XXXXXX)"
|
STAGING_DIR="$(mktemp -d /tmp/puter-dmg.XXXXXX)"
|
||||||
|
|
||||||
cleanup() {
|
cleanup() {
|
||||||
@@ -15,13 +15,13 @@ if [[ ! -d "$APP_PATH" ]]; then
|
|||||||
"$PROJECT_DIR/scripts/build-app.sh"
|
"$PROJECT_DIR/scripts/build-app.sh"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
ditto --norsrc "$APP_PATH" "$STAGING_DIR/Puter.app"
|
ditto --norsrc "$APP_PATH" "$STAGING_DIR/puter.app"
|
||||||
xattr -cr "$STAGING_DIR/Puter.app"
|
xattr -cr "$STAGING_DIR/puter.app"
|
||||||
codesign --verify --deep --strict "$STAGING_DIR/Puter.app"
|
codesign --verify --deep --strict "$STAGING_DIR/puter.app"
|
||||||
ln -s /Applications "$STAGING_DIR/Applications"
|
ln -s /Applications "$STAGING_DIR/Applications"
|
||||||
|
|
||||||
hdiutil create \
|
hdiutil create \
|
||||||
-volname "Puter" \
|
-volname "puter" \
|
||||||
-srcfolder "$STAGING_DIR" \
|
-srcfolder "$STAGING_DIR" \
|
||||||
-format UDZO \
|
-format UDZO \
|
||||||
-imagekey zlib-level=9 \
|
-imagekey zlib-level=9 \
|
||||||
|
|||||||
Reference in New Issue
Block a user