Skip to content
Open
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
10 changes: 10 additions & 0 deletions Resources/tr.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -590,3 +590,13 @@
"The downloaded update payload, staging copy, updater state, and updater logs." = "İndirilen güncelleme paketi, hazırlık kopyası, güncelleyici durumu ve güncelleyici günlükleri.";
"The installed app, its settings, accounts, and documents." = "Kurulu uygulama, ayarları, hesapları ve belgeleri.";
"Your account, playlists, local files, and explicitly downloaded offline library." = "Hesabınız, çalma listeleriniz, yerel dosyalarınız ve bilerek indirdiğiniz çevrimdışı kitaplığınız.";

/* MARK: Welcome / Onboarding */
"Welcome to ClearDisk" = "ClearDisk'e Hoş Geldiniz";
"Developer cache cleaner & disk space monitor" = "Geliştirici önbellek temizleyici ve disk alanı izleyici";
"ClearDisk lives here in your menu bar!" = "ClearDisk menü çubuğunuzda burada yer alır!";
"Runs in your Menu Bar" = "Menü Çubuğunuzda Çalışır";
"ClearDisk sits quietly in your menu bar without filling your Dock. Click its icon anytime to inspect caches and clean disk space." = "ClearDisk, Dock'unuzu doldurmadan menü çubuğunuzda sessizce çalışır. Önbellekleri incelemek ve disk alanını temizlemek için simgesine dilediğiniz zaman tıklayın.";
"MacBook Notch & Hidden Icons" = "MacBook Çentiği ve Gizli Simgeler";
"If your menu bar is crowded or hidden behind a camera notch, you can also launch ClearDisk from Finder or Spotlight anytime to open it." = "Menü çubuğunuz kalabalıksa veya kamera çentiğinin arkasında kalıyorsa, ClearDisk'i açmak için Finder veya Spotlight'tan dilediğiniz zaman tekrar başlatabilirsiniz.";
"Got it! Go to Menu Bar" = "Anladım! Menü Çubuğuna Git";
39 changes: 38 additions & 1 deletion Sources/ClearDisk/ClearDiskApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,12 @@ struct ClearDiskApp {
static func main() {
let app = NSApplication.shared
app.delegate = delegate
app.setActivationPolicy(.accessory) // Menu bar only, no dock icon
let isFirstLaunch = !UserDefaults.standard.bool(forKey: AppDelegate.hasSeenWelcomeWindowKey)
if isFirstLaunch {
app.setActivationPolicy(.regular) // Standalone window with Dock icon during first-launch onboarding
} else {
app.setActivationPolicy(.accessory) // Menu bar only, no dock icon
}
app.run()
}
}
Expand Down Expand Up @@ -50,6 +55,8 @@ extension Notification.Name {

// MARK: - App Delegate
class AppDelegate: NSObject, NSApplicationDelegate, NSPopoverDelegate {
static let hasSeenWelcomeWindowKey = "ClearDisk.hasSeenWelcomeWindow"
var welcomeWindowController: WelcomeWindowController?
var statusItem: NSStatusItem!
var popover: NSPopover!
var diskMonitor: DiskMonitor!
Expand Down Expand Up @@ -157,6 +164,17 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSPopoverDelegate {
self?.diskMonitor.refreshDiskSpaceOnly()
}
}

// Show welcome window on first launch
if !UserDefaults.standard.bool(forKey: Self.hasSeenWelcomeWindowKey) {
welcomeWindowController = WelcomeWindowController(
diskMonitor: diskMonitor,
onDismiss: { [weak self] in
self?.completeWelcome()
}
)
welcomeWindowController?.show()
}
}

func updateMenuBarIcon() {
Expand Down Expand Up @@ -274,4 +292,23 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSPopoverDelegate {
}
NotificationCenter.default.post(name: .clearDiskPopoverDidClose, object: nil)
}

