diff --git a/slab/menubar-swift/bin/verify-solo-bundle.sh b/slab/menubar-swift/bin/verify-solo-bundle.sh index afff75971..74f48c2cb 100755 --- a/slab/menubar-swift/bin/verify-solo-bundle.sh +++ b/slab/menubar-swift/bin/verify-solo-bundle.sh @@ -72,8 +72,21 @@ else printf ' %s\n' ${LEAKS} fi +# ── Universal binary ────────────────────────────────────────────────────── +# Info.plist advertises macOS 11, which includes Intel Macs. An arm64-only +# binary there does not degrade, it refuses to launch. +SLICES="$(lipo -archs "${BIN}" 2>/dev/null)" +if [[ "${SLICES}" == *arm64* && "${SLICES}" == *x86_64* ]]; then + pass "universal (${SLICES})" +else + fail "not universal — got '${SLICES}'" +fi + # ── Linked libraries are all system ─────────────────────────────────────── -NONSYSTEM="$(otool -L "${BIN}" 2>/dev/null | tail -n +2 | awk '{print $1}' \ +# A fat binary prints one " (architecture x86_64):" header per slice, so +# dropping just the first line is not enough — the header repeats. Dependency +# lines are the indented ones; the headers are not. +NONSYSTEM="$(otool -L "${BIN}" 2>/dev/null | grep -E '^[[:space:]]' | awk '{print $1}' \ | grep -vE '^(/usr/lib/|/System/)' | sort -u)" if [[ -z "${NONSYSTEM}" ]]; then pass "links only system libraries" diff --git a/slab/menubar-swift/release-solo.sh b/slab/menubar-swift/release-solo.sh index 8f599d9d4..bc6052fec 100755 --- a/slab/menubar-swift/release-solo.sh +++ b/slab/menubar-swift/release-solo.sh @@ -50,12 +50,21 @@ for arg in "$@"; do done # ── 1. Build ────────────────────────────────────────────────────────────── -say "building release binary" +# Universal, not native. `swift build -c release` on this Mac produces an +# arm64-only binary that an Intel Mac cannot launch at all — and the Info.plist +# advertises macOS 11, which includes plenty of Intel machines. The studio +# build can be native because every Mac here is Apple silicon; a download +# cannot make that assumption about anybody. +say "building release binary (universal)" cd "${SCRIPT_DIR}" -swift build -c release >/dev/null -BUILT="$(swift build -c release --show-bin-path)/slab-menubar-swift" +ARCHS=(--arch arm64 --arch x86_64) +swift build -c release "${ARCHS[@]}" >/dev/null +BUILT="$(swift build -c release "${ARCHS[@]}" --show-bin-path)/slab-menubar-swift" [[ -x "${BUILT}" ]] || { err "no binary at ${BUILT}"; exit 1; } -ok "built $(du -h "${BUILT}" | awk '{print $1}')" +SLICES="$(lipo -archs "${BUILT}")" +[[ "${SLICES}" == *arm64* && "${SLICES}" == *x86_64* ]] \ + || { err "expected a universal binary, got: ${SLICES}"; exit 1; } +ok "built $(du -h "${BUILT}" | awk '{print $1}') (${SLICES})" VERSION="$(/usr/libexec/PlistBuddy -c "Print CFBundleShortVersionString" "${SCRIPT_DIR}/Info.plist")" diff --git a/system/public/slab/index.html b/system/public/slab/index.html index 11f7c04d6..9f8311c15 100644 --- a/system/public/slab/index.html +++ b/system/public/slab/index.html @@ -264,6 +264,56 @@ max-width: 62ch; } + /* ── Download ─────────────────────────────────────────────────────── */ + .download { margin: 4px 0 26px; } + .dl-button { + display: inline-flex; + flex-direction: column; + align-items: center; + gap: 2px; + padding: 16px 34px; + border-radius: 14px; + text-decoration: none; + color: #fff; + background: linear-gradient(180deg, #7a4cc4 0%, #5b34a0 100%); + box-shadow: + 0 1px 0 rgba(255,255,255,0.22) inset, + 0 10px 28px -10px rgba(122, 76, 196, 0.9), + 0 0 0 1px rgba(255,255,255,0.08); + transition: transform 120ms var(--ease, ease), box-shadow 120ms ease; + } + .dl-button:hover { + transform: translateY(-1px); + box-shadow: + 0 1px 0 rgba(255,255,255,0.28) inset, + 0 14px 34px -10px rgba(122, 76, 196, 1), + 0 0 0 1px rgba(255,255,255,0.14); + } + .dl-button:active { transform: translateY(1px); } + .dl-main { + font-family: var(--mono); + font-size: 19px; + letter-spacing: 0.01em; + } + .dl-meta { + font-size: 12.5px; + opacity: 0.82; + font-variant-numeric: tabular-nums; + } + .dl-fine { + font-size: 13.5px; + color: var(--ink-dim); + margin: 12px 0 0; + line-height: 1.55; + max-width: 52ch; + } + .dl-hash { + font-family: var(--mono); + font-size: 11px; + word-break: break-all; + opacity: 0.75; + } + /* ── Recent changes ───────────────────────────────────────────────── */ #recent-commits { list-style: none; padding: 0; margin: 0; max-width: 72ch; } #recent-commits li { @@ -399,14 +449,29 @@

