The ChatOSS app pattern
For most project- or collection-based apps, copy ChatOSS Kanban's three-column architecture:
- Left — project library: create/select/rename/delete projects or durable workspaces.
- Middle — project AI chat: show a model picker from
chat.listModels(), keep chat history per project, include a fresh selected-project snapshot in the system message, and expose complete CRUD tools for everything the user can change. - Right — app canvas: the product-specific surface (sticky notes, board, editor, diagram, preview, etc.). Usually this is the only column that should fundamentally change between apps.
Small single-purpose utilities may omit the pattern, but it is the default. The canonical example in the in-app Docs app is Sticky Notes — use it as the visual/architectural reference.
Design rules
- Style from the platform classes and tokens (
.app-shell,.btn,.list-row,var(--surface-2), …) — see Styling & theming. Never redefine:rootcolour tokens and never use@media (prefers-color-scheme: dark); the host stampsdata-themeand both themes come for free. - Never use
confirm()/alert()/prompt()— the sandbox has noallow-modals, soconfirm()silently returnsfalseand every destructive action guarded by it becomes a no-op. Build the confirmation with.overlay/.modal. → The runtime bridge - Stream AI output with
onToken— never a spinner followed by a wall of text. - Disable buttons while work is running; show a short status line.
- The app must work by hand too — treat AI as an alternative interface to the same data.
- Agent tools must be complete and orthogonal: create/read/update/delete plus any domain actions the UI supports. Return truthful result strings and never let the model claim an unconfirmed mutation.
Checklist before delivering
app.jsonis valid JSON with id (lowercase reverse-DNS), name, version, description;capabilitieshas ONLY what the code uses. → The manifest- Entry HTML references your files with plain relative paths.
- Every
window.chatosscall is awaited; every gated call handles null/denied. - State persists via
scopedDataand restores on launch. - If the app has chat, the user can choose a model from
chat.listModels()and the choice persists per project/workspace. → AI chat - Project/collection apps follow library → CRUD agent → product canvas, and the agent can perform every mutation available by hand.
- The UI is built from the platform classes + tokens; no
:rootcolour overrides, noprefers-color-schemerule. Check it in BOTH ChatOSS Light and Dark. - No
confirm()/alert()/prompt()/window.print()anywhere — they do not work in the sandbox. Destructive actions use an in-frame.modal. - An icon file exists and is named in
"icon". - If the app exports documents (PDF/Word/Excel/PowerPoint/image): use the OS
documentsAPI (no vendored library, nowindow.print()— it's blocked in the sandbox). Declare"documents"(+"fileAccess"if you usedocuments.save()). For PDF/DOCX with any formatting (bold, italic, headings, lists), pass the content ascontent.blocks(structured blocks + styled runs) — NOT plainparagraphs/text, which strips formatting. → Documents - If the app produces files of its own (reports, exports, transcripts), they go in
drive— not buried in a database row and not begging the user for a folder. The user's own project files stay infiles. → Two filesystems - If you're building outside ChatOSS: zip the files (they must sit at the zip root), rename the zip to
yourapp.aip, and drop it on the ChatOSS Apps app. - If you ARE inside ChatOSS Code: publish the finished app with the
publish_apptool — the user gets a Publish / Cancel prompt, and on Publish the folder is zipped, validated, and installed.