diff --git a/apps/test123/20260227-072646/.gitignore b/apps/test123/20260227-072646/.gitignore deleted file mode 100644 index 74c83bb..0000000 --- a/apps/test123/20260227-072646/.gitignore +++ /dev/null @@ -1,39 +0,0 @@ -# dependencies (bun install) -node_modules - -# dev data -data/ - -# honk honk -.claude/settings.local.json - -# output -dist/ -*.tgz - -# code coverage -coverage -*.lcov - -# logs -logs -_.log -report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json - -# dotenv environment variable files -.env -.env.development.local -.env.test.local -.env.production.local -.env.local - -# caches -.eslintcache -.cache -*.tsbuildinfo - -# IntelliJ based IDEs -.idea - -# Finder (MacOS) folder config -.DS_Store diff --git a/apps/test123/20260227-072646/.npmrc b/apps/test123/20260227-072646/.npmrc deleted file mode 100644 index 6c57d5c..0000000 --- a/apps/test123/20260227-072646/.npmrc +++ /dev/null @@ -1 +0,0 @@ -registry=https://npm.nose.space diff --git a/apps/test123/20260227-072646/CLAUDE.md b/apps/test123/20260227-072646/CLAUDE.md deleted file mode 100644 index 84d6dfb..0000000 --- a/apps/test123/20260227-072646/CLAUDE.md +++ /dev/null @@ -1,307 +0,0 @@ -# Toes - Guide to Writing Apps - -Toes manages and runs web apps, each on its own port. - -Apps are server-rendered TypeScript using **Hype** (wraps Hono) and **Forge** (CSS-in-JS). - -Runtime is **Bun**. - -## Required Components - -Every toes app/tool must have: - -1. **`.npmrc`** pointing to `registry=https://npm.nose.space` (the private registry for `@because/*` packages) -2. **`package.json`** with a `scripts.toes` entry (this is how toes discovers and runs apps) -3. **HTTP `GET /ok`** returning 200 (health check endpoint — toes polls this every 30s and restarts unresponsive apps) - -## App vs Tool - -An **app** shows in the sidebar and opens in its own browser tab. - -A **tool** renders as a tab inside the dashboard (in an iframe). It receives `?app=` to know the selected app. The only code difference is `"toes": { "tool": true }` in package.json and some extra imports from `@because/toes`. - -## Required Files - -Every app needs `.npmrc`, `tsconfig.json`, `package.json`, and `index.tsx`. - -**.npmrc** -- always this exact content: -``` -registry=https://npm.nose.space -``` - -**tsconfig.json** -- use exactly, do not improvise: -```json -{ - "compilerOptions": { - "lib": ["ESNext"], - "target": "ESNext", - "module": "Preserve", - "moduleDetection": "force", - "jsx": "react-jsx", - "jsxImportSource": "hono/jsx", - "allowJs": true, - "moduleResolution": "bundler", - "allowImportingTsExtensions": true, - "verbatimModuleSyntax": true, - "noEmit": true, - "strict": true, - "skipLibCheck": true, - "noFallthroughCasesInSwitch": true, - "noUncheckedIndexedAccess": true, - "noImplicitOverride": true, - "noUnusedLocals": false, - "noUnusedParameters": false, - "noPropertyAccessFromIndexSignature": false, - "baseUrl": ".", - "paths": { - "$*": ["src/server/*"], - "#*": ["src/client/*"], - "@*": ["src/shared/*"] - } - } -} -``` - -**package.json** for an app: -```json -{ - "name": "my-app", - "private": true, - "module": "index.tsx", - "type": "module", - "scripts": { "toes": "bun run --watch index.tsx" }, - "toes": { "icon": "🖥️" }, - "dependencies": { - "@because/forge": "*", - "@because/hype": "*" - }, - "devDependencies": { "@types/bun": "latest" } -} -``` - -For a **tool**, add `@because/toes` to dependencies and set `"tool": true` (or a string for a custom tab label like `"tool": ".env"`): -```json -{ - "toes": { "icon": "🔧", "tool": true }, - "dependencies": { - "@because/forge": "*", - "@because/hype": "*", - "@because/toes": "*" - } -} -``` - -## Hype - -Hype wraps Hono. It adds `app.defaults` (the Bun server export), `app.sse()` for server-sent events, and `Hype.router()` for sub-routers. Everything else is standard Hono. - -```tsx -import { Hype } from '@because/hype' -const app = new Hype() - -app.get('/', c => c.html(

