user app publishing system — build, serve, and use personal apps in workspace panels

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-27 17:23:24 +00:00
co-authored by Claude Opus 4.6
parent 4d30672adb
commit fd4d77a389
17 changed files with 873 additions and 48 deletions
@@ -0,0 +1,65 @@
# AGENTS.md — Simple App Template
## Project Structure
```
src/
├── index.ts # Bun server entry (routes, HMR)
├── index.html # HTML entry point
├── frontend.tsx # React root mount
├── index.css # Tailwind + global styles
├── App.tsx # Main React component
└── components/ # UI components
build.ts # Build script (bun run build → dist/)
.officerdev/
└── meta.json # Project metadata
```
## Tech Stack
- **Runtime**: Bun
- **Language**: TypeScript (strict)
- **Framework**: React 19
- **Styling**: Tailwind CSS v4
- **UI**: shadcn/ui components (Radix UI primitives)
- **Icons**: lucide-react
## Build System
- `bun dev` — starts dev server with HMR
- `bun run build` — produces `dist/` via `build.ts`
- Build uses `bun-plugin-tailwind` for CSS processing
- Output: minified JS/CSS bundles + HTML in `dist/`
## Publishing
This project can be published as a standalone app in officer.dev.
**Version**: Set in `package.json``version` field. Bump before republishing.
**Icon**: When publishing, choose from these available lucide icon names:
Activity, Airplay, Archive, BarChart3, Bell, Blocks, BookOpen, Box, BrainCircuit,
Calendar, Camera, ChartPie, CircleDot, Cloud, Code, Compass, CreditCard, Database,
FileText, Folder, Gamepad2, Globe, Heart, Home, Image, Inbox, Layers, Layout,
LineChart, Link, List, Mail, Map, MessageCircle, Monitor, Music, Palette, PenTool,
Play, Puzzle, Radio, Rocket, Search, Settings, ShoppingCart, Star, Sun, Table,
Terminal, Timer, Users, Wand2, Zap
## Iframe Constraints
Published apps run inside an iframe in officer.dev workspace panels:
- **Auth token injection**: The iframe URL includes a `?token=` param. A script is injected into the HTML `<head>` that automatically adds `Authorization: Bearer <token>` to all fetch/XHR requests.
- **Path rewriting**: All absolute paths (`/api/...`, `/assets/...`) are rewritten to go through the serve proxy at `/api/app-serve/{slug}/`. Relative paths work as-is.
- **No direct DOM access**: The app cannot access the parent frame.
- **API access**: Use `fetch('/api/...')` — the injected script rewrites paths and adds auth headers automatically.
## Coding Conventions
- Functional components, arrow functions
- No default exports — use named exports
- Strict TypeScript, no `any`
- Semicolons, single quotes (JS), double quotes (JSX)
- 2-space indentation
- Prefer composition over inheritance
- Keep components small and focused