func completeWelcome() {
UserDefaults.standard.set(true, forKey: Self.hasSeenWelcomeWindowKey)
NSApp.setActivationPolicy(.accessory)
welcomeWindowController?.close()
welcomeWindowController = nil
DispatchQueue.main.asyncAfter(deadline: .now() + 0.15) { [weak self] in
self?.togglePopover()
}
}

func applicationShouldHandleReopen(_ sender: NSApplication, hasVisibleWindows flag: Bool) -> Bool {
if !UserDefaults.standard.bool(forKey: Self.hasSeenWelcomeWindowKey) {
welcomeWindowController?.show()
} else {
togglePopover()
}
return true
}
}
220 changes: 220 additions & 0 deletions Sources/ClearDisk/WelcomeWindow.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
import AppKit
import SwiftUI

// MARK: - Welcome Window Controller
final class WelcomeWindowController: NSObject, NSWindowDelegate {
private let window: NSWindow
var onDismiss: (() -> Void)?
private var isDismissed = false

init(diskMonitor: DiskMonitor, onDismiss: @escaping () -> Void) {
self.onDismiss = onDismiss
let window = NSWindow(
contentRect: NSRect(x: 0, y: 0, width: 540, height: 470),
styleMask: [.titled, .closable, .fullSizeContentView],
backing: .buffered,
defer: false
)
window.title = L("Welcome to ClearDisk")
window.titleVisibility = .hidden
window.titlebarAppearsTransparent = true
window.isReleasedWhenClosed = false
window.center()

self.window = window
super.init()

let rootView = WelcomeView(diskMonitor: diskMonitor) { [weak self] in
self?.dismiss()
}
window.contentViewController = NSHostingController(rootView: rootView)
window.delegate = self
}

func show() {
NSApp.activate(ignoringOtherApps: true)
window.makeKeyAndOrderFront(nil)
}

func close() {
window.close()
}

private func dismiss() {
guard !isDismissed else { return }
isDismissed = true
close()
onDismiss?()
}

func windowWillClose(_ notification: Notification) {
guard !isDismissed else { return }
isDismissed = true
onDismiss?()
}
}