getting it

-

Slab is not packaged yet.

-

There is no download, no App Store listing, no one-line installer. It lives inside the Aesthetic Computer monorepo and is built from source on each Mac. If that does not put you off:

+

Free, signed, and about seven megabytes.

+ +
+ + Download Slab + for macOS + +

+ macOS 11 or later, Intel and Apple Silicon. Notarized by Apple, so it opens without a warning. +
+ +

+
+ +

Drag it to Applications and launch it. On first run it asks once whether it may add its hooks to your Claude Code settings, which is how it learns when a session starts, stops, or wants you. It backs up that file before touching it, and the Slab menu can take the hooks back out again.

+

macOS will ask for Accessibility the first time Slab moves a window, and for Screen Recording only if you ask it for a frame. Both live in System Settings under Privacy & Security, and Slab tells you when it needs one.

+

This is the first public build, and it is honest about its age. The fleet half of Slab, the parts that reach across machines, stays quiet until it is configured. If something is wrong, the source is right there and so am I.

+ +

Rather build it yourself? The repo is the studio version, symlinked so an edit lands immediately:

git clone https://github.com/whistlegraph/aesthetic-computer.git
 cd aesthetic-computer/slab
 ./install.sh --prompt-host        # hooks + session markers; no audio, no sudo
 ./menubar-swift/install.sh        # builds and launches the menu bar
-

It wants macOS on Apple silicon, the Xcode command line tools, and Claude Code. Codex and Easel sessions are tracked too. The installer merges hooks into ~/.claude/settings.json, installs a launchd agent, and asks for Accessibility so it can move Terminal windows. Screen Recording is only requested the first time something asks for a frame. ./uninstall.sh takes it all back out.

-

The lid ambient, the fleet ledger and the multi-Mac navigation are tuned to one studio's machines right now. Expect to read a shell script or two. A build you can just download is the next thing, and interest is what moves it up the list.

view source · read the README

@@ -464,6 +529,27 @@ cd aesthetic-computer/slab poll(); })(); + // The download button reads manifest.json rather than hardcoding a + // version, so publishing a new build is one file to edit and the page + // follows. The href in the markup is the fallback if this never lands. + (function download() { + const button = document.getElementById('dl'); + const meta = document.getElementById('dl-meta'); + const hash = document.getElementById('dl-hash'); + if (!button) return; + fetch('/slab/manifest.json', { cache: 'no-store' }) + .then((r) => r.ok ? r.json() : Promise.reject(new Error('HTTP ' + r.status))) + .then((data) => { + const latest = (data.versions || []).find((v) => v.version === data.latest); + if (!latest || !latest.dmg) return; + button.href = latest.dmg; + const mb = (latest.size / (1024 * 1024)).toFixed(1); + if (meta) meta.textContent = `${latest.version} · ${mb} MB · macOS`; + if (hash && latest.sha256) hash.textContent = `sha256 ${latest.sha256}`; + }) + .catch(() => { /* the markup's own href still works */ }); + })(); + // Recent changes: the last commits that touched slab/, from the same // endpoint the Menu Band page uses. (function recentChanges() { diff --git a/system/public/slab/manifest.json b/system/public/slab/manifest.json new file mode 100644 index 000000000..816bbaf6d --- /dev/null +++ b/system/public/slab/manifest.json @@ -0,0 +1,14 @@ +{ + "_comment": "Release index for the Slab direct download, served by lith at /slab/manifest.json and read by the download button on /slab. The DMG itself lives on assets.aesthetic.computer; this file is the bookkeeping that points at it. Written alongside slab/menubar-swift/release-solo.sh — see slab/README.md. Keep older versions in versions[] so an archival link never dies.", + "latest": "1.0", + "versions": [ + { + "version": "1.0", + "dmg": "https://assets.aesthetic.computer/slab/Slab-1.0.dmg", + "size": 6924404, + "sha256": "5db38b4faf957e6669a3c5dd18c10e05dfe1ea29cd37e5fbd87178f83d3b9365", + "releasedAt": "2026-09-16", + "requirements": "macOS 11+ · Universal (Intel + Apple Silicon)" + } + ] +}