Imported from arach/lattices (
products/blink/AGENTS.md). Install upstream withnpx skills add arach/lattices --skill blink. Copyright stays with the author.
AGENTS.md
This tree is the Blink product inside the Lattices monorepo (products/blink).
Keep Blink.app, NoteStore, and the panel runtime separate from the Lattices
menu bar app even though they share this repository and website.
Blink in 60 seconds
Blink is a native macOS spatial note-taking app. It is not a document library with detachable windows: the note is the window, and the desktop is the workspace.
The human loop is capture → place → recall:
- Create or find a note from the menubar popover, command palette, or global new-note hotkey.
- The note opens as its one floating
NSPanel; reopening it focuses that panel rather than creating a duplicate. - Move, resize, shade, or focus panels to arrange a working set. Blink restores each note to the same place.
- Edit and read in the same panel. The menubar popover and command palette are launchers, not alternate document windows.
Blink is an LSUIElement menubar-only app: floating note panels + menubar
popover + command palette, with no Dock icon and no main/library window. The v1
Tauri app remains at tag v1-final; port lessons and behavior, never its code.
When operating Blink rather than changing its code, use the blink CLI for
notes and edit config.json for behavior/appearance. Drive the GUI only when a
task specifically requires the live visual surface.
State model
The markdown files are the durable truth for notes. Runtime note state converges through one path:
panel edits / popover actions → PanelManager / AppModel → NoteStore → atomic .md
CLI / external writes → same .md → directory watcher → NoteStore.reconcile()
NoteStore notifications → AppModel snapshot → popover / palette / open panels
- Notes live at
~/Library/Application Support/Blink/Notes/<id>.md, whereidis a unique title-derived slug. Body and metadata travel together in each markdown file; metadata is YAML frontmatter. Unknown frontmatter must survive round trips verbatim. UUIDv5 identity is derived only for v1 compatibility. NoteStoreowns note mutations and the in-memory index.AppModelmirrors it for UI surfaces; it is not a second store.PanelManagerowns live windows, pending editor text, one-panel-per-note identity, geometry, and save flushing. Do not bypass it for panel lifecycle.- Exact per-device panel frames (
NSWindowframe autosave), the open-panel set, and each note's read/edit mode live inUserDefaults. Portableblink.slotfrontmatter is placement intent; the autosaved frame is the device-specific position.NotePanelsuspends frame autosave while physics owns a drag/fling/shade and persists the unshaded resting frame once settled. - External writers are first-class. The directory watcher calls
NoteStore.reconcile(), producing the same notifications as in-app edits. config.jsonowns behavior and appearance and hot-applies independently of note state.BLINK_HOMEoverrides the file-backed root (notes, config, and attachments) for tests/agents; it does not redirectUserDefaults. The Mac peer host is disabled wheneverBLINK_HOMEis set so sandbox notes can never inherit the production device identity or trust list.
Source-of-truth order
When documents disagree, use this order:
- Current code and tests describe shipped behavior.
- This file's hard requirements are non-regression constraints.
docs/cli.md,docs/config.md,docs/workspaces.md, anddocs/release.mddescribe shipped agent-facing or release surfaces.docs/placement.mdis mixed-status: the grid primitive is shipped; its live-plane collision, scene, and tidy semantics are proposed.docs/v2-plan.md,docs/v2-ui-map.md, anddocs/notes-representation.mdare local, untracked design references absent from a fresh clone. They contain intent and history and may lag the code.docs/agent-api.mdanddocs/agent-integration.mdare proposals, not implemented surfaces.docs/functionality-v1.mdis a local, untracked v1 inventory and scope/lessons donor, not a description of the v2 codebase.
Other useful references:
- UI studies / visual spec:
design/studio(tracked shared-Studio app;bun dev→localhost:3060/studio). - The CLI operates on the same files as the app:
blink ls/cat/new/present/type/write/search/rm/path/workspace; seedocs/cli.md. - Agents configure Blink by editing
~/Library/Application Support/Blink/config.json, not by driving Settings; seedocs/config.md. blink workspacedefines a named treatment inconfig.json; membership is oneblink.workspacefrontmatter key. Seedocs/workspaces.md. Brands are generic treatments—never hardcode a specific product identity into Blink.
Where changes belong
Sources/BlinkApp— AppDelegate/status item/popover, command palette, AppModel, PanelManager, NotePanel, config hot reload, and WebBridge.Sources/BlinkCore— pure Swift with no AppKit: note model and identity, frontmatter, atomic file storage, NoteStore, treatments, workspaces, and grid math / panel physics.Sources/BlinkCLI—swift-argument-parserCLI over BlinkCore.BlinkPathskeeps the app and CLI on the same locations.Sources/BlinkPeer— encrypted Multipeer Connectivity discovery, pairing, and snapshot transport shared by macOS and iOS. Bonjour advertises presence; new devices request explicit Mac approval inside an encrypted session, and remembered device access remains revocable from the Mac. Both sides keep their private identity and trust material in non-synchronizing Keychain storage, never preferences; the iOS seed and note cache do not travel in device backups.apps/ios— XcodeGen source for the read-only, offline-first iPhone/iPad companion. It consumes the local BlinkCore and BlinkPeer package products.Tests/BlinkCoreTests— narrow tests for storage, frontmatter, identity, workspaces, grid placement, and panel physics; run the matching suite for domain changes.web/editor— vanilla CodeMirror 6 (no React), bundled into onedist/editor.htmland hosted by each note panel. Load-bearing bridge contract:ready/ user-onlycontentChanged/saveRequested→ native;setContent/getContent/focus← native.- Generic panel or web-bridge primitives should be shaped for eventual HudsonKit upstreaming once proven here.
Commands
swift build # resolves pinned Hudson (BLINK_HUDSON_SOURCE=path for local checkout)
swift test # BlinkCore tests
swift build --product blink # notes CLI (docs/cli.md)
(cd web/editor && bun install && bun run build) # editor bundle
./scripts/run-app.sh --debug --restart # bundle dist/Blink.app + launch
(cd apps/ios && xcodegen generate) # generate BlinkMobile.xcodeproj
Hard requirements (inherited from v1 bugs)
- Flush pending saves on note-switch, panel-close, and quit. Never trust a debounce alone.
- All note writes are atomic: temp file +
fsync+ rename. - Note metadata lives in the markdown file's frontmatter, never only in a side index. Preserve foreign frontmatter verbatim.
- One panel per note—opening an open note focuses it.
- Panel geometry persists per note and restores exactly; never scramble a layout.
- Note mutations flow through
NoteStore→ notifications →AppModel; never mutate one UI surface as the source of truth. contentChangedfires on user edits only. ProgrammaticsetContentmust never echo (the v1 cross-note corruption bug).
Conventions
AGENTS.mdandCLAUDE.mdmirror the same instructions (apart from their title); keep them in sync.- For larger initiatives/features, survey the ecosystem first: check HudsonKit
(
../../../hudson/packages/native/apple/HudsonKit) for reusable primitives and talkie / scout (../../../talkie,../../../openscout) for proven patterns. Small widgets/tweaks do not need the survey. This tree is already inside lattices atproducts/blink. - Log prefix
[BLINK]via HudLogger (HudsonObservability). - Hyper = ⌃⌥⇧⌘. Global hotkeys use Carbon
RegisterEventHotKeyand do not require Accessibility permission. - User-visible name is "Blink".
- GUI verification: LSUIElement apps are invisible to System Events' "visible
processes." Activate Blink before synthesized keystrokes, and use
AXRaisefor restored panels.