// MARK: - Welcome View
struct WelcomeView: View {
@ObservedObject var diskMonitor: DiskMonitor
let onDismiss: () -> Void
@AppStorage(AppAppearance.storageKey) private var appearance: AppAppearance = .system

private var freeSpaceString: String {
let freeGB = Double(diskMonitor.freeSpace) / 1_073_741_824
if freeGB >= 1.0 {
return " \(String(format: "%.0f", freeGB))GB"
}
return " 158GB"
}

var body: some View {
VStack(spacing: 18) {
// Header
HStack(spacing: 16) {
if let icon = NSImage(named: "AppIcon") {
Image(nsImage: icon)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 54, height: 54)
} else {
Image(systemName: "externaldrive.badge.checkmark")
.font(.system(size: 42))
.foregroundColor(.accentColor)
}

VStack(alignment: .leading, spacing: 3) {
Text(L("Welcome to ClearDisk"))
.font(.system(size: 22, weight: .bold))

Text(L("Developer cache cleaner & disk space monitor"))
.font(.system(size: 13))
.foregroundColor(.secondary)
}
Spacer()
}

// Menu Bar Mockup with pointer
VStack(spacing: 8) {
HStack(spacing: 10) {
Image(systemName: "apple.logo")
.font(.system(size: 11))
.foregroundColor(.secondary)
Text("Finder")
.font(.system(size: 11, weight: .semibold))
.foregroundColor(.secondary)
Text("File Edit View Go Window")
.font(.system(size: 10))
.foregroundColor(.secondary.opacity(0.5))

Spacer()

// Simulated system status icons
Image(systemName: "wifi")
.font(.system(size: 10))
.foregroundColor(.secondary)
Image(systemName: "battery.100")
.font(.system(size: 10))
.foregroundColor(.secondary)

// ClearDisk Highlighted Status Item
HStack(spacing: 4) {
Image(systemName: "externaldrive.fill")
.font(.system(size: 10))
Text(freeSpaceString)
.font(.system(size: 10, weight: .bold))
}
.padding(.horizontal, 6)
.padding(.vertical, 3)
.background(
RoundedRectangle(cornerRadius: 5)
.fill(Color.accentColor.opacity(0.2))
.overlay(
RoundedRectangle(cornerRadius: 5)
.stroke(Color.accentColor, lineWidth: 1.5)
)
)
}
.padding(.horizontal, 12)
.padding(.vertical, 7)
.background(
RoundedRectangle(cornerRadius: 7)
.fill(Color(NSColor.controlBackgroundColor))
)

// Callout pointer to the menu bar item
HStack {
Spacer()
HStack(spacing: 5) {
Image(systemName: "arrow.up")
.font(.system(size: 11, weight: .bold))
Text(L("ClearDisk lives here in your menu bar!"))
.font(.system(size: 11, weight: .semibold))
}
.foregroundColor(.accentColor)
.padding(.trailing, 6)
}
}
.padding(12)
.background(
RoundedRectangle(cornerRadius: 9)
.fill(Color(NSColor.windowBackgroundColor).opacity(0.5))
.overlay(
RoundedRectangle(cornerRadius: 9)
.stroke(Color.secondary.opacity(0.18), lineWidth: 1)
)
)

// Info rows
VStack(alignment: .leading, spacing: 12) {
HStack(alignment: .top, spacing: 12) {
Image(systemName: "menubar.rectangle")
.font(.system(size: 16))
.foregroundColor(.accentColor)
.frame(width: 22)
VStack(alignment: .leading, spacing: 2) {
Text(L("Runs in your Menu Bar"))
.font(.system(size: 12, weight: .semibold))
Text(L("ClearDisk sits quietly in your menu bar without filling your Dock. Click its icon anytime to inspect caches and clean disk space."))
.font(.system(size: 11))
.foregroundColor(.secondary)
}
}

HStack(alignment: .top, spacing: 12) {
Image(systemName: "laptopcomputer")
.font(.system(size: 16))
.foregroundColor(.orange)
.frame(width: 22)
VStack(alignment: .leading, spacing: 2) {
Text(L("MacBook Notch & Hidden Icons"))
.font(.system(size: 12, weight: .semibold))
Text(L("If your menu bar is crowded or hidden behind a camera notch, you can also launch ClearDisk from Finder or Spotlight anytime to open it."))
.font(.system(size: 11))
.foregroundColor(.secondary)
}
}
}
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.horizontal, 4)

Spacer()

// Footer action button
HStack {
Spacer()
Button(action: onDismiss) {
Text(L("Got it! Go to Menu Bar"))
.font(.system(size: 13, weight: .semibold))
.frame(width: 220, height: 32)
}
.buttonStyle(.borderedProminent)
.keyboardShortcut(.defaultAction)
Spacer()
}
}
.padding(24)
.frame(width: 540, height: 470)
.preferredColorScheme(appearance.colorScheme)
}
}
8 changes: 7 additions & 1 deletion scripts/build_app.sh
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,13 @@ cat > "$APP_BUNDLE/Contents/Info.plist" << EOF
EOF

# Ad-hoc code sign the entire bundle (better Gatekeeper handling than linker-signed)
codesign --force --deep -s - "$APP_BUNDLE"
STAGE_DIR="$(mktemp -d /tmp/cleardisk-sign.XXXXXX)"
cp -R "$APP_BUNDLE" "$STAGE_DIR/$APP_NAME.app"
xattr -cr "$STAGE_DIR/$APP_NAME.app"
codesign --force --deep -s - "$STAGE_DIR/$APP_NAME.app"
rm -rf "$APP_BUNDLE"
ditto "$STAGE_DIR/$APP_NAME.app" "$APP_BUNDLE"
rm -rf "$STAGE_DIR"
echo "Code signed (ad-hoc)."

echo "Done! App bundle created at: $APP_BUNDLE"
Expand Down