From 271ff151a1f73738324a296f08a931e91695c614 Mon Sep 17 00:00:00 2001 From: Chris Wanstrath Date: Sun, 15 Feb 2026 07:47:41 -0800 Subject: [PATCH] single out toes logs on dashboard --- src/client/components/UnifiedLogs.tsx | 30 ++++++++++++++++++++++++--- src/client/styles/dashboard.ts | 30 +++++++++++++++++++++++++++ src/client/styles/index.ts | 2 ++ src/server/api/system.ts | 11 ++++++++++ src/server/tui.ts | 11 +++++++--- 5 files changed, 78 insertions(+), 6 deletions(-) diff --git a/src/client/components/UnifiedLogs.tsx b/src/client/components/UnifiedLogs.tsx index f250d13..0e95f79 100644 --- a/src/client/components/UnifiedLogs.tsx +++ b/src/client/components/UnifiedLogs.tsx @@ -4,6 +4,8 @@ import { LogsBody, LogsHeader, LogsSection, + LogsTab, + LogsTabs, LogsTitle, LogText, LogTimestamp, @@ -16,8 +18,11 @@ export interface UnifiedLogLine { text: string } +type LogFilter = 'all' | 'toes' + const MAX_LOGS = 200 +let _filter: LogFilter = 'all' let _logs: UnifiedLogLine[] = [] let _source: EventSource | undefined @@ -54,7 +59,6 @@ function parseLogText(text: string): { method?: string, path?: string, status?: function LogLineEntry({ log }: { log: UnifiedLogLine }) { const parsed = parseLogText(log.text) const statusColor = getStatusColor(parsed.status) - return ( {formatTime(log.time)} @@ -66,21 +70,31 @@ function LogLineEntry({ log }: { log: UnifiedLogLine }) { ) } +const filteredLogs = (): UnifiedLogLine[] => + _filter === 'toes' ? _logs.filter(l => l.app === 'toes') : _logs + +function setFilter(filter: LogFilter) { + _filter = filter + renderLogs() +} + function LogsBodyContent() { + const logs = filteredLogs() return ( <> - {_logs.length === 0 ? ( + {logs.length === 0 ? ( No activity yet ) : ( - _logs.map((log, i) => ) + logs.map((log, i) => ) )} ) } function renderLogs() { + update('#unified-logs-tabs', ) update('#unified-logs-body', ) // Auto-scroll after render requestAnimationFrame(() => { @@ -101,11 +115,21 @@ export function initUnifiedLogs() { } } +function LogsTabsBar() { + return ( + + setFilter('all')}>All + setFilter('toes')}>Toes + + ) +} + export function UnifiedLogs() { return ( Logs +
diff --git a/src/client/styles/dashboard.ts b/src/client/styles/dashboard.ts index 775144d..2e31a27 100644 --- a/src/client/styles/dashboard.ts +++ b/src/client/styles/dashboard.ts @@ -97,6 +97,36 @@ export const LogsClearButton = define('LogsClearButton', { }, }) +export const LogsTabs = define('LogsTabs', { + display: 'flex', + gap: 0, +}) + +export const LogsTab = define('LogsTab', { + base: 'button', + background: 'none', + border: 'none', + borderBottom: '2px solid transparent', + padding: '0 8px 8px', + fontSize: 11, + fontWeight: 600, + color: theme('colors-textFaint'), + cursor: 'pointer', + textTransform: 'uppercase', + letterSpacing: '0.05em', + selectors: { + '&:hover': { + color: theme('colors-text'), + }, + }, + variants: { + active: { + color: theme('colors-text'), + borderBottomColor: theme('colors-text'), + }, + }, +}) + export const LogsBody = define('LogsBody', { height: 200, overflow: 'auto', diff --git a/src/client/styles/index.ts b/src/client/styles/index.ts index 153267a..f3e933d 100644 --- a/src/client/styles/index.ts +++ b/src/client/styles/index.ts @@ -9,6 +9,8 @@ export { LogsClearButton, LogsHeader, LogsSection, + LogsTab, + LogsTabs, LogsTitle, LogStatus, LogText, diff --git a/src/server/api/system.ts b/src/server/api/system.ts index fe7120f..f6fe719 100644 --- a/src/server/api/system.ts +++ b/src/server/api/system.ts @@ -1,4 +1,5 @@ import { allApps, APPS_DIR, onChange } from '$apps' +import { onHostLog } from '../tui' import { Hype } from '@because/hype' import { cpus, platform, totalmem } from 'os' import { join } from 'path' @@ -258,7 +259,17 @@ function collectLogs() { } } +function pushHostLog(text: string) { + const line: UnifiedLogLine = { time: Date.now(), app: 'toes', text } + unifiedLogs.push(line) + if (unifiedLogs.length > MAX_UNIFIED_LOGS) unifiedLogs.shift() + for (const listener of unifiedLogListeners) listener(line) +} + // Subscribe to app changes to collect logs onChange(collectLogs) +// Subscribe to host-level log messages +onHostLog(pushHostLog) + export default router diff --git a/src/server/tui.ts b/src/server/tui.ts index 9f02fdd..b52bc11 100644 --- a/src/server/tui.ts +++ b/src/server/tui.ts @@ -7,8 +7,13 @@ let _apps: App[] = [] let _enabled = (process.stdout.isTTY ?? false) && !process.env.DEBUG let _lastRender = 0 let _renderTimer: Timer | undefined +let _hostLogListeners = new Set<(text: string) => void>() let _showEmoji = false +export const onHostLog = (cb: (text: string) => void) => { + _hostLogListeners.add(cb) +} + export const setShowEmoji = (show: boolean) => { _showEmoji = show scheduleRender() @@ -21,9 +26,9 @@ export function appLog(app: App, ...msg: string[]) { } export function hostLog(...msg: string[]) { - if (!_enabled) { - console.log('🐾', msg.join(' ')) - } + const text = msg.join(' ') + if (!_enabled) console.log('🐾', text) + for (const listener of _hostLogListeners) listener(text) } export function setApps(apps: App[]) {