This commit is contained in:
me
2026-01-30 20:32:37 +08:00
parent 222449cb1b
commit a4e6557d1b
24 changed files with 2189 additions and 6235 deletions

View File

@@ -1,203 +1,225 @@
import { Theme, SpacingSystem, Typography } from "../theme/theme.slint";
import { Card } from "../components/card.slint";
// Profiles Page - Configuration profiles management
import { Theme, Typography, SpacingSystem } from "../theme/theme.slint";
import { Card, CardHeader, CardTitle, CardContent, CardFooter } from "../components/card.slint";
import { Button } from "../components/button.slint";
import { Badge } from "../components/badge.slint";
import { Progress } from "../components/progress.slint";
import { Empty } from "../components/empty.slint";
import { Profile } from "../types.slint";
export struct ProfileData {
id: string,
name: string,
type: string, // "local" or "remote"
description: string,
usage: float, // For remote profiles
total: float, // For remote profiles
updated: string,
}
export component ProfilesPage inherits Rectangle {
in property <[Profile]> profiles;
export component ProfilesPage {
in property <[ProfileData]> profiles: [];
in property <string> selected-profile-id: "";
callback load-profiles();
callback select-profile(string /* profile-id */);
callback update-profile(string /* profile-id */);
callback delete-profile(string /* profile-id */);
callback add-profile();
callback refresh-all();
callback select-profile(string);
callback edit-profile(string);
callback delete-profile(string);
callback refresh-profile(string);
VerticalLayout {
padding: SpacingSystem.spacing.s4;
spacing: SpacingSystem.spacing.s4;
// Header
HorizontalLayout {
spacing: SpacingSystem.spacing.s3;
alignment: space-between;
Text {
text: "Profiles";
color: Theme.colors.foreground;
font-size: Typography.sizes.xl;
font-weight: Typography.weights.bold;
vertical-alignment: center;
}
HorizontalLayout {
spacing: SpacingSystem.spacing.s2;
Button {
text: "+";
variant: "ghost";
clicked => {
root.add-profile();
}
}
Button {
text: "↻";
variant: "ghost";
clicked => {
root.refresh-all();
}
}
}
}
// Profile Grid
ScrollView {
GridLayout {
spacing: SpacingSystem.spacing.s3;
for profile in root.profiles: ProfileItem {
profile-data: profile;
selected: profile.id == root.selected-profile-id;
clicked => {
root.select-profile(profile.id);
}
refresh-clicked => {
root.refresh-profile(profile.id);
}
}
}
}
}
}
component ProfileItem {
in property <ProfileData> profile-data;
in property <bool> selected: false;
callback clicked();
callback refresh-clicked();
private property <bool> hovered: false;
min-width: 288px; // 18rem
height: 96px;
states [
selected when root.selected: {
container.border-color: Theme.colors.primary.transparentize(0.5);
container.background: Theme.colors.accent.transparentize(0.4);
}
]
container := Card {
border-width: 2px;
VerticalLa spacing: SpacingSystem.spacing.s2;
// Header
background: Theme.colors.background;
VerticalLayout {
// Fixed header
Rectangle {
height: 48px;
background: Theme.colors.background;
HorizontalLayout {
spacing: SpacingSystem.spacing.s2;
padding: SpacingSystem.spacing.s4;
spacing: SpacingSystem.spacing.s4;
alignment: space-between;
Text {
text: "Profiles";
font-size: Typography.sizes.xl;
font-weight: Typography.weights.bold;
color: Theme.colors.foreground;
vertical-alignment: center;
}
// Action buttons
HorizontalLayout {
spacing: SpacingSystem.spacing.s2;
Text {
text: root.profile-data.type == "remote" ? "☁" : "📄";
font-size: Typography.sizes.lg;
vertical-alignment: center;
Button {
text: "";
variant: "ghost";
size: "sm";
clicked => {
root.add-profile();
}
}
{
text: root.profile-data.name;
color: Theme.colors.foreground;
font-size: Typography.sizes.base;
font-weight: Typography.weights.semibold;
vertical-alignment: center;
Button {
text: "🔄";
variant: "ghost";
size: "sm";
clicked => {
root.load-profiles();
}
}
}
if root.profile-data.type == "remote": Button {
width: 24px;
height: 24px;
text: "↻";
variant: "ghost";
clicked => {
root.refresh-clicked();
}
}
}
// Description
Text {
text: root.profile-data.description;
color: Theme.colors.muted-foreground;
font-size: Typography.sizes.xs;
overflow: elide;
}
// Progress bar for remote profiles
if root.profile-data.type == "remote": VerticalLayout {
spacing: SpacingSystem.spacing.s1;
Progress {
value: (root.profile-data.usage / root.profile-data.total) * 100;
}
HorizontalLayout {
alignment: space-between;
Text {
text: "Updated: " + root.profile-data.updated;
color: Theme.colors.muted-foreground;
font-size: 10px;
}
Text {
text: root.profile-data.usage + " / " + root.profile-data.total + " GB";
color: Theme.colors.oreground;
font-size: 10px;
}
}
}
// Local profile info
if root.profile-data.type == "local": HorizontalLayout {
alignment: space-between;
Text {
text: "Local Profile";
color: Theme.colors.muted-foreground;
font-size: 10px;
}
}
}
touch := TouchArea {
clicked => {
root.clicked();
}
moved => {
root.hovered = self.has-hover;
// 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 profiles.length == 0: Empty {
height: 300px;
icon: "📋";
title: "No Profiles";
description: "Click + to add a new profile";
}
// Profile list
for profile in profiles: Card {
hoverable: true;
CardHeader {
HorizontalLayout {
spacing: SpacingSystem.spacing.s2;
alignment: space-between;
HorizontalLayout {
spacing: SpacingSystem.spacing.s2;
Text {
text: "☁";
font-size: Typography.sizes.xl;
vertical-alignment: center;
}
VerticalLayout {
spacing: SpacingSystem.spacing.s1;
CardTitle {
text: profile.name;
}
HorizontalLayout {
spacing: SpacingSystem.spacing.s1;
Badge {
text: profile.type;
variant: "outline";
}
if profile.selected: Badge {
text: "Active";
variant: "default";
}
}
}
}
Button {
text: "🔄";
variant: "ghost";
size: "sm";
clicked => {
root.update-profile(profile.id);
}
}
}
}
CardContent {
VerticalLayout {
spacing: SpacingSystem.spacing.s2;
// Usage
HorizontalLayout {
spacing: SpacingSystem.spacing.s2;
alignment: space-between;
Text {
text: "Usage:";
font-size: Typography.sizes.xs;
color: Theme.colors.muted-foreground;
}
Text {
text: profile.usage;
font-size: Typography.sizes.xs;
color: Theme.colors.foreground;
font-weight: Typography.weights.medium;
}
}
// Expire date
HorizontalLayout {
spacing: SpacingSystem.spacing.s2;
alignment: space-between;
Text {
text: "Expires:";
font-size: Typography.sizes.xs;
color: Theme.colors.muted-foreground;
}
Text {
text: profile.expire-at;
font-size: Typography.sizes.xs;
color: Theme.colors.foreground;
font-weight: Typography.weights.medium;
}
}
// Updated date
HorizontalLayout {
spacing: SpacingSystem.spacing.s2;
alignment: space-between;
Text {
text: "Updated:";
font-size: Typography.sizes.xs;
color: Theme.colors.muted-foreground;
}
Text {
text: profile.updated-at;
font-size: Typography.sizes.xs;
color: Theme.colors.muted-foreground;
}
}
}
}
CardFooter {
HorizontalLayout {
spacing: SpacingSystem.spacing.s2;
if !profile.selected: Button {
text: "Select";
variant: "default";
size: "sm";
clicked => {
root.select-profile(profile.id);
}
}
Button {
text: "Delete";
variant: "destructive";
size: "sm";
clicked => {
root.delete-profile(profile.id);
}
}
}
}
}
}
}
}