81 lines
1.3 KiB
Markdown
81 lines
1.3 KiB
Markdown
# Tools
|
|
|
|
A tool is an app that appears as a tab in the dashboard instead of in the sidebar.
|
|
|
|
Tools know which app is selected and render in an iframe over the content area.
|
|
|
|
## making an app a tool
|
|
|
|
Add `toes.tool` to package.json:
|
|
|
|
```json
|
|
{
|
|
"toes": {
|
|
"tool": true,
|
|
"icon": "🔧"
|
|
},
|
|
"scripts": {
|
|
"toes": "bun run --watch index.tsx"
|
|
}
|
|
}
|
|
```
|
|
|
|
## getting the selected app
|
|
|
|
Query param `?app=<name>` tells you which app the user selected:
|
|
|
|
```tsx
|
|
app.get('/', c => {
|
|
const appName = c.req.query('app')
|
|
if (!appName) {
|
|
return c.html(<p>No app selected</p>)
|
|
}
|
|
// do something with appName
|
|
})
|
|
```
|
|
|
|
## accessing app files
|
|
|
|
Always go through the `current` symlink:
|
|
|
|
```ts
|
|
const APPS_DIR = process.env.APPS_DIR ?? '.'
|
|
const appPath = join(APPS_DIR, appName, 'current')
|
|
```
|
|
|
|
Not `APPS_DIR/appName` directly.
|
|
|
|
## talking to the parent
|
|
|
|
**Navigate to another tool:**
|
|
|
|
```js
|
|
window.parent.postMessage({
|
|
type: 'navigate-tool',
|
|
tool: 'code',
|
|
params: { app: 'my-app', version: '20260130-000000' }
|
|
}, '*')
|
|
```
|
|
|
|
**Resize your iframe:**
|
|
|
|
```js
|
|
window.parent.postMessage({
|
|
type: 'resize-iframe',
|
|
height: 500
|
|
}, '*')
|
|
```
|
|
|
|
## iframe behavior
|
|
|
|
- iframes are cached per tool+app combination
|
|
- Never recreated once loaded
|
|
- State persists across tab switches
|
|
|
|
## cli
|
|
|
|
```bash
|
|
toes list --tools # list tools only
|
|
toes list --all # list apps and tools
|
|
```
|