800 lines
28 KiB
Plaintext
800 lines
28 KiB
Plaintext
import { Theme, SpacingSystem, Typography } from "./theme/theme.slint";
|
|
import { Button } from "./components/button.slint";
|
|
import { Card } from "./components/card.slint";
|
|
import { Switch } from "./components/switch.slint";
|
|
import { Badge } from "./components/badge.slint";
|
|
import { Progress } from "./components/progress.slint";
|
|
import { ScrollView } from "std-widgets.slint";
|
|
|
|
// Helper components - must be defined before they are used
|
|
|
|
component SettingRow {
|
|
in property <string> label: "";
|
|
height: 52px;
|
|
HorizontalLayout {
|
|
spacing: 12px;
|
|
alignment: space-between;
|
|
Text {
|
|
text: root.label;
|
|
color: Theme.colors.foreground;
|
|
font-size: 14px;
|
|
font-weight: 500;
|
|
vertical-alignment: center;
|
|
}
|
|
@children
|
|
}
|
|
}
|
|
|
|
component ProfileCard {
|
|
in property <string> name: "";
|
|
in property <string> type-icon: "";
|
|
in property <string> description: "";
|
|
in property <bool> is-remote: false;
|
|
in property <float> usage: 0;
|
|
in property <float> total: 100;
|
|
in property <string> updated: "";
|
|
min-width: 300px;
|
|
height: 140px;
|
|
Card {
|
|
VerticalLayout {
|
|
spacing: 10px;
|
|
HorizontalLayout {
|
|
spacing: 10px;
|
|
Text {
|
|
text: root.type-icon;
|
|
font-size: 20px;
|
|
vertical-alignment: center;
|
|
}
|
|
Text {
|
|
text: root.name;
|
|
color: Theme.colors.foreground;
|
|
font-size: 16px;
|
|
font-weight: 600;
|
|
vertical-alignment: center;
|
|
}
|
|
Rectangle { horizontal-stretch: 1; }
|
|
if root.is-remote: Button {
|
|
text: "↻";
|
|
variant: "ghost";
|
|
width: 28px;
|
|
height: 28px;
|
|
}
|
|
}
|
|
Text {
|
|
text: root.description;
|
|
color: Theme.colors.muted-foreground;
|
|
font-size: 13px;
|
|
overflow: elide;
|
|
}
|
|
if root.is-remote: VerticalLayout {
|
|
spacing: 6px;
|
|
Progress {
|
|
value: (root.usage / root.total) * 100;
|
|
}
|
|
HorizontalLayout {
|
|
alignment: space-between;
|
|
Text {
|
|
text: "Updated: " + root.updated;
|
|
color: Theme.colors.muted-foreground;
|
|
font-size: 11px;
|
|
}
|
|
Text {
|
|
text: root.usage + " / " + root.total + " GB";
|
|
color: Theme.colors.foreground;
|
|
font-size: 11px;
|
|
font-weight: 600;
|
|
}
|
|
}
|
|
}
|
|
if !root.is-remote: Text {
|
|
text: "Local Profile";
|
|
color: Theme.colors.muted-foreground;
|
|
font-size: 11px;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
component LogRow {
|
|
in property <string> time: "";
|
|
in property <string> level: "";
|
|
in property <string> message: "";
|
|
private property <color> level-color: level == "error" ? #ef4444 :
|
|
level == "warn" ? #eab308 :
|
|
level == "info" ? #3b82f6 : #6b7280;
|
|
height: 28px;
|
|
HorizontalLayout {
|
|
padding-left: 12px;
|
|
padding-right: 12px;
|
|
spacing: 12px;
|
|
Text {
|
|
text: root.time;
|
|
color: Theme.colors.muted-foreground;
|
|
font-size: 12px;
|
|
font-family: "monospace";
|
|
vertical-alignment: center;
|
|
width: 80px;
|
|
}
|
|
Text {
|
|
text: root.level;
|
|
color: root.level-color;
|
|
font-size: 12px;
|
|
font-family: "monospace";
|
|
font-weight: 700;
|
|
vertical-alignment: center;
|
|
width: 70px;
|
|
}
|
|
Text {
|
|
text: root.message;
|
|
color: Theme.colors.foreground;
|
|
font-size: 12px;
|
|
font-family: "monospace";
|
|
vertical-alignment: center;
|
|
}
|
|
}
|
|
}
|
|
|
|
component ConnectionItem {
|
|
in property <string> source: "";
|
|
in property <string> destination: "";
|
|
in property <string> protocol: "";
|
|
in property <string> upload: "";
|
|
in property <string> download: "";
|
|
in property <string> duration: "";
|
|
height: 72px;
|
|
Card {
|
|
HorizontalLayout {
|
|
spacing: 16px;
|
|
Rectangle {
|
|
width: 48px;
|
|
height: 48px;
|
|
border-radius: 10px;
|
|
background: Theme.colors.primary.transparentize(0.9);
|
|
Text {
|
|
text: "🔗";
|
|
font-size: 20px;
|
|
horizontal-alignment: center;
|
|
vertical-alignment: center;
|
|
}
|
|
}
|
|
VerticalLayout {
|
|
spacing: 6px;
|
|
Text {
|
|
text: root.source + " → " + root.destination;
|
|
color: Theme.colors.foreground;
|
|
font-size: 14px;
|
|
font-weight: 500;
|
|
overflow: elide;
|
|
}
|
|
HorizontalLayout {
|
|
spacing: 16px;
|
|
Text {
|
|
text: root.protocol;
|
|
color: Theme.colors.muted-foreground;
|
|
font-size: 12px;
|
|
}
|
|
Text {
|
|
text: "↑ " + root.upload;
|
|
color: Theme.colors.primary;
|
|
font-size: 12px;
|
|
font-weight: 600;
|
|
}
|
|
Text {
|
|
text: "↓ " + root.download;
|
|
color: #22c55e;
|
|
font-size: 12px;
|
|
font-weight: 600;
|
|
}
|
|
Text {
|
|
text: root.duration;
|
|
color: Theme.colors.muted-foreground;
|
|
font-size: 12px;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
component ProxyNode {
|
|
in property <string> name: "";
|
|
in property <string> type-text: "";
|
|
in property <string> latency: "";
|
|
min-width: 200px;
|
|
height: 70px;
|
|
Card {
|
|
HorizontalLayout {
|
|
spacing: 12px;
|
|
alignment: space-between;
|
|
VerticalLayout {
|
|
spacing: 6px;
|
|
Text {
|
|
text: root.name;
|
|
color: Theme.colors.foreground;
|
|
font-size: 14px;
|
|
font-weight: 600;
|
|
}
|
|
Text {
|
|
text: root.type-text;
|
|
color: Theme.colors.muted-foreground;
|
|
font-size: 12px;
|
|
}
|
|
}
|
|
Text {
|
|
text: root.latency;
|
|
color: Theme.colors.primary;
|
|
font-size: 14px;
|
|
font-weight: 600;
|
|
vertical-alignment: center;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Exported page components
|
|
|
|
export component HomePage {
|
|
VerticalLayout {
|
|
padding: 24px;
|
|
spacing: 20px;
|
|
Text {
|
|
text: "Home";
|
|
font-size: 28px;
|
|
font-weight: 700;
|
|
color: Theme.colors.foreground;
|
|
}
|
|
ScrollView {
|
|
VerticalLayout {
|
|
spacing: 20px;
|
|
Card {
|
|
VerticalLayout {
|
|
spacing: 16px;
|
|
HorizontalLayout {
|
|
spacing: 16px;
|
|
Rectangle {
|
|
width: 56px;
|
|
height: 56px;
|
|
border-radius: 12px;
|
|
background: Theme.colors.primary.transparentize(0.9);
|
|
Text {
|
|
text: "☁";
|
|
color: Theme.colors.primary;
|
|
font-size: 28px;
|
|
horizontal-alignment: center;
|
|
vertical-alignment: center;
|
|
}
|
|
}
|
|
VerticalLayout {
|
|
spacing: 6px;
|
|
HorizontalLayout {
|
|
spacing: 10px;
|
|
Text {
|
|
text: "NanoCloud";
|
|
color: Theme.colors.foreground;
|
|
font-size: 20px;
|
|
font-weight: 700;
|
|
vertical-alignment: center;
|
|
}
|
|
}
|
|
HorizontalLayout {
|
|
spacing: 8px;
|
|
Text {
|
|
text: "🌐 Free-Japan1-Ver.7";
|
|
color: Theme.colors.muted-foreground;
|
|
font-size: 13px;
|
|
vertical-alignment: center;
|
|
}
|
|
Badge {
|
|
text: "Vmess";
|
|
variant: "outline";
|
|
}
|
|
Badge {
|
|
text: "UDP";
|
|
variant: "secondary";
|
|
}
|
|
}
|
|
}
|
|
Rectangle { horizontal-stretch: 1; }
|
|
Button {
|
|
text: "↻";
|
|
variant: "ghost";
|
|
width: 40px;
|
|
height: 40px;
|
|
}
|
|
}
|
|
Rectangle {
|
|
height: 1px;
|
|
background: Theme.colors.border;
|
|
}
|
|
GridLayout {
|
|
spacing: 20px;
|
|
Row {
|
|
VerticalLayout {
|
|
spacing: 6px;
|
|
Text {
|
|
text: "Usage / Total";
|
|
color: Theme.colors.muted-foreground;
|
|
font-size: 12px;
|
|
font-weight: 600;
|
|
}
|
|
Text {
|
|
text: "1.26GB / 100GB";
|
|
color: Theme.colors.foreground;
|
|
font-size: 16px;
|
|
font-weight: 600;
|
|
}
|
|
Progress { value: 1.26; }
|
|
}
|
|
VerticalLayout {
|
|
spacing: 6px;
|
|
Text {
|
|
text: "Expiry Date";
|
|
color: Theme.colors.muted-foreground;
|
|
font-size: 12px;
|
|
font-weight: 600;
|
|
}
|
|
Text {
|
|
text: "2025-11-11";
|
|
color: Theme.colors.foreground;
|
|
font-size: 16px;
|
|
font-weight: 600;
|
|
}
|
|
}
|
|
VerticalLayout {
|
|
spacing: 6px;
|
|
Text {
|
|
text: "Last Update";
|
|
color: Theme.colors.muted-foreground;
|
|
font-size: 12px;
|
|
font-weight: 600;
|
|
}
|
|
Text {
|
|
text: "2025-10-12";
|
|
color: Theme.colors.foreground;
|
|
font-size: 16px;
|
|
font-weight: 600;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
Card {
|
|
VerticalLayout {
|
|
spacing: 16px;
|
|
Text {
|
|
text: "Location Info";
|
|
font-size: 16px;
|
|
font-weight: 600;
|
|
color: Theme.colors.foreground;
|
|
}
|
|
HorizontalLayout {
|
|
spacing: 32px;
|
|
VerticalLayout {
|
|
spacing: 6px;
|
|
Text {
|
|
text: "IP Address";
|
|
color: Theme.colors.muted-foreground;
|
|
font-size: 12px;
|
|
font-weight: 600;
|
|
}
|
|
Text {
|
|
text: "47.238.198.100";
|
|
color: Theme.colors.foreground;
|
|
font-size: 14px;
|
|
font-weight: 500;
|
|
}
|
|
}
|
|
VerticalLayout {
|
|
spacing: 6px;
|
|
Text {
|
|
text: "Location";
|
|
color: Theme.colors.muted-foreground;
|
|
font-size: 12px;
|
|
font-weight: 600;
|
|
}
|
|
Text {
|
|
text: "Japan · Tokyo";
|
|
color: Theme.colors.foreground;
|
|
font-size: 14px;
|
|
font-weight: 500;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
Card {
|
|
VerticalLayout {
|
|
spacing: 0;
|
|
Text {
|
|
text: "Quick Settings";
|
|
font-size: 16px;
|
|
font-weight: 600;
|
|
color: Theme.colors.foreground;
|
|
padding-bottom: 12px;
|
|
}
|
|
SettingRow {
|
|
label: "TUN Mode";
|
|
Switch { checked: false; }
|
|
}
|
|
Rectangle {
|
|
height: 1px;
|
|
background: Theme.colors.border;
|
|
}
|
|
SettingRow {
|
|
label: "System Proxy";
|
|
Switch { checked: false; }
|
|
}
|
|
Rectangle {
|
|
height: 1px;
|
|
background: Theme.colors.border;
|
|
}
|
|
SettingRow {
|
|
label: "Proxy Port";
|
|
Text {
|
|
text: "7890";
|
|
color: Theme.colors.primary;
|
|
font-size: 14px;
|
|
font-weight: 600;
|
|
vertical-alignment: center;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
export component ProfilesPage {
|
|
VerticalLayout {
|
|
padding: 24px;
|
|
spacing: 20px;
|
|
HorizontalLayout {
|
|
spacing: 12px;
|
|
alignment: space-between;
|
|
Text {
|
|
text: "Profiles";
|
|
font-size: 28px;
|
|
font-weight: 700;
|
|
color: Theme.colors.foreground;
|
|
vertical-alignment: center;
|
|
}
|
|
HorizontalLayout {
|
|
spacing: 8px;
|
|
Button {
|
|
text: "+ Add";
|
|
variant: "default";
|
|
}
|
|
Button {
|
|
text: "↻";
|
|
variant: "ghost";
|
|
}
|
|
}
|
|
}
|
|
ScrollView {
|
|
GridLayout {
|
|
spacing: 16px;
|
|
Row {
|
|
ProfileCard {
|
|
name: "NanoCloud";
|
|
type-icon: "☁";
|
|
description: "Free tier subscription - Japan servers";
|
|
is-remote: true;
|
|
usage: 1.26;
|
|
total: 100.0;
|
|
updated: "Just now";
|
|
}
|
|
ProfileCard {
|
|
name: "Local Config";
|
|
type-icon: "📄";
|
|
description: "Custom local configuration file";
|
|
is-remote: false;
|
|
updated: "2025-01-15";
|
|
}
|
|
ProfileCard {
|
|
name: "Premium VPN";
|
|
type-icon: "☁";
|
|
description: "Premium subscription with global servers";
|
|
is-remote: true;
|
|
usage: 45.8;
|
|
total: 500.0;
|
|
updated: "2 hours ago";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
export component LogsPage {
|
|
VerticalLayout {
|
|
padding: 24px;
|
|
spacing: 20px;
|
|
Text {
|
|
text: "Logs";
|
|
font-size: 28px;
|
|
font-weight: 700;
|
|
color: Theme.colors.foreground;
|
|
}
|
|
Card {
|
|
height: 500px;
|
|
ScrollView {
|
|
VerticalLayout {
|
|
spacing: 0;
|
|
LogRow {
|
|
time: "10:23:45";
|
|
level: "info";
|
|
message: "Mihomo core started successfully";
|
|
}
|
|
LogRow {
|
|
time: "10:23:46";
|
|
level: "info";
|
|
message: "HTTP proxy listening on 127.0.0.1:7890";
|
|
}
|
|
LogRow {
|
|
time: "10:23:47";
|
|
level: "debug";
|
|
message: "Loading configuration from config.yaml";
|
|
}
|
|
LogRow {
|
|
time: "10:23:48";
|
|
level: "info";
|
|
message: "Profile loaded: NanoCloud";
|
|
}
|
|
LogRow {
|
|
time: "10:23:50";
|
|
level: "warn";
|
|
message: "DNS resolution timeout for example.com";
|
|
}
|
|
LogRow {
|
|
time: "10:23:51";
|
|
level: "error";
|
|
message: "Failed to connect to proxy server";
|
|
}
|
|
LogRow {
|
|
time: "10:23:52";
|
|
level: "info";
|
|
message: "Retrying connection...";
|
|
}
|
|
LogRow {
|
|
time: "10:23:53";
|
|
level: "info";
|
|
message: "Connection established successfully";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
export component ConnectionsPage {
|
|
VerticalLayout {
|
|
padding: 24px;
|
|
spacing: 20px;
|
|
Text {
|
|
text: "Connections";
|
|
font-size: 28px;
|
|
font-weight: 700;
|
|
color: Theme.colors.foreground;
|
|
}
|
|
ScrollView {
|
|
VerticalLayout {
|
|
spacing: 12px;
|
|
ConnectionItem {
|
|
source: "192.168.1.100:54321";
|
|
destination: "github.com:443";
|
|
protocol: "HTTPS";
|
|
upload: "1.2 KB/s";
|
|
download: "45.6 KB/s";
|
|
duration: "00:02:15";
|
|
}
|
|
ConnectionItem {
|
|
source: "192.168.1.100:54322";
|
|
destination: "google.com:443";
|
|
protocol: "HTTPS";
|
|
upload: "0.5 KB/s";
|
|
download: "12.3 KB/s";
|
|
duration: "00:01:30";
|
|
}
|
|
ConnectionItem {
|
|
source: "192.168.1.100:54323";
|
|
destination: "youtube.com:443";
|
|
protocol: "HTTPS";
|
|
upload: "2.1 KB/s";
|
|
download: "256.7 KB/s";
|
|
duration: "00:05:42";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
export component SettingsPage {
|
|
VerticalLayout {
|
|
padding: 24px;
|
|
spacing: 20px;
|
|
Text {
|
|
text: "Settings";
|
|
font-size: 28px;
|
|
font-weight: 700;
|
|
color: Theme.colors.foreground;
|
|
}
|
|
ScrollView {
|
|
VerticalLayout {
|
|
spacing: 16px;
|
|
Card {
|
|
VerticalLayout {
|
|
spacing: 0;
|
|
Text {
|
|
text: "App Settings";
|
|
font-size: 16px;
|
|
font-weight: 600;
|
|
color: Theme.colors.foreground;
|
|
padding-bottom: 12px;
|
|
}
|
|
SettingRow {
|
|
label: "Auto Start";
|
|
Switch { checked: false; }
|
|
}
|
|
Rectangle {
|
|
height: 1px;
|
|
background: Theme.colors.border;
|
|
}
|
|
SettingRow {
|
|
label: "Silent Start";
|
|
Switch { checked: false; }
|
|
}
|
|
Rectangle {
|
|
height: 1px;
|
|
background: Theme.colors.border;
|
|
}
|
|
SettingRow {
|
|
label: "Clash Core";
|
|
Switch { checked: true; }
|
|
}
|
|
}
|
|
}
|
|
Card {
|
|
VerticalLayout {
|
|
spacing: 0;
|
|
Text {
|
|
text: "Clash Settings";
|
|
font-size: 16px;
|
|
font-weight: 600;
|
|
color: Theme.colors.foreground;
|
|
padding-bottom: 12px;
|
|
}
|
|
SettingRow {
|
|
label: "Allow LAN";
|
|
Switch { checked: false; }
|
|
}
|
|
Rectangle {
|
|
height: 1px;
|
|
background: Theme.colors.border;
|
|
}
|
|
SettingRow {
|
|
label: "IPv6";
|
|
Switch { checked: false; }
|
|
}
|
|
Rectangle {
|
|
height: 1px;
|
|
background: Theme.colors.border;
|
|
}
|
|
SettingRow {
|
|
label: "Unified Delay";
|
|
Switch { checked: true; }
|
|
}
|
|
}
|
|
}
|
|
Card {
|
|
VerticalLayout {
|
|
spacing: 12px;
|
|
Text {
|
|
text: "About";
|
|
font-size: 16px;
|
|
font-weight: 600;
|
|
color: Theme.colors.foreground;
|
|
}
|
|
HorizontalLayout {
|
|
spacing: 12px;
|
|
alignment: space-between;
|
|
Text {
|
|
text: "App Version";
|
|
color: Theme.colors.muted-foreground;
|
|
font-size: 14px;
|
|
}
|
|
Text {
|
|
text: "1.0.0";
|
|
color: Theme.colors.foreground;
|
|
font-size: 14px;
|
|
font-weight: 600;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
export component ProxiesPage {
|
|
VerticalLayout {
|
|
padding: 24px;
|
|
spacing: 20px;
|
|
Text {
|
|
text: "Proxies";
|
|
font-size: 28px;
|
|
font-weight: 700;
|
|
color: Theme.colors.foreground;
|
|
}
|
|
ScrollView {
|
|
VerticalLayout {
|
|
spacing: 16px;
|
|
Card {
|
|
VerticalLayout {
|
|
spacing: 12px;
|
|
Text {
|
|
text: "Hong Kong";
|
|
font-size: 16px;
|
|
font-weight: 600;
|
|
color: Theme.colors.foreground;
|
|
}
|
|
GridLayout {
|
|
spacing: 12px;
|
|
Row {
|
|
ProxyNode {
|
|
name: "HK-01";
|
|
type-text: "Vmess";
|
|
latency: "45ms";
|
|
}
|
|
ProxyNode {
|
|
name: "HK-02";
|
|
type-text: "Vmess";
|
|
latency: "52ms";
|
|
}
|
|
ProxyNode {
|
|
name: "HK-03";
|
|
type-text: "Trojan";
|
|
latency: "38ms";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
Card {
|
|
VerticalLayout {
|
|
spacing: 12px;
|
|
Text {
|
|
text: "Japan";
|
|
font-size: 16px;
|
|
font-weight: 600;
|
|
color: Theme.colors.foreground;
|
|
}
|
|
GridLayout {
|
|
spacing: 12px;
|
|
Row {
|
|
ProxyNode {
|
|
name: "JP-Tokyo-01";
|
|
type-text: "Vmess";
|
|
latency: "122ms";
|
|
}
|
|
ProxyNode {
|
|
name: "JP-Tokyo-02";
|
|
type-text: "SS";
|
|
latency: "115ms";
|
|
}
|
|
ProxyNode {
|
|
name: "JP-Osaka-01";
|
|
type-text: "Vmess";
|
|
latency: "135ms";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|