import { Theme, SpacingSystem, Typography } from "../theme/theme.slint"; import { Tabs, TabItem, TabContent } from "../components/tabs.slint"; export struct LogEntry { time: string, level: string, // "debug", "info", "warn", "error" message: string, } export component LogsPage { in property <[LogEntry]> mihomo-logs: []; in property <[LogEntry]> app-logs: []; in-out property current-tab: 0; callback tab-changed(int); VerticalLayout { padding: SpacingSystem.spacing.s4; spacing: SpacingSystem.spacing.s4; // Header with Tabs HorizontalLayout { spacing: SpacingSystem.spacing.s3; alignment: space-between; Text { text: "Logs"; color: Theme.colors.foreground; font-size: Typography.sizes.xl; font-weight: Typography.weights.bold; vertical-alignment: center; } Rectangle { horizontal-stretch: 1; } } // Tabs Tabs { tabs: [ { title: "Mihomo Logs" }, { title: "App Logs" }, ]; current-index: root.current-tab; tab-changed(index) => { root.current-tab = index; root.tab-changed(index); } // Mihomo Logs TabContent { index: 0; current-index: root.current-tab; LogView { logs: root.mihomo-logs; } } // App Logs TabContent { index: 1; current-index: root.current-tab; LogView { logs: root.app-logs; } } } } } component LogView { in property <[LogEntry]> logs: []; Rectangle { background: Theme.colors.background; border-radius: SpacingSystem.radius.md; border-width: 1px; border-color: Theme.colors.border; ScrollView { VerticalLayout { padding: SpacingSystem.spacing.s2; spacing: 0; for log[index] in root.logs: LogRow { log-entry: log; odd: Math.mod(index, 2) == 1; } } } } } component LogRow { in property log-entry; in property ool> odd: false; private property hovered: false; private property level-color: log-entry.level == "error" ? #ef4444 : log-entry.level == "warn" ? #eab308 : log-entry.level == "info" ? #3b82f6 : log-entry.level == "debug" ? #6b7280 : Theme.colors.muted-foreground; height: 24px; states [ hovered when root.hovered: { container.background: Theme.colors.muted.transparentize(0.5); } odd when root.odd && !root.hovered: { container.background: Theme.colors.muted.transparentize(0.8); } ] container := Rectangle { background: transparent; border-radius: SpacingSystem.radius.sm; animate background { duration: 150ms; } HorizontalLayout { padding-left: SpacingSystem.spacing.s2; padding-right: SpacingSystem.spacing.s2; spacing: SpacingSystem.spacing.s2; // Time Text { text: root.log-entry.time; color: Theme.colors.muted-foreground; font-size: 11px; font-family: "monospace"; vertical-alignment: center; width: 80px; } // Level Text { text: root.log-entry.level; color: root.level-color; font-size: 11px; font-family: "monospace"; font-weight: Typography.weights.bold; vertical-alignment: center; width: 60px; } // Message Text { text: root.log-entry.message; color: Theme.colors.foreground; font-size: 11px; font-family: "ospace"; vertical-alignment: center; overflow: elide; } } touch := TouchArea { moved => { root.hovered = self.has-hover; } } } }