/tool redirects
This commit is contained in:
parent
d1b7e973d3
commit
a25088e723
|
|
@ -216,9 +216,12 @@ const fileMemoryScript = `
|
|||
var version = params.get('version') || 'current';
|
||||
if (!app) return;
|
||||
var key = 'code-app:' + app + ':' + version + ':file';
|
||||
if (file) {
|
||||
localStorage.setItem(key, file);
|
||||
if (params.has('file')) {
|
||||
// Explicit file param (even empty) - save it
|
||||
if (file) localStorage.setItem(key, file);
|
||||
else localStorage.removeItem(key);
|
||||
} else {
|
||||
// No file param - restore saved location
|
||||
var saved = localStorage.getItem(key);
|
||||
if (saved) {
|
||||
var url = '/?app=' + encodeURIComponent(app);
|
||||
|
|
@ -318,7 +321,7 @@ function PathBreadcrumb({ appName, filePath, versionParam }: BreadcrumbProps) {
|
|||
return (
|
||||
<Breadcrumb>
|
||||
{crumbs.length > 0 ? (
|
||||
<BreadcrumbLink href={`/?app=${appName}${versionParam}`}>{appName}</BreadcrumbLink>
|
||||
<BreadcrumbLink href={`/?app=${appName}${versionParam}&file=`}>{appName}</BreadcrumbLink>
|
||||
) : (
|
||||
<BreadcrumbCurrent>{appName}</BreadcrumbCurrent>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import { join } from 'path'
|
|||
import type { Child } from 'hono/jsx'
|
||||
|
||||
const APPS_DIR = process.env.APPS_DIR!
|
||||
const TOES_URL = process.env.TOES_URL!
|
||||
|
||||
const app = new Hype({ prettyHTML: false })
|
||||
|
||||
|
|
@ -181,8 +182,7 @@ app.get('/', async c => {
|
|||
{versions.map(v => (
|
||||
<VersionItem>
|
||||
<VersionLink
|
||||
href="#"
|
||||
onclick={`window.parent.postMessage({type:'navigate-tool',tool:'code',params:{app:'${appName}',version:'${v.name}'}}, '*'); return false;`}
|
||||
href={`${TOES_URL}/tool/code?app=${appName}&version=${v.name}`}
|
||||
>
|
||||
{formatTimestamp(v.name)}
|
||||
</VersionLink>
|
||||
|
|
|
|||
|
|
@ -45,19 +45,17 @@ const appPath = join(APPS_DIR, appName, 'current')
|
|||
|
||||
Not `APPS_DIR/appName` directly.
|
||||
|
||||
## talking to the parent
|
||||
## linking to tools
|
||||
|
||||
**Navigate to another tool:**
|
||||
Use `/tool/:name` URLs to link directly to tools with params:
|
||||
|
||||
```js
|
||||
window.parent.postMessage({
|
||||
type: 'navigate-tool',
|
||||
tool: 'code',
|
||||
params: { app: 'my-app', version: '20260130-000000' }
|
||||
}, '*')
|
||||
```html
|
||||
<a href="/tool/code?app=my-app&version=20260130-000000">
|
||||
View in Code
|
||||
</a>
|
||||
```
|
||||
|
||||
**Resize your iframe:**
|
||||
## resize iframe
|
||||
|
||||
```js
|
||||
window.parent.postMessage({
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import { render as renderApp } from 'hono/jsx/dom'
|
||||
import { Dashboard } from './components'
|
||||
import { apps, selectedApp, selectedTab, setApps, setSelectedApp, setSelectedTab } from './state'
|
||||
import { apps, selectedApp, selectedTab, setApps, setSelectedApp } from './state'
|
||||
import { initModal } from './components/modal'
|
||||
import { initToolIframes, updateToolIframes, setNavigateToolHandler, navigateToTool } from './tool-iframes'
|
||||
import { initToolIframes, updateToolIframes } from './tool-iframes'
|
||||
import { initUpdate } from './update'
|
||||
|
||||
const render = () => {
|
||||
|
|
@ -19,15 +19,6 @@ initModal(render)
|
|||
initUpdate(render)
|
||||
initToolIframes()
|
||||
|
||||
// Handle tool-to-tool navigation via postMessage
|
||||
setNavigateToolHandler((tool, params) => {
|
||||
const tools = apps.filter(a => a.tool)
|
||||
navigateToTool(tool, params, tools, (tab) => {
|
||||
setSelectedTab(tab)
|
||||
render()
|
||||
})
|
||||
})
|
||||
|
||||
// Set theme based on system preference
|
||||
const setTheme = () => {
|
||||
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches
|
||||
|
|
|
|||
|
|
@ -12,13 +12,6 @@ let currentTool: string | null = (window as any).__currentTool ?? null
|
|||
// Get the stable container (outside Hono-managed DOM)
|
||||
const getContainer = () => document.getElementById('tool-iframes')
|
||||
|
||||
// Callback for tool navigation requests
|
||||
let onNavigateTool: ((tool: string, params: Record<string, string>) => void) | null = null
|
||||
|
||||
export function setNavigateToolHandler(handler: (tool: string, params: Record<string, string>) => void) {
|
||||
onNavigateTool = handler
|
||||
}
|
||||
|
||||
// Listen for messages from iframes
|
||||
function setupMessageListener() {
|
||||
if ((window as any).__toolIframeMessageListener) return
|
||||
|
|
@ -40,13 +33,6 @@ function setupMessageListener() {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (type === 'navigate-tool') {
|
||||
const { tool, params } = event.data
|
||||
if (tool && onNavigateTool) {
|
||||
onNavigateTool(tool, params || {})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -99,45 +85,6 @@ function buildCacheKey(toolName: string, params: Record<string, string>): string
|
|||
return parts.join(':')
|
||||
}
|
||||
|
||||
// Navigate to a tool with specific params (called from postMessage handler)
|
||||
export function navigateToTool(
|
||||
toolName: string,
|
||||
params: Record<string, string>,
|
||||
tools: Array<{ name: string; port?: number; state: string }>,
|
||||
onSelectTab: (tab: string) => void
|
||||
) {
|
||||
const tool = tools.find(t => t.name === toolName)
|
||||
if (!tool || tool.state !== 'running' || !tool.port) return
|
||||
|
||||
const container = getContainer()
|
||||
if (!container) return
|
||||
|
||||
const cacheKey = buildCacheKey(toolName, params)
|
||||
|
||||
// Create iframe if needed
|
||||
let cached = iframes.get(cacheKey)
|
||||
if (!cached || cached.port !== tool.port) {
|
||||
const iframe = document.createElement('iframe')
|
||||
iframe.src = buildToolUrl(tool.port, params)
|
||||
iframe.dataset.toolName = toolName
|
||||
iframe.dataset.appName = params.app ?? ''
|
||||
iframe.style.cssText = `width: 100%; border: none;`
|
||||
cached = { iframe, port: tool.port }
|
||||
iframes.set(cacheKey, cached)
|
||||
container.appendChild(iframe)
|
||||
}
|
||||
|
||||
// Switch to that tab
|
||||
onSelectTab(toolName)
|
||||
|
||||
// Force show this specific iframe
|
||||
currentTool = cacheKey
|
||||
;(window as any).__currentTool = cacheKey
|
||||
for (const [key, { iframe }] of iframes) {
|
||||
iframe.style.display = key === cacheKey ? 'block' : 'none'
|
||||
}
|
||||
}
|
||||
|
||||
// Update which iframe is visible based on selected tab and tool state
|
||||
export function updateToolIframes(
|
||||
selectedTab: string,
|
||||
|
|
|
|||
|
|
@ -433,7 +433,7 @@ async function runApp(dir: string, port: number) {
|
|||
|
||||
const proc = Bun.spawn(['bun', 'run', 'toes'], {
|
||||
cwd,
|
||||
env: { ...process.env, PORT: String(port), NO_AUTOPORT: 'true', APPS_DIR },
|
||||
env: { ...process.env, PORT: String(port), NO_AUTOPORT: 'true', APPS_DIR, TOES_URL: `http://localhost:${process.env.PORT || 3000}` },
|
||||
stdout: 'pipe',
|
||||
stderr: 'pipe',
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { initApps } from '$apps'
|
||||
import { allApps, initApps } from '$apps'
|
||||
import appsRouter from './api/apps'
|
||||
import syncRouter from './api/sync'
|
||||
import { Hype } from '@because/hype'
|
||||
|
|
@ -8,6 +8,18 @@ const app = new Hype({ layout: false })
|
|||
app.route('/api/apps', appsRouter)
|
||||
app.route('/api/sync', syncRouter)
|
||||
|
||||
// Tool URLs: /tool/code?app=todo&file=README.md -> redirect to tool port
|
||||
app.get('/tool/:tool', c => {
|
||||
const toolName = c.req.param('tool')
|
||||
const tool = allApps().find(a => a.tool && a.name === toolName)
|
||||
if (!tool || tool.state !== 'running' || !tool.port) {
|
||||
return c.text(`Tool "${toolName}" not found or not running`, 404)
|
||||
}
|
||||
const params = new URLSearchParams(c.req.query()).toString()
|
||||
const url = params ? `http://localhost:${tool.port}?${params}` : `http://localhost:${tool.port}`
|
||||
return c.redirect(url)
|
||||
})
|
||||
|
||||
console.log('🐾 Toes!')
|
||||
initApps()
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user