Files
shadcn-slint/ui/pages/proxies.slint
2026-01-30 20:32:37 +08:00

282 lines
12 KiB
Plaintext

// Proxies Page - Proxy nodes management with tabs and groups
import { Theme, Typography, SpacingSystem } from "../theme/theme.slint";
import { Item } from "../components/item.slint";
import { Badge } from "../components/badge.slint";
import { Button } from "../components/button.slint";
import { Empty } from "../components/empty.slint";
import { Container } from "../layouts/container.slint";
import { ProxyGroup, ProxyNode } from "../types.slint";
export component ProxiesPage inherits Rectangle {
in property <[ProxyGroup]> proxy-groups;
in-out property <string> proxy-mode: "rule"; // "rule" | "global" | "direct"
callback load-proxy-groups(string /* mode */);
callback select-proxy(string /* group-name */, string /* node-name */);
callback test-proxy-delay(string /* node-name */);
background: Theme.colors.background;
VerticalLayout {
// Fixed header with tabs
Rectangle {
height: 48px;
background: Theme.colors.background;
HorizontalLayout {
padding: SpacingSystem.spacing.s4;
spacing: SpacingSystem.spacing.s4;
alignment: space-between;
Text {
text: "Proxies";
font-size: Typography.sizes.xl;
font-weight: Typography.weights.bold;
color: Theme.colors.foreground;
vertical-alignment: center;
}
// Mode tabs
HorizontalLayout {
spacing: SpacingSystem.spacing.s1;
Rectangle {
width: 80px;
height: 32px;
background: proxy-mode == "rule" ? Theme.colors.primary : Theme.colors.muted;
border-radius: SpacingSystem.radius.md;
TouchArea {
clicked => {
proxy-mode = "rule";
root.load-proxy-groups("rule");
}
}
Text {
text: "Rule";
font-size: Typography.sizes.sm;
font-weight: Typography.weights.medium;
color: proxy-mode == "rule" ? Theme.colors.primary-foreground : Theme.colors.muted-foreground;
horizontal-alignment: center;
vertical-alignment: center;
}
}
Rectangle {
width: 80px;
height: 32px;
background: proxy-mode == "global" ? Theme.colors.primary : Theme.colors.muted;
border-radius: SpacingSystem.radius.md;
TouchArea {
clicked => {
proxy-mode = "global";
root.load-proxy-groups("global");
}
}
Text {
text: "Global";
font-size: Typography.sizes.sm;
font-weight: Typography.weights.medium;
color: proxy-mode == "global" ? Theme.colors.primary-foreground : Theme.colors.muted-foreground;
horizontal-alignment: center;
vertical-alignment: center;
}
}
Rectangle {
width: 80px;
height: 32px;
background: proxy-mode == "direct" ? Theme.colors.primary : Theme.colors.muted;
border-radius: SpacingSystem.radius.md;
TouchArea {
clicked => {
proxy-mode = "direct";
root.load-proxy-groups("direct");
}
}
Text {
text: "Direct";
font-size: Typography.sizes.sm;
font-weight: Typography.weights.medium;
color: proxy-mode == "direct" ? Theme.colors.primary-foreground : Theme.colors.muted-foreground;
horizontal-alignment: center;
vertical-alignment: center;
}
}
}
}
}
// Scrollable content
Flickable {
viewport-height: content-layout.preferred-height + SpacingSystem.spacing.s4 * 2;
content-layout := VerticalLayout {
padding: SpacingSystem.spacing.s4;
spacing: SpacingSystem.spacing.s4;
// Empty state
if proxy-groups.length == 0: Empty {
height: 300px;
icon: "🌍";
title: "No Groups";
description: "Proxy groups will appear here";
}
// Proxy groups
for group in proxy-groups: Container {
VerticalLayout {
padding: SpacingSystem.spacing.s4;
spacing: SpacingSystem.spacing.s3;
// Group header
HorizontalLayout {
spacing: SpacingSystem.spacing.s3;
alignment: space-between;
VerticalLayout {
spacing: SpacingSystem.spacing.s1;
Text {
text: group.name;
font-size: Typography.sizes.base;
font-weight: Typography.weights.semibold;
color: Theme.colors.foreground;
}
Text {
text: "Type: " + group.type + " | Current: " + group.now;
font-size: Typography.sizes.xs;
color: Theme.colors.muted-foreground;
}
}
Text {
text: group.nodes.length + " nodes";
font-size: Typography.sizes.xs;
color: Theme.colors.muted-foreground;
vertical-alignment: center;
}
}
// Divider
Rectangle {
height: 1px;
background: Theme.colors.border;
}
// Proxy nodes
VerticalLayout {
spacing: SpacingSystem.spacing.s2;
for node in group.nodes: Rectangle {
height: 60px;
background: node.name == group.now ? Theme.colors.accent : transparent;
border-radius: SpacingSystem.radius.md;
border-width: 1px;
border-color: Theme.colors.border;
HorizontalLayout {
padding: SpacingSystem.spacing.s3;
spacing: SpacingSystem.spacing.s3;
alignment: space-between;
// Node info
HorizontalLayout {
spacing: SpacingSystem.spacing.s3;
// Status indicator
Rectangle {
width: 8px;
height: 8px;
border-radius: 4px;
background: node.delay >= 0 && node.delay < 200 ? #22c55e :
node.delay >= 200 && node.delay < 500 ? #f59e0b :
node.delay >= 500 ? #ef4444 : Theme.colors.muted;
}
// Node details
VerticalLayout {
spacing: SpacingSystem.spacing.s1;
Text {
text: node.name;
font-size: Typography.sizes.sm;
font-weight: Typography.weights.medium;
color: Theme.colors.foreground;
}
HorizontalLayout {
spacing: SpacingSystem.spacing.s2;
Badge {
text: node.type;
variant: "outline";
}
if node.udp: Badge {
text: "UDP";
variant: "outline";
}
if node.delay >= 0: Text {
text: node.delay + "ms";
font-size: Typography.sizes.xs;
color: Theme.colors.muted-foreground;
}
}
}
}
// Actions
HorizontalLayout {
spacing: SpacingSystem.spacing.s2;
Button {
text: "🔍";
variant: "ghost";
size: "sm";
clicked => {
root.test-proxy-delay(node.name);
}
}
if node.name != group.now: Button {
text: "Select";
variant: "outline";
size: "sm";
clicked => {
root.select-proxy(group.name, node.name);
}
}
if node.name == group.now: Badge {
text: "Active";
variant: "default";
}
}
}
TouchArea {
clicked => {
if node.name != group.now {
root.select-proxy(group.name, node.name);
}
}
}
}
}
}
}
}
}
}
}