Hello

)) -app.get('/ok', c => c.text('ok')) // Health check -- required - -export default app.defaults -``` - -Constructor options: `prettyHTML` (default true, tools should set false), `layout` (default true), `logging` (default true). - -**SSE** -- the one non-Hono addition: -```tsx -app.sse('/stream', (send, c) => { - send({ hello: 'world' }) - const interval = setInterval(() => send({ time: Date.now() }), 1000) - return () => clearInterval(interval) // cleanup on disconnect -}) -``` - -**Sub-routers:** -```tsx -const api = Hype.router() -api.get('/items', c => c.json([])) -app.route('/api', api) // mounts at /api/items -``` - -## Forge - -Forge creates styled JSX components via `define()`. Properties use camelCase CSS. Numbers auto-convert to `px` (except `flex`, `opacity`, `zIndex`, `fontWeight`). - -```tsx -import { define, stylesToCSS } from '@because/forge' - -const Box = define('Box', { - padding: 20, - borderRadius: '6px', -}) -// content renders
content
-``` - -**`base`** -- set the HTML element (default `div`): -```tsx -const Button = define('Button', { base: 'button', padding: '8px 16px' }) -const Link = define('Link', { base: 'a', textDecoration: 'none' }) -``` - -**`states`** -- pseudo-classes: -```tsx -const Item = define('Item', { - padding: 12, - states: { - ':hover': { backgroundColor: '#eee' }, - ':last-child': { borderBottom: 'none' }, - }, -}) -``` - -**`selectors`** -- nested CSS (`&` = the component): -```tsx -const List = define('List', { - selectors: { - '& > li:last-child': { borderBottom: 'none' }, - }, -}) -``` - -**`variants`** -- conditional styles via props: -```tsx -const Button = define('Button', { - base: 'button', - variants: { - variant: { - primary: { backgroundColor: '#2563eb', color: 'white' }, - danger: { backgroundColor: '#dc2626', color: 'white' }, - }, - }, -}) -// -``` - -**Serving CSS** -- apps serve `stylesToCSS()` from a route. Tools prepend `baseStyles`: -```tsx -app.get('/styles.css', c => - c.text(baseStyles + stylesToCSS(), 200, { 'Content-Type': 'text/css; charset=utf-8' }) -) -``` - -## Theme Tokens - -Tools import `theme` from `@because/toes/tools`. It returns CSS variables that resolve per light/dark mode. - -```tsx -import { baseStyles, ToolScript, theme } from '@because/toes/tools' - -const Container = define('Container', { - color: theme('colors-text'), - border: `1px solid ${theme('colors-border')}`, -}) -``` - -Available tokens: - -| Token | Use for | -|-------|---------| -| `colors-bg`, `colors-bgSubtle`, `colors-bgElement`, `colors-bgHover` | Backgrounds | -| `colors-text`, `colors-textMuted`, `colors-textFaint` | Text | -| `colors-border` | Borders | -| `colors-link` | Links | -| `colors-primary`, `colors-primaryText` | Primary actions | -| `colors-error`, `colors-dangerBorder`, `colors-dangerText` | Errors/danger | -| `colors-success`, `colors-successBg` | Success states | -| `colors-statusRunning`, `colors-statusStopped` | Status indicators | -| `fonts-sans`, `fonts-mono` | Font stacks | -| `spacing-xs` (4), `spacing-sm` (8), `spacing-md` (12), `spacing-lg` (16), `spacing-xl` (24) | Spacing (px) | -| `radius-md` (6px) | Border radius | - -## Writing a Tool - -Tools need three extra things vs apps: - -1. `` in `` (handles dark mode + iframe height communication) -2. `baseStyles` prepended to CSS output -3. Handle the `?app=` query param - -```tsx -import { Hype } from '@because/hype' -import { define, stylesToCSS } from '@because/forge' -import { baseStyles, ToolScript, theme } from '@because/toes/tools' -import type { Child } from 'hono/jsx' - -const APPS_DIR = process.env.APPS_DIR! -const app = new Hype({ prettyHTML: false }) - -const Container = define('Container', { - fontFamily: theme('fonts-sans'), - padding: '20px', - paddingTop: 0, - maxWidth: '800px', - margin: '0 auto', - color: theme('colors-text'), -}) - -function Layout({ title, children }: { title: string; children: Child }) { - return ( - - - - - {title} - - - - - {children} - - - ) -} - -app.get('/ok', c => c.text('ok')) -app.get('/styles.css', c => - c.text(baseStyles + stylesToCSS(), 200, { 'Content-Type': 'text/css; charset=utf-8' }) -) - -app.get('/', async c => { - const appName = c.req.query('app') - if (!appName) return c.html(

No app selected

) - // ... tool logic using join(APPS_DIR, appName, 'current') for file paths - return c.html(...) -}) - -export default app.defaults -``` - -**Environment variables** available to tools: `APPS_DIR`, `TOES_URL` (base URL of Toes server), `PORT`, `TOES_DIR`. - -**Accessing app files:** always use `join(APPS_DIR, appName, 'current')`. - -**Calling the Toes API:** `fetch(\`${TOES_URL}/api/apps\`)`, `fetch(\`${TOES_URL}/api/apps/${name}\`)`. - -**Linking between tools:** `View Code`. - -## Patterns - -**Fire-and-forget with polling** -- for long-running ops, don't await in POST. Use `` to poll while running. - -**Inline client JS** -- use `