From c7f8f09ba94727add5da62bcb9171b8a6608096d Mon Sep 17 00:00:00 2001 From: Chris Wanstrath Date: Sun, 1 Mar 2026 09:35:05 -0800 Subject: [PATCH] Add global field to filter tool tabs --- src/client/components/AppDetail.tsx | 4 ++-- src/client/components/Nav.tsx | 4 ++-- src/client/index.tsx | 2 +- src/server/api/apps.ts | 4 ++-- src/server/apps.ts | 6 ++++-- src/shared/types.ts | 1 + 6 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/client/components/AppDetail.tsx b/src/client/components/AppDetail.tsx index 811ae64..f99da5b 100644 --- a/src/client/components/AppDetail.tsx +++ b/src/client/components/AppDetail.tsx @@ -45,8 +45,8 @@ const OpenEmojiPicker = define('OpenEmojiPicker', { }) export function AppDetail({ app, render }: { app: App, render: () => void }) { - // Find all tools - const tools = apps.filter(a => a.tool) + // Find global tools (shown as tabs on every app) + const tools = apps.filter(a => a.tool && a.global) const selectedTab = getSelectedTab(app.name) return ( diff --git a/src/client/components/Nav.tsx b/src/client/components/Nav.tsx index 516f106..d6d4942 100644 --- a/src/client/components/Nav.tsx +++ b/src/client/components/Nav.tsx @@ -16,8 +16,8 @@ export function Nav({ app, render }: { app: App; render: () => void }) { navigate(tab === 'overview' ? `/app/${app.name}` : `/app/${app.name}/${tab}`) } - // Find all tools - const tools = apps.filter(a => a.tool) + // Find global tools (shown as tabs on every app) + const tools = apps.filter(a => a.tool && a.global) const titlecase = (s: string) => s.split(' ').map(part => part[0]?.toUpperCase() + part.slice(1)) return ( diff --git a/src/client/index.tsx b/src/client/index.tsx index 209b405..e004c2f 100644 --- a/src/client/index.tsx +++ b/src/client/index.tsx @@ -10,7 +10,7 @@ const render = () => { renderApp(, document.getElementById('app')!) // Update tool iframes after DOM settles requestAnimationFrame(() => { - const tools = apps.filter(a => a.tool) + const tools = apps.filter(a => a.tool && a.global) updateToolIframes(getSelectedTab(selectedApp), tools, selectedApp) }) } diff --git a/src/server/api/apps.ts b/src/server/api/apps.ts index 9c136c5..6c9ee6c 100644 --- a/src/server/api/apps.ts +++ b/src/server/api/apps.ts @@ -26,9 +26,9 @@ function convert(app: BackendApp): SharedApp { router.sse('/stream', (send) => { const broadcast = () => { const apps: SharedApp[] = allApps().map(({ - name, state, icon, error, port, started, logs, tool, tunnelEnabled, tunnelUrl + name, state, icon, error, port, started, logs, tool, global: global_, tunnelEnabled, tunnelUrl }) => ({ - name, state, icon, error, port, started, logs, tool, tunnelEnabled, tunnelUrl, + name, state, icon, error, port, started, logs, tool, global: global_, tunnelEnabled, tunnelUrl, })) send(apps) } diff --git a/src/server/apps.ts b/src/server/apps.ts index 2119c1a..210325d 100644 --- a/src/server/apps.ts +++ b/src/server/apps.ts @@ -159,7 +159,8 @@ export function registerApp(dir: string) { const state: AppState = error ? 'invalid' : 'stopped' const icon = pkg.toes?.icon ?? DEFAULT_EMOJI const tool = pkg.toes?.tool - _apps.set(dir, { name: dir, state, icon, error, tool }) + const global_ = pkg.toes?.global + _apps.set(dir, { name: dir, state, icon, error, tool, global: global_ }) update() emit({ type: 'app:create', app: dir }) if (!error) { @@ -379,7 +380,8 @@ function discoverApps() { const state: AppState = error ? 'invalid' : 'stopped' const icon = pkg.toes?.icon ?? DEFAULT_EMOJI const tool = pkg.toes?.tool - _apps.set(dir, { name: dir, state, icon, error, tool }) + const global_ = pkg.toes?.global + _apps.set(dir, { name: dir, state, icon, error, tool, global: global_ }) } update() } diff --git a/src/shared/types.ts b/src/shared/types.ts index c0fdd42..a24b714 100644 --- a/src/shared/types.ts +++ b/src/shared/types.ts @@ -28,6 +28,7 @@ export type App = { started?: number logs?: LogLine[] tool?: boolean | string + global?: boolean tunnelEnabled?: boolean tunnelUrl?: string }