POPSULE CAPSULE KIT (spec 1) https://popsule.app/kit.txt . Full spec with exact types: https://popsule.app/llms-full.txt A capsule is a tiny personal app for one person, running inside the Popsule app on their phone. You write it as ONE HTML file. Popsule runs it sealed and offline and gives it native features through window.popsule. Popsule checks every capsule; breaking a rule below means it is rejected with an error that says how to fix it. HOW TO DELIVER - With the Popsule connection (tools get_kit, plan_capsule, validate, preview, push_capsule): call validate and preview until there are no errors, then push_capsule. Do not paste the file. - Without it: reply with ONLY the finished HTML file, in ONE code block, nothing else. The person shares your reply to Popsule. 1. PLAN FIRST (pick 2 to 4 features) does: one plain sentence ("Splits a bill between friends, with tip") remembers: what it saves between opens, or nothing glance: what a widget should show, or no widget reminds: when to remind, or never quick: one thing worth doing without opening ("log a glass"), or none lifespan: "once" if it does one job and is done 2. THE FILE - One .html file under 1 MB. Must have a and the manifest (below) in <head>. - Inline <script> and <style> only. Images as data: URLs. System fonts. - Phone first (about 380 px wide), touch targets at least 44 px. - Support light and dark: style with @media (prefers-color-scheme: dark). Never hard-code a white page. - Load saved state on start and save on every change. - Plain words on screen. No developer terms (no "JSON", "API", "kv"). - Libraries: do not add <script src>. List them in the manifest "libraries" with an exact version from cdnjs.cloudflare.com or cdn.jsdelivr.net (like chart.js@4.4.1); Popsule packs them in. NOT ALLOWED (rejected) - fetch, XMLHttpRequest, WebSocket, EventSource, navigator.sendBeacon. - Loading anything from the internet: <script src>, stylesheet or font links, remote images, @import. - localStorage, sessionStorage, indexedDB, document.cookie. Use popsule.kv. - alert(), confirm(), prompt(). Build questions into the page. - window.open, changing top.location, <form action>, password fields. 3. THE MANIFEST (JSON, in <head>, before other scripts) <script type="application/popsule+json"> { "spec": 1, "name": "Grocery List", "does": "Keeps your grocery list, with a widget you can tick off", "capabilities": ["kv", "widget", "actions"] } </script> - spec: always 1. name: 1 to 40 characters (2 to 4 words). does: one sentence, up to 120 characters. - capabilities: every feature you use, from the list below. Using one you did not list fails. - Optional fields: widget, actions, data, shareIn, libraries, resets, dataVersion, lifespan. A field needs its capability (widget needs "widget", actions needs "actions", data needs "data", shareIn needs "share-in"), and "actions", "data", "share-in" need their field. - resets: [{ "kv": "glasses", "to": 0, "every": "day" }] ("day" = midnight, "week" = Monday) - lifespan: "once" for a capsule that does one job; it must then call popsule.done({ summary }). 4. FEATURES (capabilities) kv popsule.kv: remember things. get(key), set(key, value), delete(key), keys(), add(key, number) for counters, push(key, item) for lists. add/push merge across devices. data popsule.data: shared lists other capsules can use. Built-ins: tasks {text, done, list?, due?}, expenses {amount (whole cents), currency, date, note?, category?}, notes {title?, text}, contacts {name, phone?, email?}, events {title, start, end?}. Manifest: "data": { "reads": ["tasks"], "writes": ["tasks"] }. Calls: query, add, update, remove, watch. widget a native home or lock screen widget (section 5). remind popsule.remind.set({ id, title, body?, at?: "YYYY-MM-DDTHH:MM", every?: { daily: ["10:00", "16:00"] } or { weekly: { day: "sat", time: "10:00" } }, buttons?: [{ id, text, apply }] }). Max 8 reminders, each at most 6 times a day. notify popsule.notify(title, body): only while open. actions quick actions without opening (voice, shortcuts). Manifest: "actions": [{ "id": "add", "phrase": "Add {item} to Grocery List", "input": { "item": "text" }, "apply": { "kv": "items", "push": { "name": "{item}", "done": false } } }, { "id": "open", "phrase": "Show Grocery List", "open": true }] Each needs id, phrase, and either apply or "open": true. Every {name} in phrase must be in input. share-in accept photos, links, text or files shared from other apps; read popsule.launch.share. live lock screen while something runs: popsule.live.start({ id, title, until }), update, end. files popsule.files.pick({ accept }) and popsule.files.save({ name, type, text }). ai popsule.ai.complete({ prompt }): the only thing that leaves the phone. Only for text the person typed or shared in. Use only if the request truly needs words written or sorted. Also: popsule.supports("live") before relying on a feature; popsule.theme is "paper" or "charcoal". apply (in actions, reminder buttons, widget buttons) is one of: { "kv": KEY, "add": NUMBER } | { "kv": KEY, "push": VALUE } | { "kv": KEY, "set": VALUE } | { "collection": NAME, "add": { ...record } } {item}, {today}, {now} are filled in. 5. WIDGETS (drawn by Popsule from blocks; never HTML) "widget": { "sizes": ["small", "medium", "large", "lock"], "suggest": { "accent": "data", "surface": "paper", "density": "roomy" }, "blocks": [ { "type": "title", "text": "Water today" }, { "type": "value", "bind": { "kv": "glasses" }, "format": "{n} of 8", "accent": true }, { "type": "button", "id": "plus", "text": "+1", "tap": { "kv": "glasses", "add": 1 } } ] } - Blocks: title, value, detail, bar, ring (bind + max), segments (bind + total, max 14), rows (bind to a list, max 5, "tap": "toggle" flips done), countdown (bind or until), icon, button. - At most 4 blocks (small and lock show 3). Taps only work on blocks bound to saved data. - rows: { "type": "rows", "bind": { "kv": "items", "text": "name", "done": "done", "limit": 5 }, "tap": "toggle" } - accent by meaning: "data" blue (counts of their things), "time" orange (dates, deadlines), "file" green (one-time jobs), "seal" purple (kept things), "ink". surface: paper, tint, bold. - While open you may also send a picture of the moment: popsule.widget.update({ blocks: [...] }). 6. TESTS (required if it calculates money, dates, times or amounts) Put the maths in plain functions and add a test block. Popsule runs it when checking, never for the person. <script type="application/popsule+test"> test("splits evenly with tip", () => expect(eachPays(128, 4, 0.15)).toBe(36.8)); test("rounds up to the cent", () => expect(eachPays(100, 3, 0)).toBe(33.34)); </script> expect(x).toBe(y) / .toEqual(y) / .toBeCloseTo(y, digits). Work in whole cents for money. 7. GOOD HABITS - Read saved data defensively: old fields may be missing. - Every popsule.* call returns a Promise; calls may be made at startup. - Readable code: clear names, short functions. The person can open and read it in Popsule. 8. SMALLEST COMPLETE EXAMPLE <!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Water Tracker

0