From ed46638b50dafec28018d6addb4dd463acab4278 Mon Sep 17 00:00:00 2001 From: Chris Wanstrath Date: Fri, 30 Jan 2026 18:13:29 -0800 Subject: [PATCH] --all, --tools --- src/cli/commands/manage.ts | 25 ++++++++++++++++++------- src/cli/setup.ts | 3 ++- src/client/index.tsx | 2 +- src/client/tool-iframes.ts | 36 +++++++++++++++++++++++------------- 4 files changed, 44 insertions(+), 22 deletions(-) diff --git a/src/cli/commands/manage.ts b/src/cli/commands/manage.ts index bbfe4d9..fce6190 100644 --- a/src/cli/commands/manage.ts +++ b/src/cli/commands/manage.ts @@ -21,10 +21,10 @@ export async function configShow() { const source = process.env.TOES_URL ? 'TOES_URL' : process.env.TOES_HOST - ? 'TOES_HOST' + (process.env.PORT ? ' + PORT' : '') - : process.env.NODE_ENV === 'production' - ? 'default (production)' - : 'default (development)' + ? 'TOES_HOST' + (process.env.PORT ? ' + PORT' : '') + : process.env.NODE_ENV === 'production' + ? 'default (production)' + : 'default (development)' console.log(`Source: ${color.gray(source)}`) @@ -53,7 +53,7 @@ export async function infoApp(arg?: string) { } const icon = STATE_ICONS[app.state] ?? '◯' - console.log(`${icon} ${color.bold(app.name)}`) + console.log(`${icon} ${color.bold(app.name)} ${app.tool && '[tool]'}`) console.log(` State: ${app.state}`) if (app.port) { console.log(` Port: ${app.port}`) @@ -73,11 +73,22 @@ export async function infoApp(arg?: string) { if (app.error) console.log(` Error: ${color.red(app.error)}`) } -export async function listApps() { +interface ListAppsOptions { + tools?: boolean + all?: boolean +} + +export async function listApps(options: ListAppsOptions) { const apps: App[] | undefined = await get('/api/apps') if (!apps) return - for (const app of apps) { + const filtered = apps.filter((app) => { + if (options.all) return true + if (options.tools) return app.tool + return !app.tool + }) + + for (const app of filtered) { console.log(`${STATE_ICONS[app.state] ?? '◯'} ${app.name}`) } } diff --git a/src/cli/setup.ts b/src/cli/setup.ts index f22fbed..3770616 100644 --- a/src/cli/setup.ts +++ b/src/cli/setup.ts @@ -1,5 +1,4 @@ import { program } from 'commander' -import { readFileSync } from 'fs' import color from 'kleur' import { @@ -60,6 +59,8 @@ program program .command('list') .description('List all apps') + .option('-t, --tools', 'show only tools') + .option('-a, --all', 'show all apps including tools') .action(listApps) program diff --git a/src/client/index.tsx b/src/client/index.tsx index 1b2ed60..7a1782c 100644 --- a/src/client/index.tsx +++ b/src/client/index.tsx @@ -10,7 +10,7 @@ const render = () => { // Update tool iframes after DOM settles requestAnimationFrame(() => { const tools = apps.filter(a => a.tool) - updateToolIframes(selectedTab, tools) + updateToolIframes(selectedTab, tools, selectedApp) }) } diff --git a/src/client/tool-iframes.ts b/src/client/tool-iframes.ts index 6263b0d..985cdc4 100644 --- a/src/client/tool-iframes.ts +++ b/src/client/tool-iframes.ts @@ -24,9 +24,11 @@ export function initToolIframes() { const match = iframe.src.match(/localhost:(\d+)/) if (match && match[1]) { const port = parseInt(match[1], 10) - const name = iframe.dataset.toolName - if (name) { - iframes.set(name, { iframe, port }) + const toolName = iframe.dataset.toolName + const appName = iframe.dataset.appName + if (toolName) { + const cacheKey = appName ? `${toolName}:${appName}` : toolName + iframes.set(cacheKey, { iframe, port }) } } }) @@ -42,7 +44,8 @@ export function initToolIframes() { // Update which iframe is visible based on selected tab and tool state export function updateToolIframes( selectedTab: string, - tools: Array<{ name: string; port?: number; state: string }> + tools: Array<{ name: string; port?: number; state: string }>, + selectedApp: string | null ) { const container = getContainer() if (!container) return @@ -63,8 +66,11 @@ export function updateToolIframes( const tool = selectedTool! + // Build cache key for tool + app combination + const cacheKey = selectedApp ? `${tool.name}:${selectedApp}` : tool.name + // Skip if nothing changed - if (currentTool === tool.name) { + if (currentTool === cacheKey) { // Just update position in case of scroll/resize const tabContent = document.querySelector('[data-tool-target]') if (tabContent) { @@ -97,34 +103,38 @@ export function updateToolIframes( z-index: 100; ` - // Get or create the iframe for this tool - let cached = iframes.get(tool.name) + // Get or create the iframe for this tool + app combination + let cached = iframes.get(cacheKey) if (!cached || cached.port !== tool.port) { // Create new iframe (first time or port changed) const iframe = document.createElement('iframe') - iframe.src = `http://localhost:${tool.port}` + const url = selectedApp + ? `http://localhost:${tool.port}?app=${encodeURIComponent(selectedApp)}` + : `http://localhost:${tool.port}` + iframe.src = url iframe.dataset.toolName = tool.name // For hot reload recovery + iframe.dataset.appName = selectedApp ?? '' // For hot reload recovery iframe.style.cssText = ` width: 100%; height: 100%; border: none; ` cached = { iframe, port: tool.port! } - iframes.set(tool.name, cached) + iframes.set(cacheKey, cached) // Add to container container.appendChild(iframe) } // Show only the selected iframe, hide others - for (const [name, { iframe }] of iframes) { - const shouldShow = name === tool.name + for (const [key, { iframe }] of iframes) { + const shouldShow = key === cacheKey if (shouldShow && iframe.parentElement !== container) { container.appendChild(iframe) } iframe.style.display = shouldShow ? 'block' : 'none' } - currentTool = tool.name - ;(window as any).__currentTool = tool.name + currentTool = cacheKey + ;(window as any).__currentTool = cacheKey }