v0.1.2 · open source · macOS 14+

Tiny native apps
for your Mac.

Write a nooklet in Swift. Nook builds it, runs it in the background and keeps it alive after you close the window.

$ curl -fsSL nook.fatihsen.dev/install.sh | sh

Universal · Apple Silicon and Intel · needs the Swift toolchain · also as .zip

The Nook manager with Clock, Now Playing and Mini Dock nooklets running on top of it

How it works

Three steps, no per-app setup.

A nooklet is a regular Swift package. Nook takes care of building, running and keeping it alive.

  1. Step 01

    Create

    Pick a folder and a name. Nook writes a starter package with nook.json and an entry file.

    nook.jsonPackage.swiftSources/app.swift
  2. Step 02

    Build

    Open it in Xcode, write SwiftUI or AppKit. Press Run and Nook builds a release binary.

    $ swift build -c releaseBuild complete! (4.2 s)
  3. Step 03

    Forget it

    launchd keeps it running, starts it at login and restarts it after a crash. Close Nook, it stays.

    Running · 14 MBuptime 2h 14m

Why Nook

Small on purpose.

  • Your design, all of it

    SwiftUI or AppKit, any shape. Nook draws no chrome around your panel.

  • Native and light

    Compiled binaries, no web view, no interpreter. A clock uses about 9 MB.

  • One crash stays one crash

    Every nooklet is its own process, supervised by launchd.

  • Outlives the manager

    Quit Nook and your nooklets keep running, even after a reboot.

  • Just a folder

    A nooklet is a Swift package. Put it in its own repo and share it.

  • Logs and errors built in

    Live logs, build output and a jump to the failing line in Xcode.

Write one

This is the whole nooklet.

A complete clock in fourteen lines. Everything else is up to you.

Sources/app.swift
import NookKit
import SwiftUI

@main
struct Clock: Nooklet {
    var body: some NookScene {
        FloatingPanel(position: .topTrailing) {
            TimelineView(.everyMinute) { context in
                Text(context.date, style: .time)
                    .font(.system(size: 56, weight: .semibold))
            }
        }
    }
}
Tuesday, 28 October

on your desktop, after one click on Run

For developers

Plain Swift. Plain macOS APIs.

NookKit gives you a panel and a log. Everything else is the Swift and AppKit you already know. A few recipes to start from:

Sources/app.swift
import AppKit
import NookKit
import SwiftUI

@main
struct MiniDock: Nooklet {
    var body: some NookScene {
        FloatingPanel(position: .bottom) {
            DockView()
        }
    }
}

struct DockView: View {
    @State private var apps = NSWorkspace.shared.runningApplications
        .filter { $0.activationPolicy == .regular }

    var body: some View {
        HStack(spacing: 6) {
            ForEach(apps, id: \.processIdentifier) { app in
                Image(nsImage: app.icon ?? NSImage())
                    .resizable()
                    .frame(width: 40, height: 40)
                    .onTapGesture { app.activate() }
            }
        }
        .padding(10)
        .background(.ultraThinMaterial, in: Capsule())
    }
}
  • FloatingPanel

    A borderless panel. Users drag it anywhere, Nook remembers where.

    FloatingPanel(
        level: .floating,
        position: .topTrailing
    ) { MyView() }
  • PanelLevel · PanelPosition

    Desktop, normal or floating. Seven starting spots.

    level: .desktop | .normal | .floating
    position: .center | .top | .bottom
        .topLeading | .topTrailing
        .bottomLeading | .bottomTrailing
  • writeLog

    Shows up live in the Log tab, with a timestamp.

    writeLog("now playing: \(title)")
  • nook.json

    The identity Nook reads. That's the whole manifest.

    {
      "id": "dev.you.mini-dock",
      "name": "Mini Dock",
      "product": "MiniDock"
    }

Make your first nooklet tonight.

Free and open source. One command to install.

$ curl -fsSL nook.fatihsen.dev/install.sh | sh