/tool redirects
This commit is contained in:
parent
d1b7e973d3
commit
a25088e723
|
|
@ -216,9 +216,12 @@ const fileMemoryScript = `
|
||||||
var version = params.get('version') || 'current';
|
var version = params.get('version') || 'current';
|
||||||
if (!app) return;
|
if (!app) return;
|
||||||
var key = 'code-app:' + app + ':' + version + ':file';
|
var key = 'code-app:' + app + ':' + version + ':file';
|
||||||
if (file) {
|
if (params.has('file')) {
|
||||||
localStorage.setItem(key, file);
|
// Explicit file param (even empty) - save it
|
||||||
|
if (file) localStorage.setItem(key, file);
|
||||||
|
else localStorage.removeItem(key);
|
||||||
} else {
|
} else {
|
||||||
|
// No file param - restore saved location
|
||||||
var saved = localStorage.getItem(key);
|
var saved = localStorage.getItem(key);
|
||||||
if (saved) {
|
if (saved) {
|
||||||
var url = '/?app=' + encodeURIComponent(app);
|
var url = '/?app=' + encodeURIComponent(app);
|
||||||
|
|
@ -318,7 +321,7 @@ function PathBreadcrumb({ appName, filePath, versionParam }: BreadcrumbProps) {
|
||||||
return (
|
return (
|
||||||
<Breadcrumb>
|
<Breadcrumb>
|
||||||
{crumbs.length > 0 ? (
|
{crumbs.length > 0 ? (
|
||||||
<BreadcrumbLink href={`/?app=${appName}${versionParam}`}>{appName}</BreadcrumbLink>
|
<BreadcrumbLink href={`/?app=${appName}${versionParam}&file=`}>{appName}</BreadcrumbLink>
|
||||||
) : (
|
) : (
|
||||||
<BreadcrumbCurrent>{appName}</BreadcrumbCurrent>
|
<BreadcrumbCurrent>{appName}</BreadcrumbCurrent>
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import { join } from 'path'
|
||||||
import type { Child } from 'hono/jsx'
|
import type { Child } from 'hono/jsx'
|
||||||
|
|
||||||
const APPS_DIR = process.env.APPS_DIR!
|
const APPS_DIR = process.env.APPS_DIR!
|
||||||
|
const TOES_URL = process.env.TOES_URL!
|
||||||
|
|
||||||
const app = new Hype({ prettyHTML: false })
|
const app = new Hype({ prettyHTML: false })
|
||||||
|
|
||||||
|
|
@ -181,8 +182,7 @@ app.get('/', async c => {
|
||||||
{versions.map(v => (
|
{versions.map(v => (
|
||||||
<VersionItem>
|
<VersionItem>
|
||||||
<VersionLink
|
<VersionLink
|
||||||
href="#"
|
href={`${TOES_URL}/tool/code?app=${appName}&version=${v.name}`}
|
||||||
onclick={`window.parent.postMessage({type:'navigate-tool',tool:'code',params:{app:'${appName}',version:'${v.name}'}}, '*'); return false;`}
|
|
||||||
>
|
>
|
||||||
{formatTimestamp(v.name)}
|
{formatTimestamp(v.name)}
|
||||||
</VersionLink>
|
</VersionLink>
|
||||||
|
|
|
||||||
|
|
@ -45,19 +45,17 @@ const appPath = join(APPS_DIR, appName, 'current')
|
||||||
|
|
||||||
Not `APPS_DIR/appName` directly.
|
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
|
```html
|
||||||
window.parent.postMessage({
|
<a href="/tool/code?app=my-app&version=20260130-000000">
|
||||||
type: 'navigate-tool',
|
View in Code
|
||||||
tool: 'code',
|
</a>
|
||||||
params: { app: 'my-app', version: '20260130-000000' }
|
|
||||||
}, '*')
|
|
||||||
```
|
```
|
||||||
|
|
||||||
**Resize your iframe:**
|
## resize iframe
|
||||||
|
|
||||||
```js
|
```js
|
||||||
window.parent.postMessage({
|
window.parent.postMessage({
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
import { render as renderApp } from 'hono/jsx/dom'
|
import { render as renderApp } from 'hono/jsx/dom'
|
||||||
import { Dashboard } from './components'
|
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 { initModal } from './components/modal'
|
||||||
import { initToolIframes, updateToolIframes, setNavigateToolHandler, navigateToTool } from './tool-iframes'
|
import { initToolIframes, updateToolIframes } from './tool-iframes'
|
||||||
import { initUpdate } from './update'
|
import { initUpdate } from './update'
|
||||||
|
|
||||||
const render = () => {
|
const render = () => {
|
||||||
|
|
@ -19,15 +19,6 @@ initModal(render)
|
||||||
initUpdate(render)
|
initUpdate(render)
|
||||||
initToolIframes()
|
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
|
// Set theme based on system preference
|
||||||
const setTheme = () => {
|
const setTheme = () => {
|
||||||
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches
|
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)
|
// Get the stable container (outside Hono-managed DOM)
|
||||||
const getContainer = () => document.getElementById('tool-iframes')
|
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
|
// Listen for messages from iframes
|
||||||
function setupMessageListener() {
|
function setupMessageListener() {
|
||||||
if ((window as any).__toolIframeMessageListener) return
|
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(':')
|
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
|
// Update which iframe is visible based on selected tab and tool state
|
||||||
export function updateToolIframes(
|
export function updateToolIframes(
|
||||||
selectedTab: string,
|
selectedTab: string,
|
||||||
|
|
|
||||||
|
|
@ -433,7 +433,7 @@ async function runApp(dir: string, port: number) {
|
||||||
|
|
||||||
const proc = Bun.spawn(['bun', 'run', 'toes'], {
|
const proc = Bun.spawn(['bun', 'run', 'toes'], {
|
||||||
cwd,
|
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',
|
stdout: 'pipe',
|
||||||
stderr: 'pipe',
|
stderr: 'pipe',
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { initApps } from '$apps'
|
import { allApps, initApps } from '$apps'
|
||||||
import appsRouter from './api/apps'
|
import appsRouter from './api/apps'
|
||||||
import syncRouter from './api/sync'
|
import syncRouter from './api/sync'
|
||||||
import { Hype } from '@because/hype'
|
import { Hype } from '@because/hype'
|
||||||
|
|
@ -8,6 +8,18 @@ const app = new Hype({ layout: false })
|
||||||
app.route('/api/apps', appsRouter)
|
app.route('/api/apps', appsRouter)
|
||||||
app.route('/api/sync', syncRouter)
|
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!')
|
console.log('🐾 Toes!')
|
||||||
initApps()
|
initApps()
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user