20260619
This commit is contained in:
499
ui/sidebar.slint
Normal file
499
ui/sidebar.slint
Normal file
@@ -0,0 +1,499 @@
|
||||
import { Theme } from "theme.slint";
|
||||
import { Sparkline } from "widgets.slint";
|
||||
|
||||
// One filesystem row for the disk-usage panel.
|
||||
export struct DiskInfo {
|
||||
path: string,
|
||||
detail: string, // "available/total"
|
||||
percent: float, // used fraction 0.0..1.0
|
||||
}
|
||||
|
||||
export struct ProcessInfo {
|
||||
mem: string,
|
||||
cpu: string,
|
||||
name: string,
|
||||
}
|
||||
|
||||
// --- StatRow ---------------------------------------------------------------
|
||||
// A single label / percentage / progress-bar row used for CPU / memory / swap.
|
||||
component StatRow inherits Rectangle {
|
||||
in property <string> label;
|
||||
in property <float> percent; // 0.0 .. 1.0
|
||||
in property <string> detail;
|
||||
in property <brush> bar-color: Theme.accent;
|
||||
|
||||
height: 30px;
|
||||
border-radius: Theme.radius-sm;
|
||||
background: Theme.bg-panel-alt.with-alpha(0.38);
|
||||
|
||||
HorizontalLayout {
|
||||
height: parent.height;
|
||||
padding-left: 6px;
|
||||
padding-right: 6px;
|
||||
spacing: 7px;
|
||||
|
||||
Text {
|
||||
width: 32px;
|
||||
height: parent.height;
|
||||
text: label;
|
||||
color: Theme.text-secondary;
|
||||
font-size: Theme.fs-xs;
|
||||
vertical-alignment: center;
|
||||
overflow: elide;
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
y: (parent.height - self.height) / 2;
|
||||
height: 20px;
|
||||
horizontal-stretch: 1;
|
||||
background: Theme.bg-root.with-alpha(0.56);
|
||||
border-radius: Theme.radius-sm;
|
||||
border-width: 1px;
|
||||
border-color: Theme.border-subtle.with-alpha(0.58);
|
||||
clip: true;
|
||||
|
||||
Rectangle {
|
||||
x: 0;
|
||||
height: parent.height;
|
||||
width: parent.width * clamp(percent, 0.0, 1.0);
|
||||
background: bar-color.with-alpha(0.52);
|
||||
border-radius: Theme.radius-sm;
|
||||
animate width { duration: 500ms; easing: ease-out; }
|
||||
}
|
||||
|
||||
HorizontalLayout {
|
||||
height: parent.height;
|
||||
padding-left: 7px;
|
||||
padding-right: 7px;
|
||||
Text {
|
||||
height: parent.height;
|
||||
text: Math.round(percent * 100) + "%";
|
||||
color: Theme.text-primary;
|
||||
font-size: Theme.fs-xs;
|
||||
font-weight: 600;
|
||||
vertical-alignment: center;
|
||||
}
|
||||
Rectangle { horizontal-stretch: 1; }
|
||||
Text {
|
||||
height: parent.height;
|
||||
text: detail;
|
||||
color: Theme.text-primary;
|
||||
font-size: Theme.fs-xs;
|
||||
horizontal-alignment: right;
|
||||
vertical-alignment: center;
|
||||
overflow: elide;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component ProcessTable inherits VerticalLayout {
|
||||
in property <[ProcessInfo]> processes;
|
||||
|
||||
vertical-stretch: 0;
|
||||
spacing: 1px;
|
||||
|
||||
HorizontalLayout {
|
||||
height: 18px;
|
||||
padding-left: 6px;
|
||||
padding-right: 6px;
|
||||
spacing: 4px;
|
||||
Text {
|
||||
width: 42px;
|
||||
text: @tr("Memory");
|
||||
color: Theme.text-muted;
|
||||
font-size: Theme.fs-xs;
|
||||
vertical-alignment: center;
|
||||
}
|
||||
Text {
|
||||
width: 32px;
|
||||
text: "CPU";
|
||||
color: Theme.text-muted;
|
||||
font-size: Theme.fs-xs;
|
||||
vertical-alignment: center;
|
||||
}
|
||||
Text {
|
||||
text: @tr("Program");
|
||||
color: Theme.text-muted;
|
||||
font-size: Theme.fs-xs;
|
||||
vertical-alignment: center;
|
||||
horizontal-stretch: 1;
|
||||
}
|
||||
}
|
||||
|
||||
for p in root.processes : Rectangle {
|
||||
height: 18px;
|
||||
border-radius: 2px;
|
||||
background: row-ta.has-hover ? Theme.bg-hover.with-alpha(0.78) : Theme.bg-panel-alt.with-alpha(0.22);
|
||||
row-ta := TouchArea {}
|
||||
HorizontalLayout {
|
||||
height: parent.height;
|
||||
padding-left: 6px;
|
||||
padding-right: 6px;
|
||||
spacing: 4px;
|
||||
Text {
|
||||
width: 42px;
|
||||
text: p.mem;
|
||||
color: Theme.text-secondary;
|
||||
font-size: Theme.fs-xs;
|
||||
vertical-alignment: center;
|
||||
overflow: elide;
|
||||
}
|
||||
Text {
|
||||
width: 32px;
|
||||
text: p.cpu;
|
||||
color: Theme.text-secondary;
|
||||
font-size: Theme.fs-xs;
|
||||
vertical-alignment: center;
|
||||
overflow: elide;
|
||||
}
|
||||
Text {
|
||||
text: p.name;
|
||||
color: Theme.text-primary;
|
||||
font-size: Theme.fs-xs;
|
||||
vertical-alignment: center;
|
||||
horizontal-stretch: 1;
|
||||
overflow: elide;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- NetGraph --------------------------------------------------------------
|
||||
// One network throughput graph: a header row (↑ tx / ↓ rx, plus an optional
|
||||
// NIC selector dropdown) over an auto-scaled sparkline.
|
||||
component NetGraph inherits VerticalLayout {
|
||||
in property <string> up-text;
|
||||
in property <string> down-text;
|
||||
in property <[float]> history;
|
||||
in property <bool> show-selector: false;
|
||||
in property <[string]> ifaces;
|
||||
in property <string> selected;
|
||||
callback iface-selected(string);
|
||||
|
||||
// Stay at preferred height; without this the inherited VerticalLayout grabs
|
||||
// a stretch share of the sidebar and balloons the sparkline into blank space.
|
||||
vertical-stretch: 0;
|
||||
spacing: 3px;
|
||||
|
||||
HorizontalLayout {
|
||||
height: 18px;
|
||||
spacing: 3px;
|
||||
Text { // ↑ upload (green)
|
||||
width: 12px;
|
||||
height: parent.height;
|
||||
text: "\u{E5D8}";
|
||||
font-family: "Material Icons";
|
||||
color: Theme.success;
|
||||
font-size: Theme.fs-xs;
|
||||
horizontal-alignment: center;
|
||||
vertical-alignment: center;
|
||||
}
|
||||
Text {
|
||||
height: parent.height;
|
||||
text: root.up-text;
|
||||
color: Theme.text-secondary;
|
||||
font-size: Theme.fs-xs;
|
||||
vertical-alignment: center;
|
||||
}
|
||||
Text { // ↓ download (blue)
|
||||
width: 12px;
|
||||
height: parent.height;
|
||||
text: "\u{E5DB}";
|
||||
font-family: "Material Icons";
|
||||
color: Theme.accent;
|
||||
font-size: Theme.fs-xs;
|
||||
horizontal-alignment: center;
|
||||
vertical-alignment: center;
|
||||
}
|
||||
Text {
|
||||
height: parent.height;
|
||||
text: root.down-text;
|
||||
color: Theme.text-secondary;
|
||||
font-size: Theme.fs-xs;
|
||||
vertical-alignment: center;
|
||||
}
|
||||
Rectangle { horizontal-stretch: 1; } // spacer pushes selector to the right
|
||||
|
||||
if root.show-selector : Rectangle {
|
||||
width: 64px;
|
||||
height: 18px;
|
||||
border-radius: 3px;
|
||||
background: sel-ta.has-hover ? Theme.bg-hover : Theme.bg-elevated;
|
||||
|
||||
HorizontalLayout {
|
||||
height: parent.height;
|
||||
padding-left: 5px; padding-right: 4px; spacing: 2px;
|
||||
Text {
|
||||
height: parent.height;
|
||||
text: root.selected;
|
||||
color: Theme.text-primary;
|
||||
font-size: Theme.fs-xs;
|
||||
vertical-alignment: center;
|
||||
horizontal-stretch: 1;
|
||||
overflow: elide;
|
||||
}
|
||||
Text {
|
||||
height: parent.height;
|
||||
text: "\u{E5CF}";
|
||||
font-family: "Material Icons";
|
||||
color: Theme.text-muted;
|
||||
font-size: 8px;
|
||||
vertical-alignment: center;
|
||||
}
|
||||
}
|
||||
|
||||
sel-ta := TouchArea {
|
||||
mouse-cursor: pointer;
|
||||
clicked => { popup.show(); }
|
||||
}
|
||||
|
||||
popup := PopupWindow {
|
||||
x: 0; y: parent.height + 2px;
|
||||
width: 96px;
|
||||
Rectangle {
|
||||
background: Theme.bg-panel;
|
||||
border-radius: 4px;
|
||||
border-width: 1px;
|
||||
border-color: Theme.border-strong;
|
||||
drop-shadow-blur: 10px;
|
||||
drop-shadow-color: #00000050;
|
||||
VerticalLayout {
|
||||
padding: 4px;
|
||||
spacing: 1px;
|
||||
for iface in root.ifaces : Rectangle {
|
||||
height: 22px;
|
||||
border-radius: 3px;
|
||||
background: row-ta.has-hover ? Theme.bg-hover : transparent;
|
||||
row-ta := TouchArea {
|
||||
mouse-cursor: pointer;
|
||||
clicked => { root.iface-selected(iface); }
|
||||
}
|
||||
Text {
|
||||
x: 6px;
|
||||
text: iface;
|
||||
color: Theme.text-primary;
|
||||
font-size: Theme.fs-xs;
|
||||
vertical-alignment: center;
|
||||
height: parent.height;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Sparkline {
|
||||
height: 48px;
|
||||
values: root.history;
|
||||
line-color: Theme.accent;
|
||||
}
|
||||
}
|
||||
|
||||
// --- Sidebar ---------------------------------------------------------------
|
||||
// Left-hand panel showing local machine health, mimicking FinalShell's
|
||||
// @tr("Status") column but kept minimal for the v0.1 baseline.
|
||||
export component Sidebar inherits Rectangle {
|
||||
in property <bool> app-background-active: false;
|
||||
in property <float> ui-opacity: 0.72;
|
||||
in property <string> connection-state: @tr("Not connected");
|
||||
in property <int> conn-state; // 0 gray / 1 green / 2 yellow
|
||||
in property <string> resource-title: @tr("Local resources");
|
||||
in property <float> cpu-percent;
|
||||
in property <float> mem-percent;
|
||||
in property <float> swap-percent;
|
||||
in property <string> cpu-detail;
|
||||
in property <string> mem-detail;
|
||||
in property <string> swap-detail;
|
||||
in property <string> net-top-up;
|
||||
in property <string> net-top-down;
|
||||
in property <[float]> net-top-history;
|
||||
in property <[string]> net-ifaces;
|
||||
in property <string> net-selected;
|
||||
in property <bool> net-show-selector;
|
||||
in property <[DiskInfo]> disks;
|
||||
in property <[ProcessInfo]> processes;
|
||||
callback select-net-iface(string);
|
||||
|
||||
width: 220px;
|
||||
background: root.app-background-active ? Theme.bg-panel.with-alpha(root.ui-opacity) : Theme.bg-panel;
|
||||
border-width: 0;
|
||||
|
||||
VerticalLayout {
|
||||
padding: 10px;
|
||||
spacing: 10px;
|
||||
|
||||
// Status header
|
||||
VerticalLayout {
|
||||
spacing: 3px;
|
||||
Text {
|
||||
text: @tr("Running status");
|
||||
color: Theme.text-primary;
|
||||
font-size: Theme.fs-md;
|
||||
font-weight: 600;
|
||||
vertical-alignment: center;
|
||||
}
|
||||
Rectangle {
|
||||
height: 24px;
|
||||
border-radius: Theme.radius-sm;
|
||||
background: root.conn-state == 1 ? Theme.success.with-alpha(0.16)
|
||||
: root.conn-state == 2 ? Theme.warning.with-alpha(0.16)
|
||||
: Theme.bg-root;
|
||||
Text {
|
||||
x: 8px;
|
||||
width: parent.width - 16px;
|
||||
height: parent.height;
|
||||
text: connection-state;
|
||||
color: root.conn-state == 1 ? Theme.success
|
||||
: root.conn-state == 2 ? Theme.warning
|
||||
: Theme.text-secondary;
|
||||
font-size: Theme.fs-sm;
|
||||
vertical-alignment: center;
|
||||
overflow: elide;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle { height: 1px; background: Theme.border-subtle; }
|
||||
|
||||
// System stats — local machine on the welcome tab, the remote server
|
||||
// when a connected session tab is active.
|
||||
Text {
|
||||
text: root.resource-title;
|
||||
color: Theme.text-secondary;
|
||||
font-size: Theme.fs-sm;
|
||||
}
|
||||
|
||||
VerticalLayout {
|
||||
spacing: 6px;
|
||||
StatRow {
|
||||
label: "CPU";
|
||||
percent: root.cpu-percent;
|
||||
detail: root.cpu-detail;
|
||||
bar-color: Theme.accent;
|
||||
}
|
||||
StatRow {
|
||||
label: @tr("Memory");
|
||||
percent: root.mem-percent;
|
||||
detail: root.mem-detail;
|
||||
bar-color: Theme.success;
|
||||
}
|
||||
StatRow {
|
||||
label: @tr("Swap");
|
||||
percent: root.swap-percent;
|
||||
detail: root.swap-detail;
|
||||
bar-color: Theme.warning;
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle { height: 1px; background: Theme.border-subtle; }
|
||||
|
||||
ProcessTable {
|
||||
processes: root.processes;
|
||||
}
|
||||
|
||||
if root.net-top-up != "" || root.net-top-down != "" || root.net-show-selector : Rectangle {
|
||||
height: 1px;
|
||||
background: Theme.border-subtle;
|
||||
}
|
||||
|
||||
// Network — follows the active live session tab.
|
||||
if root.net-top-up != "" || root.net-top-down != "" || root.net-show-selector : VerticalLayout {
|
||||
vertical-stretch: 0;
|
||||
spacing: 6px;
|
||||
NetGraph {
|
||||
up-text: root.net-top-up;
|
||||
down-text: root.net-top-down;
|
||||
history: root.net-top-history;
|
||||
show-selector: root.net-show-selector;
|
||||
ifaces: root.net-ifaces;
|
||||
selected: root.net-selected;
|
||||
iface-selected(i) => { root.select-net-iface(i); }
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle { height: 1px; background: Theme.border-subtle; }
|
||||
|
||||
// Disk-usage panel (active tab's filesystems; local on the welcome tab).
|
||||
disk-section := VerticalLayout {
|
||||
vertical-stretch: 1;
|
||||
spacing: 3px;
|
||||
|
||||
HorizontalLayout {
|
||||
padding-left: 6px;
|
||||
padding-right: 6px;
|
||||
Text {
|
||||
text: @tr("Path");
|
||||
color: Theme.text-muted;
|
||||
font-size: Theme.fs-xs;
|
||||
horizontal-stretch: 1;
|
||||
}
|
||||
Text {
|
||||
text: @tr("Free/Total");
|
||||
color: Theme.text-muted;
|
||||
font-size: Theme.fs-xs;
|
||||
}
|
||||
}
|
||||
|
||||
disk-flick := Flickable {
|
||||
vertical-stretch: 1;
|
||||
viewport-width: self.width;
|
||||
// Grow only when the list overflows; otherwise match the visible
|
||||
// height so `alignment: start` keeps rows pinned to the top.
|
||||
viewport-height: max(self.height, disk-col.preferred-height);
|
||||
disk-col := VerticalLayout {
|
||||
alignment: start;
|
||||
spacing: 1px;
|
||||
for d[i] in root.disks : Rectangle {
|
||||
height: 20px;
|
||||
border-radius: 2px;
|
||||
clip: true;
|
||||
|
||||
// Usage fill: green-ish normally, amber/red when full.
|
||||
Rectangle {
|
||||
x: 0;
|
||||
y: 0;
|
||||
height: parent.height;
|
||||
width: parent.width * clamp(d.percent, 0.0, 1.0);
|
||||
background: d.percent > 0.9 ? Theme.danger.with-alpha(0.32)
|
||||
: d.percent > 0.75 ? Theme.warning.with-alpha(0.30)
|
||||
: Theme.accent.with-alpha(0.22);
|
||||
}
|
||||
HorizontalLayout {
|
||||
height: parent.height;
|
||||
padding-left: 6px;
|
||||
padding-right: 6px;
|
||||
spacing: 4px;
|
||||
Text {
|
||||
height: parent.height;
|
||||
text: d.path;
|
||||
color: Theme.text-secondary;
|
||||
font-size: Theme.fs-xs;
|
||||
vertical-alignment: center;
|
||||
horizontal-stretch: 1;
|
||||
overflow: elide;
|
||||
}
|
||||
Text {
|
||||
height: parent.height;
|
||||
text: d.detail;
|
||||
color: Theme.text-muted;
|
||||
font-size: Theme.fs-xs;
|
||||
vertical-alignment: center;
|
||||
horizontal-alignment: right;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "meatshell v0.2.7";
|
||||
color: Theme.text-muted;
|
||||
font-size: Theme.fs-xs;
|
||||
horizontal-alignment: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user