Tiny apps. Big personality.
Make little apps for your Mac with Svelte & your AI coding agent: a planner that fits your week, a recipe card covered in your notes, a tiny pond to stare at between meetings.
Install dotpop once. Make and share little apps as files. Each .pop carries its interface, saved work and imported files. Send a fresh template or a filled document to anyone with dotpop, without packaging and signing a separate Mac app.
You and your agent work on the same document. Check something off and your agent can read it. Ask your agent to add a row and it appears in your open window. Your documents stay yours to keep, copy and share. The iPhone app on TestFlight brings dotpop to your phone, with a Synced Library through your iCloud account. The Mac app includes the Synced Library.
Download for Mac · Download for iPhone · Explore the website · Make your first pop · Docs
macOS 15.2+ · Apple silicon · Local documents need no account
iPhone (iOS 26+) is in TestFlight; App Store release coming soon. It opens, edits and creates pops, includes the Popular templates, and imports from Files. Availability covers Synced Library and live collaboration. Synced Library uses CloudKit Production on Mac and matches TestFlight/App Store iPhones.
The iPhone app (iOS 26+, TestFlight) opens, edits and creates pops. Start from the Popular templates and keep your library in sync with your Mac through iCloud.
Download for iPhone · iOS 26+ · TestFlight
Pops can adapt to the phone with responsive layouts and play native haptics for deliberate actions. Make yours work on iPhone.
A pop is a live document with its own small interface. Your agent works on the same document through the pop CLI. Here's a fresh Pop Wins counter:
pop describe "My Wins.pop"Pop Wins: A little credit for the things you get done.
Version: 000000000000000100000011
Fields
["title"]: text (set)
["wins"]: counter (increment)
Commands (pop call PATH NAME --args JSON)
countOne — Give yourself credit for one little win.
args: {"additionalProperties":false,"properties":{},"type":"object"}
Value (rows retain their $id)
{
"title": "Pop wins today",
"wins": 0
}
pop call "My Wins.pop" countOne
pop get "My Wins.pop"{"title": "Pop wins today", "wins": 1}pop apply "My Wins.pop" --op '{"type":"increment","path":["wins"],"by":1}'
pop theme set "My Wins.pop" --values '{"accent":"#7050ad"}'The Pop Wins tutorial below defines countOne. Each app exposes its own actions through describe; apply and batch also let agents edit individual fields directly. With the window open, each increment appears immediately and the theme command turns the accent purple. Edit ▸ Undo can undo agent edits too. Text edits supplied with the version the agent read merge with what you typed since. The agent edits your local document through the pop CLI, including when the file is closed. Sync and live sharing are optional.
Sometimes you just want a packing list for one trip. A timer that looks like a tomato. A recipe card covered in your own notes. Those are good reasons to make software.
dotpop is built for tools with one clear job and a little character. Its templates include planners, music tools, photo and voice notebooks, games, and desktop companions. Or ask your agent for the thing you keep wishing existed. It can feel like a sheet of paper, a pocket calculator, or something you found in an old arcade.
Apps can declare camera and microphone capture and transcribe stored recordings on-device on a supported Mac. Capabilities and permissions explains the APIs, system consent, platform limits and future directions. Contributors should follow the template and app rebuild order so the catalog uses their latest changes.
- Local documents need no account or server. Choose iCloud when creating a pop to keep it in sync with your iPhone through your private iCloud storage, or choose On this Mac and pick a folder. Move an existing local pop with File ▸ Move to iCloud…. The iPhone app includes the Popular templates. Keep active files outside iCloud Drive and other synced folders.
- A
.popis one SQLite file: the interface, saved data and imported files travel together. Any SQLite tool can open it to look inside; edit it through dotpop or the CLI. Close a document before moving it in Finder. - Send a freshly built template so someone can start their own document. They need dotpop; opening the template asks where to save their writable copy. How to share a pop.
- Export a PNG or PDF to send an invoice, print a recipe, or drop a plan into a message.
- Use Share a Copy to send a saved copy, with its attachments and colors, while you keep working in the original.
- Add a widget for a pop with a widget view: on your Mac desktop, or for library pops on the iPhone Home Screen in TestFlight. Tap to open it; iPhone widgets also refresh in the background, when iOS allows.
- Choose Live collaboration in the Share popover. The owner signs in with Apple; anyone with the link can edit in dotpop on Mac/iPhone or in Chrome at share.dotpop.click. Chrome saves a downloadable browser copy. Stop ends the link and everyone keeps their copy. Live sharing explains how it works.
- Design for the phone as well as the desktop: pops can switch to a compact layout on iPhone and play native haptics, which stay silent on Mac and in browsers. Make it work on iPhone.
- Pin a pop Always on Top, or enter fullscreen in apps that enable it, including Checklist and Hourglass.
- Finder icons can show what's inside, such as a counter's total.
- Change a document's colors without touching its code, or edit the source to make a different tool.
Use Bun 1.4.2 or newer on macOS or Linux. Install the dotpop Mac app to open windows and pick your registered templates in its catalog; on both platforms the CLI renders template artwork and exports closed documents as PNG/PDF itself.
bunx @hitslop/pop@1.0.0 init weekend-kit
cd weekend-kit
bun installSetup asks what your pop should do, then offers to launch your agent: Codex, Claude Code, Gemini CLI, OpenCode, or another CLI. The starter already works: it includes a checklist, its icon and export views, your brief, and dotpop's guidance for agents. Try it before changing anything:
bun run dev # Open the browser preview; preview data resets on refreshAsk your agent to customize it, or open the folder in your agent yourself and try:
Read AGENTS.md, pop.ts, and the authoring/design skills in .agents/skills first. Turn this starter into "Weekend Kit," a packing list for short trips. Let me add items, group them by bag, check them off, and see how many are left. Make it feel like a pocket field notebook: warm paper, forest-green ink, and comfortable checkboxes. Include a clean printable packing list for PNG/PDF export. Keep it small, use dotpop's document APIs for saved data, and run the project checks when you're done.
Ask for changes as you go ("Make the checkboxes bigger") and see them in the preview. When it feels right, stop the preview, then:
bun run check # Check types and components
bun run build # Package it as dist/weekend-kit.pop
bun run register # Optional: add it to your dotpop templatesSend dist/weekend-kit.pop to someone with dotpop. They can open it and create their own packing list; the template contains no personal document state.
If you registered it, open dotpop, choose Weekend Kit under Templates, and select Create. Existing documents keep the app version they were made with, so make another version whenever you like.
A whole pop, from scratch below builds a complete app with its export view and Finder icon · Full tutorial · Edit a document with your agent
Run commands with bunx @hitslop/pop@1.0.0, or install it with bun install -g @hitslop/pop@1.0.0 and use pop. Generated projects have their own pinned bun run scripts.
| Task | Commands |
|---|---|
| Create and preview an app | init SOURCE, then the project's bun run check and bun run dev |
| Package and install a template | bun run build, bun run register |
| Make a writable document | create --from TEMPLATE --output DOCUMENT, then open DOCUMENT |
| Inspect and edit a writable document | schema, get, apply, batch |
| Discover and run an app's actions | describe DOCUMENT, then call DOCUMENT NAME --args JSON |
| Move data between documents | get, then one batch of insert operations |
| Customize and share colors, manage files | theme get/set/reset/export/import, attachments list/import/export |
| Export a PNG or PDF | export DOCUMENT --format FORMAT --output FILE (png or pdf) |
| Install agent guidance | bun install -g @hitslop/pop, then pop skills install; skills update with the global CLI |
Document creation and editing run on macOS and Linux. The CLI carries its own engine, which also exports a closed document as PNG/PDF and renders template artwork on both platforms; opening a window needs the Mac app. If you're unsure whether an edit happened, check with get before trying again. CLI workflows has complete examples.
Here’s where we’re heading:
- iPhone app. Open, edit and create pops, import from Files, and add Home Screen widgets in TestFlight on iOS 26+.
- Synced Library. Keep pops in sync between Mac and iPhone through iCloud.
- Live collaboration. Edit together through a link on Mac, iPhone or Chrome.
- Release the iPhone app. Bring iPhone to the App Store and qualify sharing on release devices.
- More platforms. Explore iPadOS, Android, Windows and Linux native apps.
- Ask from the window. Work with your agent beside your document, see what it changed, and undo its edits.
- Remix something you love. Make someone else’s pop your own, and keep your saved data as the app evolves.
- A store for tiny apps. Discover, publish and install community pops through an app-store-style catalog.
- More native capabilities. Calendars, reminders, notifications, Shortcuts and Siri, “Send to pop,” MIDI, and richer audio and transcription—with permissions for each pop.
See direction for priorities and ideas for the proposals. Remaining directions have no release dates yet.
What would you like to see? Tell us on Discord.
A .svelte file looks like a small web page: a <script>, some HTML and a <style>. That's the shape of a pop. You write class, onclick and {doc.current.wins} in ordinary markup, with no JSX or hooks, and the page updates when the document does.
- Styles are scoped to their component, so a pop can be all personality in plain CSS.
- Reactivity is part of the language. Read
doc.currentin markup and Svelte keeps it current, with no dependency arrays to get subtly wrong. - Transitions and motion are built in, for the hourglass that tips and the koi that drift.
- Mistakes are loud:
bun run checkcatches type and template errors before you build.
Svelte is compiled into the .pop with the app, so a document keeps the Svelte it was built with; the Mac app supplies the document engine and native services, not Svelte. Svelte is the supported integration today; other frameworks would need an adapter.
Pop Wins is a complete example: a name, a counter, and a button for giving yourself a little credit. Its window, Finder icon, and export all read the same document.
With the same Bun and Mac app setup above, create a fresh starter:
bunx @hitslop/pop@1.0.0 init pop-wins
cd pop-wins
bun installReplace the following starter files. Keep the generated package.json and tsconfig.json.
schema.ts defines the saved fields and what a new document starts with. Text is editable; a counter supports increments. It also holds the one action the button and your agent share:
import { defineDocument, s, type Tx } from "@hitslop/pop";
const schema = defineDocument({
title: s.text(),
wins: s.counter(),
}, { initial: { title: "Pop wins today" } });
export default schema;
/** Give yourself credit for one little win. */
export const countOne = (tx: Tx<typeof schema>) => tx.fields.wins.increment();Changing initial later doesn't overwrite saved wins.
pop.ts describes the app: its name, its starting window size and the colors people can change. The explicit slug identifies the app.
import { definePop } from "@hitslop/pop";
export default definePop({
slug: "pop-wins",
title: "Pop Wins",
description: "A little credit for the things you get done.",
author: { name: "You" },
categories: ["personal"],
window: { kind: "standard", width: 360, height: 360, fullscreenable: true },
theme: {
surface: "#fff7e6",
ink: "#382d24",
accent: "#28634b",
},
});Theme colors are CSS variables shared by the window, icon, and export. Change them through the theme panel or CLI, or share them as a theme file, without rebuilding. Changes stay with that document; the template keeps its defaults.
fullscreenable: true enables the Mac window's fullscreen control and View ▸ Enter Full Screen. See window behavior for other window types.
Replace the starter's commands.ts with:
import { defineCommands } from "@hitslop/pop/commands";
import schema, { countOne } from "./schema";
export default defineCommands(schema, {
countOne: {
description: "Give yourself credit for one little win.",
args: {},
run: ({ tx }) => countOne(tx),
},
});The button runs countOne with doc.change(countOne); an agent discovers the command with pop describe and runs it with pop call. Both apply the same action atomically, as one undo step. Commands are for agents, and their arguments are flat s.* scalars. Ordinary field handles remain useful for typing and other direct edits.
App.svelte is the whole interface. Import the definition from schema.ts and acquire the document with getDocument(schema) while the component initializes. Read doc.current, write through doc.fields, and let dotpop handle saving. See document access for child components and helpers.
<script lang="ts">
import { bindText, getDocument } from "@hitslop/pop/svelte";
import schema, { countOne } from "./schema";
import "./styles.css";
const doc = getDocument(schema);
</script>
<main class="wins-card">
<input aria-label="Counter title" use:bindText={doc.fields.title} />
<p class="wins-number" aria-live="polite">{doc.current.wins}</p>
<button onclick={() => doc.change(countOne)}>A little win +1</button>
</main>Preview.svelte reads its saved snapshot prop. Capture props also supply image URLs and the view's mode:
<script lang="ts">
import type { CaptureMode, CaptureProps } from "@hitslop/pop/svelte";
import type schema from "./schema";
let { snapshot }: CaptureProps<typeof schema> & { mode: CaptureMode } = $props();
</script>
<article class="wins-card">
<h1>{snapshot.title}</h1>
<p class="wins-number">{snapshot.wins}</p>
<p>Little things add up.</p>
</article>For this small counter, optional Export.svelte can reuse the complete preview:
<script lang="ts">
import type { CaptureMode, CaptureProps } from "@hitslop/pop/svelte";
import type schema from "./schema";
import Preview from "./Preview.svelte";
let props: CaptureProps<typeof schema> & { mode: CaptureMode } = $props();
</script>
<Preview {...props} />Icon.svelte:
<script lang="ts">
import type { CaptureMode, CaptureProps } from "@hitslop/pop/svelte";
import type schema from "./schema";
let { snapshot }: CaptureProps<typeof schema> & { mode: CaptureMode } = $props();
</script>
<div class="wins-icon">{snapshot.wins}</div>App.svelte is the editor. Separate components supply saved-state captures:
- Required
Preview.sveltesupplies a compact overview for Quick Look and in-app previews. - Optional
Export.sveltesupplies complete PNG/PDF output. Use normal document flow so long content can expand. Without it, export actions are hidden. - Required
Icon.sveltesupplies the document's dynamic Finder icon, centered on a transparent 512 × 512 canvas. Missing or failed artwork can still fall back to the preview or generic document icon. Widget.sveltesupplies small, medium and large widget images from saved data for Mac and for library pops on iPhone in TestFlight. The phone renders its own images, including in the background when iOS allows. Saves, watched file changes and startup freshness checks refresh them while dotpop runs; WidgetKit controls when the new image appears.
dotpop refreshes the Finder preview and icon when you close the document. On macOS and Linux, build and register render the template's initial artwork from starting values.
Capture views use Takumi: restricted rendering on Mac and Linux, and in-process rendering on iPhone. Mac and Linux support selectable vector PDF. Capture components must be synchronous. Quick Look reads the saved artwork.
See icons, previews, and exports for capture details and size limits.
Style the views in styles.css:
* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; color: var(--pop-ink); }
.wins-card {
padding: 28px;
text-align: center;
overflow-wrap: anywhere;
background: var(--pop-surface);
}
main.wins-card { min-height: 100%; }
.wins-card input {
width: 100%; padding: 10px; border: 0; border-radius: 8px;
font: inherit; color: inherit; background: transparent; text-align: center;
}
.wins-card h1 { margin: 0; font-size: 24px; }
.wins-number { margin: 20px 0; font-size: 72px; font-weight: 800; }
.wins-card button {
min-height: 44px; padding: 12px 20px; border: 0; border-radius: 12px;
font: inherit; color: var(--pop-surface); background: var(--pop-accent);
cursor: pointer;
}
.wins-card :focus-visible { outline: 3px solid var(--pop-accent); outline-offset: 4px; }
.wins-icon {
width: 440px; height: 440px; border-radius: 100px;
display: grid; place-items: center; font-size: 150px; font-weight: 800;
color: var(--pop-accent); background: var(--pop-surface);
}The builder finds each part by its filename: pop.ts, schema.ts and App.svelte, plus
required Preview.svelte and Icon.svelte, optional Export.svelte and commands.ts. Stylesheets are imported from
components, as App.svelte does above. See the
runtime reference for the app interface.
bun run check # Check the types and Svelte component
bun run dev # Try it in the browser; preview data resets on refreshThe preview reloads as you edit. Stop it before continuing.
bun run build # Create dist/pop-wins.pop (works on Linux too)
bun run register # Render its preview and icon, then add Pop Wins to your catalogIn dotpop, choose Pop Wins → Create, then save your document as My Wins.pop. Change its title, add some wins, export a PNG/PDF, and close and reopen it to see the saved values. Check its refreshed icon in Finder, too.
Try the commands from You and your agent, one document on your saved document. pop theme reset "My Wins.pop" restores the template's colors.
A pop is a SQLite file holding a Svelte app and a Loro document. Loro's CRDTs (conflict-free replicated data types) merge edits to text, lists and counters. One Rust core owns the document, the file and every edit, so the Mac app and the CLI change a pop the same way.
| Layer | Built with | Role |
|---|---|---|
| Document engine | Rust, Loro | Merging text, lists and counters; atomic edits; the single owner of the document |
| File | SQLite | The .pop: app, assets, artwork and saved state in one database |
| Mac app | Swift, SwiftUI, AppKit, WebKit | Windows, catalog, Quick Look and PNG/PDF export; a thin native layer over the Rust core via UniFFI |
| Pop interface | Svelte 5, TypeScript | The authored app, rendered from document snapshots; the page holds no CRDT |
| Author SDK and CLI | @hitslop/pop, Bun |
Schemas, pop dev (Vite with a native Rust owner), build, edit and export |
| Contracts | Rust serde, ts-rs, UniFFI | Rust owns internal wires and app acceptance; generated TypeScript and typed Swift |
page (WebKit) ──▶ Swift façade ──▶ Rust owner ──▶ .pop
pop CLI ───────────────────────▶ Rust owner (the open window's, or the closed file's lock)
- App and agent edits take one path, so an open window updates live and a closed file is edited without starting WebKit.
- Text merges character by character.
- A failed save keeps the document open and shows a retry.
app markers, catalog columns, definition_json one immutable declaration
assets key, media_type, encoding, size, bytes ui.js, ui.css, commands.js, media
artwork name (preview | icon), png Quick Look preview and Finder icon
-- populated when you create and edit a document
document uuid the document's identity
history seq, bytes saved Loro state: a snapshot, then the edits saved since
share room, endpoint room and endpoint for live sharing; empty when unshared
attachments id (SHA-256), media_type, bytes files you import
Any SQLite tool shows these tables. history holds Loro bytes rather than rows, so change a pop's data through dotpop or the CLI, not SQL. All seven tables exist in templates and documents. A template has app assets, optional artwork and a seed Loro snapshot; creating a document copies it and adds its own identity. The file's SQLite application_id and user_version mark its format, so a newer file is refused rather than rewritten.
Architecture · Engineering contract · Compatibility
Use the Bun version pinned in package.json, Xcode, and XcodeGen on macOS:
bun install --frozen-lockfile
bun install --cwd apps/landing --frozen-lockfile
bun run build
bun run verify --all --native
bun pop dev pop/quick-checklistDevelopment covers setup and adding templates, and the documentation index covers how the platform works.
Made something wonderfully specific? Show us your pop. Share a screenshot, swap ideas, or help someone make their first pop app.
Other projects exploring personal software and interactive documents:
- Your executable is a SQLite database and Actually Queryable Executables: Farid Zakaria's SELF, a program and its state in one SQLite file. It inspired the
.popfile. - Hyperclay: HTML files you can reshape in place, where the live document is the source of truth.
- Capsule: documents that run like apps, shared as single SQLite files.
- bento: an office suite that fits in one self-contained HTML file.
- Decker: HyperCard-style decks of interactive cards.
- TiddlyWiki: a personal wiki that lives in one HTML file.
- uapp
Older inspirations: HyperCard and Smalltalk put making your own tools within reach. Winamp's skin system was a major inspiration for dotpop's look and feel.
Release builds of the Mac app enable Firebase Analytics and Crashlytics. They send launch, open, create (source), duplicate and export (format) events; operation breadcrumbs; failure records with fixed operation, classification, reason and format fields; crash reports; and Firebase's standard app and device data.
Our custom event, breadcrumb and failure fields do not include document contents or file paths. Automatic crash reports contain diagnostic information collected by the Firebase SDK. Collection is enabled in release builds with no in-app toggle; Debug builds and tests do not upload telemetry.
See Privacy for the public disclosure.
MIT © 2026 dotpop contributors. See LICENSE and third-party notices.


