import { Theme, SpacingSystem, Typography } from "../theme/theme.slint"; import { Tabs, TabItem, TabContent } from "../components/tabs.slint"; import { Accordion, AccordionItemData, AccordionContent } from "../components/accordion.slint"; import { Item } from "../components/item.slint"; export struct ProxyNode { name: string, type: string, latency: string, } export struct ProxyGroup { name: string, nodes: [ProxyNode], expanded: bool, } export component ProxiesPage { in-out property <[ProxyGroup]> proxy-groups: []; in-out property current-mode: 0; // 0=Rule, 1=Global, 2=Direct in property selected-proxy: ""; callback mode-changed(int); callback group-toggled(int, bool); callback proxy-selected(string); VerticalLayout { padding: SpacingSystem.spacing.s4; spacing: SpacingSystem.spacing.s4; // Header with Tabs HorizontalLayout { spacing: SpacingSystem.spacing.s3; alignment: space-between; Text { text: "Proxies"; 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: "Rule" }, { title: "Global" }, { title: "Direct" }, ]; current-index: root.current-mode; tab-changed(index) => { root.current-mode = index; root.mode-changed(index); } // Tab Content TabContent { index: 0; current-index: root.current-mode; ScrollView { VerticalLayout { spacing: SpacingSystem.spacing.s3; Accordion { items: root.proxy-groups.map(|g| { return { title: g.name, expanded: g.expanded }; }); item-toggled(index, expanded) => { root.group-toggled(index, expanded); } for group[group-index] in root.proxy-groups: AccordionContent { ProxyGroupContent { nodes: group.nodes; selected-proxy: root.selected-proxy; node-selected(name) => { root.proxy-selected(name); } } } } } } } TabContent { index: 1; current-index: root.current-mode; Text { text: "Global mode - All traffic goes through selected proxy"; color: Theme.colors.muted-foreground; horizontal-alignment: center; vertical-alignment: center; } } TabContent { index: 2; current-index: root.current-mode; Text { text: "Direct mode - All traffic goes direct"; color: Theme.colors.muted-foreground; horizontal-alignment: center; vertical-alignment: center; } } } } } component ProxyGroupContent { in property <[ProxyNode]> nodes: []; in property selected-proxy: ""; callback node-selected(string); GridLayout { spacing: SpacingSystem.spacing.s3; for node[index] in root.nodes: ProxyNodeItem { node-data: node; selected: node.name == root.selected-proxy; clicked => { root.node-selected(node.name); } } } } component ProxyNodeItem { in property node-data; in property selected: false; callback clicked(); private property hovered: false; min-width: 200px; height: 60px; states [ selected when root.selected: { container.border-color: Theme.coloimary; container.background: Theme.colors.primary.transparentize(0.9); } hovered when root.hovered && !root.selected: { container.background: Theme.colors.muted; } ] container := Rectangle { background: transparent; border-radius: SpacingSystem.radius.md; border-width: 1px; border-color: Theme.colors.border; animate background { duration: 200ms; } animate border-color { duration: 200ms; } HorizontalLayout { padding: SpacingSystem.spacing.s3; spacing: SpacingSystem.spacing.s3; alignment: space-between; VerticalLayout { spacing: SpacingSystem.spacing.s1; HorizontalLayout { spacing: SpacingSystem.spacing.s2; Text { text: "✓"; color: root.selected ? Theme.colors.primary : Theme.colors.muted-foreground; font-size: Typography.sizes.sm; vertical-alignment: center; } Text { text: root.node-data.name; color: Theme.colors.foreground; font-size: Typography.sizes.sm; font-weight: Typography.weights.medium; vertical-alignment: center; } } Text { text: root.node-data.type; color: Theme.colors.muted-foreground; font-size: Typography.sizes.xs; } } Text { text: root.node-data.latency; color: Theme.colors.primary; font-size: Typography.sizes.sm; font-weight: Typography.weights.semibold; vertical-alignment: center; } } touch := TouchArea { clicked => { root.clicked(); } moved => { root.hovered = self.has-hover; } } } }